64-битный формат Mach-O не поддерживает абсолютные 32-битные адреса. NASM Доступ к массиву

Запустите этот код с моего компьютера Mac, используя команду:

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

Это код, который я пытаюсь запустить на моем компьютере, который находит наибольшее число в массиве.

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

Ошибка:

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

1 ответ

Прежде всего, остерегайтесь ошибок NASM с выходным форматом macho64 с 64-битной абсолютной адресацией (NASM 2.13.02+) и RIP-относительной в NASM 2.11.08. 64-разрядная абсолютная адресация не рекомендуется, поэтому этот ответ должен работать даже для глючного NASM 2.13.02 и выше. (Ошибки не вызывают эту ошибку, они приводят к неправильным адресам, используемым во время выполнения.)


[data_items + edi*4] 32-битный режим адресации Четное [data_items + rdi*4] можно использовать только 32-битное абсолютное смещение, поэтому оно тоже не будет работать. Обратите внимание, что использование адреса в качестве 32-битного (расширенного знака) немедленного типа cmp rdi, data_items тоже проблема: только mov позволяет 64-битный немедленный.

64-разрядный код в OS X вообще не может использовать 32-разрядную абсолютную адресацию. Исполняемые файлы загружаются с базовым адресом выше 4 ГБ, поэтому адреса меток просто не помещаются в 32-разрядные целые числа с расширением нуля или знака. RIP-относительная адресация является лучшим / наиболее эффективным решением, независимо от того, нужна ли вам независимость от позиции или нет1.

В НАСМdefault relв верхней части вашего файла сделает все [] Операнды памяти предпочитают RIP-относительную адресацию. См. Также раздел 3.3. Эффективные адреса в руководстве 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

Но RIP-относительный возможен только тогда, когда другие регистры не задействованы, поэтому для индексации статического массива сначала нужно получить адрес в регистре. Используйте RIP-родственник lea rsi, [rel data_items],

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

Или ты мог add rsi, 4 внутри цикла и использовать более простой режим адресации, как mov eax, [rsi],

Обратите внимание, что mov rsi, data_items будет работать для получения адреса в реестре, но вы не хотите этого, потому что это менее эффективно.

Технически, любой адрес в пределах + -2 ГБ вашего массива будет работать, поэтому, если у вас есть несколько массивов, вы можете адресовать другие относительно одного общего базового адреса, связывая только один регистр с указателем. например lea rbx, [arr1] / ... / mov eax, [rbx + rdi*4 + arr2-arr1], Относительные ошибки адресации - Mac 10.10 упоминает, что в руководстве "Оптимизация сборки" Agner Fog есть несколько примеров адресации массива, в том числе с использованием __mh_execute_header в качестве ориентира. (Код в этом вопросе выглядит как очередная попытка перенести этот 32-битный пример Linux с книги PGU на 64-битную OS X, в то же время, в первую очередь, при изучении asm.)


Обратите внимание, что в Linux зависимые от позиции исполняемые файлы загружаются в нижние 32 бита виртуального адресного пространства, поэтому вы увидите такой код mov eax, [array + rdi*4] или же mov edi, symbol_name в примерах Linux или выводе компилятора на http://gcc.godbolt.org/. gcc -pie -fPIE создаст независимые от позиции исполняемые файлы в Linux, и используется по умолчанию во многих последних дистрибутивах, но не в Godbolt.

Это не поможет вам в MacOS, но я упомяну об этом на тот случай, если кто-нибудь запутается в коде, который они видели для других ОС, или почему архитекторы AMD64 стараются позволить [disp32] Режимы адресации у всех на x86-64.


И кстати, предпочитаю использовать 64-битные режимы адресации в 64-битном коде. например, использовать [rsi + rdi*4]не [esi + edi*4], Обычно вы не хотите усекать указатели до 32-разрядных, и для кодирования требуется дополнительный префикс размера адреса.

Точно так же вы должны использовать syscall делать 64-битные системные вызовы, а не int 0x80, Каковы соглашения о вызовах для системных вызовов UNIX и Linux на i386 и x86-64 для различий, в которых регистры передают аргументы.


Сноска 1: 64-разрядная абсолютная адресация поддерживается в OS X, но только в зависимых от позиции исполняемых файлах (не PIE). Этот связанный вопрос x64 nasm: вставка адресов памяти в стек и функция вызова включает в себя ld предупреждение от использования gcc main.o связывать:

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

Таким образом, компоновщик проверяет, используются ли какие-либо абсолютные 64-разрядные перемещения, и если это так, отключает создание независимого от позиции исполняемого файла. Пирог может воспользоваться ASLR для безопасности. Я думаю, что код разделяемой библиотеки всегда должен быть независимым от позиции в OS X; Я не знаю, разрешены ли таблицы переходов или другие случаи указателей как данных (т.е. исправлены динамическим компоновщиком), или их нужно инициализировать во время выполнения, если вы не делаете зависимый от позиции исполняемый файл.

mov r64, imm64 больше (10 байт) и не быстрее чем lea r64, [RIP_rel32] (7 байт).

Так что вы могли бы использовать mov rsi, qword data_items вместо REA-относительного LEA, который работает примерно так же быстро и занимает меньше места в кэшах кода и кэше UOP. 64-битные немедленные пользователи также имеют штраф за выборку uop-кэша для семейства Sandybridge ( http://agner.org/optimize/): для чтения из строки кэша uop требуется 2 цикла вместо 1.

x86 также имеет форму mov который загружает / сохраняет с / на 64-битный абсолютный адрес, но только для AL/AX/EAX/RAX. См. http://felixcloutier.com/x86/MOV.html. Вы тоже этого не хотите, потому что он больше и не быстрее mov eax, [rel foo],


(Связанный: синтаксическая версия AT&T того же вопроса)

Другие вопросы по тегам