Ошибка ассемблера: 64-битный Mach-O не поддерживает абсолютные 32-битные адреса - PullRequest
5 голосов
/ 05 июля 2011

Итак, я изучаю сборку носа x86_64 на своем Mac для развлечения.После hello world и некоторой базовой арифметики я попытался скопировать немного более продвинутую программу hello world с этого сайта и модифицировать ее для 64-битной Intel, но я не могу избавиться от этого одного сообщения об ошибке: hello.s:53: error: Mach-O 64-bit format does not support 32-bit absolute addresses.Вот команда, которую я использую для сборки и связывания: nasm -f macho64 hello.s && ld -macosx_version_min 10.6 hello.o.И вот соответствующая строка:

cmp rsi, name+8

rsi - это регистр, который я использую для моего индекса в цикле, а name - это четырехугольное слово, зарезервированное для пользовательского ввода, которое является именем, которое к этому моментууже написано.

Вот часть кода (чтобы увидеть остальные, нажмите на ссылку и перейдите вниз, единственное отличие состоит в том, что я использую 64-битные регистры):

loopAgain:
mov al, [rsi]           ; al is a 1 byte register
cmp al, 0x0a            ; if al holds an ascii newline...
je exitLoop             ; then jump to label exitLoop

; If al does not hold an ascii newline...
mov rax, 0x2000004      ; System call write = 4
mov rdi, 1              ; Write to stdout = 1
mov rdx, 1              ; Size to write
syscall

inc rsi

cmp rsi, name+8         ; LINE THAT CAUSES ERROR
jl loopAgain

Ответы [ 2 ]

4 голосов
/ 05 июля 2011

Инструкция cmp не поддерживает 64-битный непосредственный операнд. Таким образом, вы не можете поместить 64-битную непосредственную ссылку на адрес в один из его операндов - загрузить name+8 в регистр (используя обычный MOV), а затем сравнить с этим регистром.

Вы можете увидеть, какие кодировки инструкций разрешены в руководстве Intel ISA (предупреждение: огромный PDF). Как видно из записи для CMP, существуют кодировки CMP r/m32, imm32 и CMP r/m64, imm32, которые позволяют сравнивать 32-битные непосредственные значения как с 32-битными, так и с 64-битными регистрами, но не с CMP r/m64, imm64. Однако существует кодировка MOV r64, imm64.

Так как nasm падает, сбой MOV rcx, name+8 является просто ошибкой в ​​nasm. Пожалуйста, сообщите об этом разработчикам nasm (убедившись, что вы используете последнюю версию nasm; также убедитесь, что этот патч не устраняет проблему). В любом случае, однако, одним из обходных путей будет добавление символа для конца name:

name:
    resb 8
name_end:

Теперь просто используйте MOV rcx, name_end. Преимущество этого заключается в том, что нет необходимости обновлять ссылки при изменении размера name. В качестве альтернативы вы можете использовать другой ассемблер, например, ассемблеры clang или GNU binutils.

3 голосов
/ 05 июля 2011

Я полагаю, что проблема, с которой вы сталкиваетесь, проста: формат Mach-O требует перемещения кода, что означает, что доступ к данным должен осуществляться не по абсолютному адресу, а по относительному адресу. То есть ассемблер не может преобразовать name в константу, поскольку она не константа, данные могут быть по любому адресу.

Теперь, когда вы знаете, что адрес данных относится к адресу вашего кода, посмотрите, сможете ли вы понять вывод из GCC. Например,

static unsigned global_var;
unsigned inc(void)
{
    return ++global_var;
}

_inc:
    mflr r0                                           ; Save old link register
    bcl 20,31,"L00000000001$pb"                       ; Jump
"L00000000001$pb":
    mflr r10                                          ; Get address of jump
    mtlr r0                                           ; Restore old link register
    addis r2,r10,ha16(_global_var-"L00000000001$pb")  ; Add offset to address
    lwz r3,lo16(_global_var-"L00000000001$pb")(r2)    ; Load global_var
    addi r3,r3,1                                      ; Increment global_var
    stw r3,lo16(_global_var-"L00000000001$pb")(r2)    ; Store global_var
    blr                                               ; Return

Обратите внимание, что это на PowerPC, потому что я не знаю ABI Mach-O для x86-64. На PowerPC вы делаете прыжок, сохраняя счетчик программ, а затем выполняете арифметику с результатом. Я полагаю, что на x86-64 происходит нечто совершенно иное.

(Примечание: если вы посмотрите на вывод сборки GCC, попробуйте посмотреть на него с помощью -O2. Я не буду беспокоиться о том, чтобы посмотреть -O0, потому что он слишком многословен и труднее для понимания.)

Моя рекомендация? Если вы не пишете компилятор (а иногда даже тогда), пишите свои ассемблерные функции одним из двух способов:

  • Передать все необходимые указатели в качестве аргументов функции, или,
  • Запишите сборку как встроенную сборку внутри функции C.

Как правило, это также будет более переносимым, так как вы будете меньше полагаться на определенные детали ABI. Но ABI все еще важен! Если вы не знаете ABI и следите за ним, то вы будете вызывать ошибки, которые довольно сложно обнаружить. Например, несколько лет назад в коде сборки LibSDL была ошибка, из-за которой memcpy (также сборка) из libc при некоторых очень специфических обстоятельствах копировала неправильные данные.

...