Перемещение сборки усечено, чтобы соответствовать

Привет! Я пытался написать простую программу hello world в ассемблере и скомпилировать ее в файл.o, а затем связать ее со стандартной библиотекой C, чтобы создать.exe, чтобы я мог просматривать разборку для "put" на моем система, использующая gdb -tui, Я использую Cygwin со следующими версиями утилиты (получил их с as --version && ld --version). Я пытаюсь сделать все это на Windows 8 x64.

как версия 2.25

лд версия 2.25

test.asm

Я видел несколько стандартов сборки в интернете во время изучения сборки x86. Я думаю, что я пишу здесь, ГАЗ.

.extern puts
_start:
    mov $msg, %rdi
    call puts
    xor %rax, %rax
    ret
msg: 
    .ascii "hello world"

ассемблер

Я могу собрать вышеуказанный файл без проблем, as Утилита не дает мне предупреждение или ошибки, вот как я называю as полезность.

as test.asm -o test.o

линкер

Вот где у меня возникли проблемы, следующая команда, как я думаю, я должен связать объектный файл со стандартной библиотекой c.

ld test.o -o test.exe -lc

Эта команда выдает следующие ошибки, которые я поставил в тупик. Я пытался найти ответ в других сообщениях и через Google, но, возможно, я что-то упустил.

test.o:fake:(.text+0x3): relocation truncated to fit: R_X86_64_32S against `.text`
/usr/lib/libc.a(t-d000957.o):fake:(.text+0x2): undefined reference to `__imp_puts`
/usr/lib/libc.a(t-d000957.o):fake:(.text+0x2): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `__imp_puts`

2 ответа

Решение

Код с некоторыми комментариями. Похоже, вы заимствовали из соглашения о 64-битных вызовах в Linux, передав первый параметр в RDI. На окнах вы передаете первый параметр в RCX. См. 64-разрядное соглашение о вызовах для Microsoft Windows. Вам нужно использовать movabsq переместить 64-битный адрес метки в 64-битный регистр. Вы также должны убедиться, что вы правильно выровняете стек (16-байтовая граница); выделить как минимум 32 байта для теневого пространства; и я добавил кадр стека.

.extern puts
.global main
.text
main:
    push %rbp
    mov %rsp, %rbp         /* Setup stack frame */
    subq $0x20, %rsp       /* Allocate space for 32 bytes shadow space 
                              no additional bytes to align stack are needed
                              since return address and rbp on stack maintain
                              16 byte alignment */ 
    movabsq $msg, %rcx     /* Loads a 64 bit register with a label's address
                              Windows 64-bit calling convention
                              passes param 1 in rcx */
    call puts
    xor %rax, %rax         /* Return value */
    mov %rbp, %rsp
    pop %rbp               /* Remove current stackframe */
    ret

.data
msg:
    .asciz "hello world"   /* Remember to zero terminate the string */

Переименуйте ваш файл ассемблера с помощью .s расширение вместо .asm и собрать и связать с:

gcc -o test.exe test.s

Без переименования .asm в .s вы можете обнаружить, что GCC на Cygwin будет путать ваш файл ассемблера со скриптом компоновщика.


Версия без стековой рамки

Этот код похож на код выше, но кадр стека удален. Пролог и код эпилога RBP / RSP были удалены в этой версии. Нам все еще нужно выровнять стек. Поскольку мы больше не помещаем RBP в стек, нам нужно выделить 32 байта теневого пространства в стеке и дополнительные 8 байтов, чтобы вернуть стек в 16-байтовое выравнивание. Это выравнивание и выделение теневого пространства необходимо выполнить перед вызовом других функций (например, Win32 API и библиотеки C) из наших функций. Неправильная настройка стека может привести к тому, что вызовы других функций будут таинственно повреждены или будут вести себя неожиданно. В соглашении о вызовах в 64-битной среде Windows это описано по ссылке, указанной ранее в начале этого ответа.

Модифицированный код:

.extern puts
.global main
.text
main:
    subq $0x28, %rsp       /* Allocate space for 32 bytes shadow space
                              Additional 8 bytes to align stack are needed
                              since 8 byte return address on stack no longer
                              makes the stack 16 byte aligned. 32+8=0x28 */
    movabsq $msg, %rcx     /* Loads a 64 bit register with a label's address
                              Windows 64-bit calling convention
                              passes param 1 in rcx */
    call puts
    xor %rax, %rax         /* Return value */
    addq $0x28, %rsp       /* Restore stack pointer to state it was in prior
                              to executing this function so that `ret` won't fail */
    ret

.data
msg:
    .asciz "hello world"   /* Remember to zero terminate the string */

Существует ряд проблем с тем, что вы написали, и попытками исправить это. Во-первых, как говорит Джестер, если вы собираетесь использовать функцию библиотеки C, ваша точка входа должна быть названа main, Это дает библиотеке времени выполнения C возможность инициализировать себя перед вызовом main, Когда вы изменили точку входа на main Вы также не объявили это глобальным. Это означало, что компоновщик не смог его найти, и именно поэтому вы получили ошибку о том, что не нашли WinMain, Из-за того, как написана библиотека времени выполнения Cygwin, в конечном итоге он ищет несколько разных символов в качестве точки входа, WinMain является одним из них, и что он в итоге жалуется на то, что не нашел. Однако, если вы не пишете приложение Win32, вы должны использовать main,

Наконец, relocation truncated to fit: R_X86_64_32S against '.text' сообщение приходит от mov $msg, %rdi инструкция. Ассемблер GNU интерпретирует эту инструкцию как 32-битный непосредственный операнд слева, однако msg является 64-битным адресом, поэтому он "усекается". Решение либо использовать movabs $msg,%rdi, который использует 64-битную немедленную, или еще лучше, lea msg(%rip),%rdi который использует относительную адресацию RIP.

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