Почему эта команда movq работает на linux, а не osx?

Следующий код сборки дает ошибку при запуске as в OSX 10.9.4, но работает успешно в Linux (Debian 7.6). В частности, команда movq не похожа на аргумент label.

$ cat test.S
.globl _main
_main:
    movq $_main, %rax
    ret

Вот ошибка:

$ as -o test.o test.S 
test.S:3:32-bit absolute addressing is not supported for x86-64
test.S:3:cannot do signed 4 byte relocation

Изменение $_main в строке 3 на литерал, такой как $10, отлично работает.

Код должен был быть модифицирован очень незначительным образом, чтобы заставить его работать в Linux - просто удалив подчеркивания из меток.

$ cat test.S
.globl main
main:
    movq $main, %rax
    ret

Довольно легко убедиться, что код действительно работает в Linux:

$ as -o test.o test.S
$ gcc -o test.out test.o
$ ./test.out

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

Я немного поработал с использованием LEA (загружаемый эффективный адрес), но прежде чем я сделаю это изменение, я хотел бы понять разницу - почему он работает на Linux, а не на OSX?

Ответ 1

Вы правильно знаете, что команда movq не может ссылаться на абсолютный адрес. Это отчасти связано с форматом OS X ABI Mach-O, который использует перемещаемую адресацию для символов.

Программа, которая скомпилирована как независимый от позиции исполняемый файл (PIE), вообще не может ссылаться на абсолютный виртуальный адрес таким же образом, как и movq $_main, %rax. Вместо этого вызываются Глобальные таблицы смещения, которые позволяют относить относительный код позиции (PC-rel) и не зависят от положения код (PIC) для извлечения глобальных символов в их текущем абсолютном адресе. Демонстрированный ниже, GOTPCREL(%rip) создает интерпретацию lea rdi, _msg:

Код PC-rel, ссылающийся на него Глобальная таблица смещения:

.globl _main
_main:

    movq     [email protected](%rip), %rax
    sub      $8, %rsp
    mov      $0, %rax
    movq     [email protected](%rip), %rdi
    call     _printf
    add      $8, %rsp
    ret

.cstring
_msg:

    .ascii  "Hello, world\n"

Mac OS X Mach-O Assembler:

$ as -o test.o test.asm

Apple версия GCC:

$ gcc -o test.out test.o

Вывод:

$ ./test.out
Hello, world