装配体重新定位被截断以适应

v3n*_*774 2 x86 assembly linker relocation 32bit-64bit

您好,我一直在尝试用汇编语言编写一个简单的 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"
Run Code Online (Sandbox Code Playgroud)

汇编器

我可以毫无问题地组装上述文件,该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`
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 5

带有一些注释的代码。您似乎借用了 Linux 64 位调用约定,在RDI中传递第一个参数。在 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 */
Run Code Online (Sandbox Code Playgroud)

使用扩展名重命名汇编程序文件,.s而不是.asm使用以下内容进行汇编和链接:

gcc -o test.exe test.s
Run Code Online (Sandbox Code Playgroud)

如果不重命名,.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 */
Run Code Online (Sandbox Code Playgroud)