Перемещение сборки усечено, чтобы соответствовать
Привет! Я пытался написать простую программу 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.