2

嗨,我一直在尝试用汇编语言编写一个简单的 hello world 程序并将其编译为 .o 文件,然后将其与标准 C 库链接以创建一个 .exe,以便我可以在我的计算机上查看“puts”的反汇编系统使用gdb -tui. 我正在使用带有以下实用程序版本的 Cygwin(通过 获得这些as --version && ld --version)。我正在尝试在 Windows 8 x64 上完成所有这些工作。

作为 2.25 版

ld 版本 2.25

测试.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`
4

2 回答 2

4

带有一些注释的代码。您似乎通过在RDI中传递第一个参数来借鉴 Linux 64 位调用约定。在 Windows 上,您传递RCX中的第一个参数。请参阅 Microsoft Windows 的64 位调用约定。您需要使用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 */
于 2015-12-30T01:40:07.647 回答
4

您所写的内容以及您为修复它所做的尝试存在许多问题。第一个是,就像 Jester 所说,如果你要使用 C 库函数,你的入口点应该命名为main. 这使 C 运行时库有机会在调用main. 当您将入口点更改为main您并没有将其声明为全局时。这意味着链接器找不到它,这就是为什么你得到关于 not found 的错误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 相对寻址。

于 2015-12-30T01:40:17.157 回答