2015-12-30 2 views
1

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

как версия 2,25

л.д. версии 2,25

test.asm

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

.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` 
+0

Если вы хотите использовать библиотеку C, вы должны использовать точки входа 'main' и' gcc' для связи. – Jester

+0

Я попробовал ваше предложение, к сожалению, он дал мне несколько новых ошибок. Вот что я сделал, я заменил '_start' на' main' и заменил 'ld test.o -o test.exe -lc' на' gcc test.o -o test.exe'. Это привело к следующим ошибкам: «Перемещение усечено для соответствия: R_X86_64_32S против« .text »libcygwin.a (libcmain.o): В функции« main »:', '/libcmain.c:39: неопределенная ссылка на WinMain','/libcmain.c: 39 :(text.startup + 0x7f): перемещение усечено для соответствия: R_X86_64_PC32 против неопределенного символа «WinMain», 'collect2: error: ld возвращено 1 статус выхода'. – v3nd3774

+0

Тогда, очевидно, cygwin требует 'WinMain', поэтому попробуйте это. – Jester

ответ

3

Код с комментариями. Кажется, вы заимствовали из 64-битного соглашения об использовании Linux, передав первый параметр в RDI. В окнах вы передаете первый параметр в RCX. См. 64-bit calling convention для 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 смутит ваш файл на ассемблере с сценарий компоновщика.


Версия без Stack Рама

Этот код похож на приведенный выше код, но кадр стека удаляется.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 */ 
+0

@RossRidge: Я изменил комментарий, чтобы соответствовать тому, что я написал в ответ. У меня была копия и вставка из какого-то другого кода, и это было неоднозначно. И да, я был вашим сторонником. –

+0

Спасибо, Майкл! Я получил исполняемый файл, у меня была ошибка с ошибкой сегментации, но при дальнейшей проверке я обнаружил, что завершение работы программы с помощью 'ret' вызывало проблему, после замены этой строки на' call ExitProcess' проблема была решена, и исполняемый файл работает как это должно быть. Спасибо, что я боролся с этим форумом! Существуют ли какие-либо ресурсы (книги, статьи) помимо соглашений о вызовах Windows, которые вы рекомендуете читать? – v3nd3774

+1

Я запустил ваш код после выполнения инструкций в вашем сообщении, и я получил ту же ошибку. Я попытался заменить строку 'mov% rbp,% rsp' на строку' mov% rsp,% rbp' и удалил ошибку ошибки сегментации, которую я получал. Спасибо, Майкл! – v3nd3774

3

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

+1

Спасибо, что объяснил это так ясно, я понимаю, что происходит более четко! – v3nd3774

Смежные вопросы