O formato Mach-O 64-bit não suporta endereços absolutos de 32 bits. Matriz de Acesso NASM

Executando este código fora do meu computador Mac, usando o comando:

nasm -f macho64 -o max.a maximum.asm 

Este é o código que estou tentando executar no meu computador que encontra o maior número dentro de uma matriz.

 section .data data_items: dd 3,67,34,222,45,75,54,34,44,33,22,11,66,0 section .text global _start _start: mov edi, 0 mov eax, [data_items + edi*4] mov ebx, eax start_loop: cmp eax, 0 je loop_exit inc edi mov eax, [data_items + edi*4] cmp eax, ebx jle start_loop mov ebx, eax jmp start_loop loop_exit: mov eax, 1 int 0x80 

Erro:

 maximum.asm:14: error: Mach-O 64-bit format does not support 32-bit absolute addresses maximum.asm:21: error: Mach-O 64-bit format does not support 32-bit absolute addresses 

Primeiro de tudo, cuidado com os erros NASM com o formato de saída macho64 com endereçamento absoluto de 64 bits (NASM 2.13.02+) e com o parente RIP no NASM 2.11.08 . Endereçamento absoluto de 64 bits não é recomendado, portanto, esta resposta deve funcionar mesmo para o NASM com bugs 2.13.02 e superior. (Os bugs não causam esse erro, eles levam a endereços errados sendo usados ​​em tempo de execução.)


[data_items + edi*4] é um modo de endereçamento de 32 bits. Mesmo [data_items + rdi*4] só pode usar um deslocamento absoluto de 32 bits, então não funcionaria também. Note que usar um endereço como imediato de 32 bits (estendido por sinal) como cmp rdi, data_items também é um problema : somente mov permite um imediato de 64 bits.

O código de 64 bits no OS X não pode usar o endereçamento absoluto de 32 bits . Os executáveis ​​são carregados em um endereço base acima de 4GiB, portanto, os endereços de label simplesmente não se ajustam a números inteiros de 32 bits, com extensão de zero ou de sinal. O endereçamento relativo a RIP é a melhor / mais eficiente solução, independentemente de você precisar ou não ser independente de posição 1 .

No NASM, o default rel no topo do seu arquivo fará com que todos os operandos de memory [] prefiram o endereçamento relativo ao RIP. Veja também a Seção 3.3 Endereços Efetivos no manual do NASM.

 default rel ; near the top of file; affects later instructions my_func: ... mov ecx, [data_items] ; uses the default: RIP-relative ;mov ecx, [abs data_items] ; override to absolute [disp32], unusuable mov ecx, [rel data_items] ; explicitly RIP-relative 

Mas o RIP-relative só é possível quando não há outros registradores envolvidos , portanto, para indexar um array estático, é necessário primeiro obter o endereço em um registrador . Use um lea rsi, [rel data_items] relativo RIP lea rsi, [rel data_items] .

  lea rsi, [data_items] ; can be outside the loop ... mov eax, [rsi + rdi*4] 

Ou você pode add rsi, 4 dentro do loop e usar um modo de endereçamento mais simples como mov eax, [rsi] .

Note que mov rsi, data_items irá trabalhar para obter um endereço em um registrador, mas você não quer isso porque é menos eficiente.

Tecnicamente, qualquer endereço dentro de + -2GiB do seu array funcionará, então se você tiver múltiplos arrays, você pode endereçar os outros em relação a um endereço base comum, apenas amarrando um registrador com um ponteiro. por exemplo, lea rbx, [arr1] / … / mov eax, [rbx + rdi*4 + arr2-arr1] . Erros de endereçamento relativo – O Mac 10.10 menciona que o guia de “assembly otimizada” da Agner Fog tem alguns exemplos de endereçamento de matriz, incluindo um usando o __mh_execute_header como ponto de referência. (O código nessa pergunta parece com outra tentativa de portar este exemplo de Linux de 32 bits do livro de PGU para o OS X de 64 bits, ao mesmo tempo em que aprende asm em primeiro lugar.)


Note que no Linux, executáveis ​​dependentes de posição são carregados nos 32 bits baixos do espaço de endereço virtual, então você verá código como mov eax, [array + rdi*4] ou mov edi, symbol_name em exemplos do Linux ou saída do compilador em http : //gcc.godbolt.org/ . gcc -pie -fPIE fará executáveis ​​independentes de posição no Linux e é o padrão em muitas distribuições recentes , mas não no Godbolt.

Isso não ajuda você no MacOS, mas eu o menciono caso alguém esteja confuso sobre o código que eles viram para outros sistemas operacionais, ou porque os arquitetos do AMD64 se deram ao trabalho de permitir [disp32] modos de endereçamento em x86-64.


E BTW, prefira usar modos de endereçamento de 64 bits em código de 64 bits . por exemplo, use [rsi + rdi*4] , não [esi + edi*4] . Você geralmente não deseja truncar pointers para 32 bits e custa um prefixo de tamanho de endereço extra para codificar.

Da mesma forma, você deve estar usando syscall para fazer chamadas de sistema de 64 bits, não int 0x80 . Quais são as convenções de chamada para as chamadas do sistema UNIX e Linux em i386 e x86-64 para as diferenças nas quais registradores passam args em.


Nota de rodapé 1: o endereçamento absoluto de 64 bits é suportado no OS X, mas apenas em executáveis ​​dependentes da posição (não-PIE). Esta questão relacionada x64 nasm: empurrando endereços de memory para a function stack & call inclui um aviso ld de usar gcc main.o para link:

 ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from main.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie 

Portanto, o vinculador verifica se qualquer realocação absoluta de 64 bits é usada e, se assim for, desabilita a criação de um Executável Independente de Posição. Uma TORTA pode beneficiar de ASLR para segurança. Eu acho que o código da biblioteca compartilhada sempre tem que ser independente da posição no OS X; Eu não sei se tabelas de salto ou outros casos de pointers como dados são permitidos (ou seja, corrigidos pelo vinculador dynamic), ou se eles precisam ser inicializados em tempo de execução se você não estiver fazendo um executável dependente de posição.

mov r64, imm64 é maior (10 bytes) e não mais rápido que lea r64, [RIP_rel32] (7 bytes).

Portanto, você poderia usar mov rsi, qword data_items vez de um LEA relativo a RIP que é executado tão rápido e ocupa menos espaço em caches de código e no cache uop. Os imediatos de 64 bits também têm uma penalidade de busca de cache uop na família Sandybridge ( http://agner.org/optimize/ ): eles levam 2 ciclos para ler de uma linha de cache uop em vez de 1.

O x86 também tem uma forma de mov que carrega / armazena de / para um endereço absoluto de 64 bits, mas apenas para AL / AX / EAX / RAX. Veja http://felixcloutier.com/x86/MOV.html . Você não quer isso também, porque é maior e não mais rápido que mov eax, [rel foo] .


(Relacionado: uma versão de syntax AT & T da mesma pergunta )