程序集重定位被截断以适合

时间:2015-12-30 00:03:19

标签: assembly linker linker-errors 32bit-64bit relocation

您好我一直在尝试在汇编中编写一个简单的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`

2 个答案:

答案 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相对寻址。