您好我一直在尝试在汇编中编写一个简单的hello world程序并将其编译成.o文件,然后将其与标准C库链接以创建.exe以便我可以查看反汇编'放'在我的系统上使用gdb -tui
。我正在使用Cygwin以下实用程序版本(使用as --version && ld --version
得到这些)。我试图在Windows 8 x64上完成所有这些。
版本2.25
ld version 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
此命令会产生以下错误,这些错误让我感到难过。我试图通过其他帖子和谷歌找到答案,但也许我错过了一些东西。
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 :(得分:3)
带有一些评论的代码。您似乎通过在 RDI 中传递第一个参数来借用Linux 64位调用约定。在Windows上,您传递 RCX 中的第一个参数。有关Microsoft Windows的信息,请参阅64-bit calling convention。您需要使用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
,您可能会发现Cygwin上的 GCC 会使汇编程序文件与链接描述文件混淆。
此代码与上面的代码类似,但删除了堆栈帧。此版本中已删除 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 */
答案 1 :(得分:3)
您撰写的内容以及您为解决此问题所做的尝试存在许多问题。第一个是,就像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位地址,因此它结束为#34;截断以适应"。解决方案是使用movabs $msg,%rdi
,使用64位立即数,或者更好,lea msg(%rip),%rdi
使用RIP相对寻址。