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

Привет, я пытался написать простую программу hello world на ассемблере и скомпилировать ее в файл .o, а затем связать ее со стандартной библиотекой C, чтобы создать .exe, чтобы я мог просматривать дизассемблирование для «путей» на моем система с использованием 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`

person v3nd3774    schedule 30.12.2015    source источник
comment
Если вы хотите использовать библиотеку C, вы должны использовать точки входа main и gcc для компоновки.   -  person Jester    schedule 30.12.2015
comment
Я попробовал ваше предложение, к сожалению, оно дало мне несколько новых ошибок. Вот что я сделал: заменил _start на main, а ld test.o -o test.exe -lc на gcc test.o -o test.exe. Это привело к следующим ошибкам relocation truncated to fit: R_X86_64_32S against '.text' libcygwin.a(libcmain.o): In function 'main': , `/libcmain.c:39: неопределенная ссылка на WinMain`, `/libcmain.c:39:(.text.startup+0x7f): перемещение усечено, чтобы соответствовать: R_X86_64_PC32 против неопределенного символа 'WinMain' ` , ` collect2: ошибка: ld вернул 1 статус выхода ` .   -  person v3nd3774    schedule 30.12.2015
comment
Тогда, очевидно, cygwin требует WinMain, так что попробуйте.   -  person Jester    schedule 30.12.2015
comment
Я только что попытался изменить main на WinMain без каких-либо изменений в ошибках в OP. Примечание Похоже, что я могу использовать gcc test.asm -o test.o -c для создания объектного файла, я чувствую, что проблема возникает, когда я запускаю его через компоновщик ld. Вы предложили изменить main на WinMain, верно?   -  person v3nd3774    schedule 30.12.2015
comment
Измените _start на WinMain, а также добавьте .global WinMain для экспорта.   -  person Jester    schedule 30.12.2015


Ответы (2)


Код с некоторыми комментариями. Похоже, вы заимствовали соглашение о вызовах 64-разрядной версии Linux, передав первый параметр в RDI. В Windows вы передаете первый параметр в 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 */
person Michael Petch    schedule 30.12.2015
comment
@RossRidge: я изменил комментарий, чтобы он соответствовал тому, что я написал в ответе. Я скопировал и вставил из какого-то другого кода, и это было неоднозначно. И да, я был вашим сторонником. - person Michael Petch; 30.12.2015
comment
Спасибо, Майкл! Я запустил исполняемый файл, у меня была ошибка сегментации, но при дальнейшем осмотре я обнаружил, что проблема была вызвана завершением программы с ret, после замены этой строки на call ExitProcess проблема была решена, и исполняемый файл работает так, как предполагалось. Спасибо, я боролся с этим какое-то время! Есть ли какие-либо ресурсы (книги, статьи), помимо соглашений о вызовах Windows, которые вы рекомендуете прочитать? - person v3nd3774; 30.12.2015
comment
Я запустил ваш код, следуя инструкциям в вашем сообщении, и получил ту же ошибку. Я попытался поменять местами строку mov %rbp, %rsp со строкой mov %rsp, %rbp, и это устранило ошибку ошибки сегментации, которую я получал. Спасибо, Майкл! - person v3nd3774; 30.12.2015
comment
@MichaelPetch: Он прав, в вашей версии с фреймом стека аргументы перевернуты. Пролог должен с mov на %rbp, а эпилог должен с mov на %rsp (и восстановить старый %rbp, что он и делает). - person Peter Cordes; 30.12.2015
comment
@PeterCordes А, вы знаете, я даже этого не видел. Я вырвал некоторый код из синтаксиса сборки Intel, вставил его в свой ответ и добавил %s. Я даже не заметил, что это то, о чем идет речь (я забыл поменять местами операнды) - person Michael Petch; 30.12.2015
comment
@ v3nd3774: посмотрите мой обновленный код. На самом деле я поменял местами операнды, когда скопировал некоторый код NASM в свой ответ, добавил %s к именам регистров и не менял местами операнды. Я даже не заметил, что вы предполагаете, что я, возможно, имел их задом наперед. Прости за это. - person Michael Petch; 30.12.2015
comment
Вам не нужен movabs, и это плохой выбор по сравнению с lea msg(%rip), %rcx, поэтому это не будет моей первой рекомендацией. Как загрузить адрес функции или метку в регистр - person Peter Cordes; 26.06.2021

Есть ряд проблем с тем, что вы написали, и попытками исправить это. Во-первых, как говорит Jester, если вы собираетесь использовать функцию библиотеки 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.

person Ross Ridge    schedule 30.12.2015
comment
Спасибо, что так доступно объяснили, теперь я понимаю, что происходит! - person v3nd3774; 30.12.2015