【问题标题】:main and stack alignment主要和堆栈对齐
【发布时间】:2015-01-08 02:38:15
【问题描述】:

我有一个打印文本和浮点数的函数。这是一个不使用main的版本

extern printf
extern _exit

section .data
    hello:     db 'Hello world! %f',10,0
    pi:        dq  3.14159
section .text
    global _start
_start:
    xor eax, eax
    lea rdi, [rel hello]
    movsd xmm0, [rel pi]
    mov eax, 1
    call printf
    mov rax, 0
    jmp _exit

我像这样组装和链接这个

nasm -felf64 hello.asm
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -melf_x86_64

这运行良好。但是,现在我想使用main 来执行此操作。

global main
extern printf

section .data
    hello:     db 'Hello world! %f',10,0
    pi:        dq  3.14159
section .text
    main:
    sub rsp, 8
    xor eax, eax
    lea rdi, [rel hello]
    movsd xmm0, [rel pi]
    mov eax, 1
    call printf
    mov rax, 0
    add rsp, 8
    ret

我像这样组装和链接

nasm -felf64 hello_main.asm
gcc hello_main.o

这也运行良好。但是,我必须在调用printf 之前从堆栈指针中减去八个字节,然后在调用printf 之后将八个字节添加到堆栈指针中,否则会出现分段错误。

查看堆栈指针,我发现不使用 main 时它是 16 字节对齐的,但使用 main 它只有八字节对齐。必须减去和添加八个字节的事实表明它始终是 8 字节对齐的,而不是 16 字节对齐的(除非我误解了某些东西)。为什么是这样?我认为使用 x86_64 代码我们可以假设堆栈是 16 字节对齐的(至少对于我认为包括 main 的标准库函数调用)。

【问题讨论】:

    标签: linux gcc assembly nasm x86-64


    【解决方案1】:

    根据 ABI,堆栈指针 + 8 在进入函数时应保持 16 字节对齐。必须减去 8 的原因是 call 本身在堆栈上放置了 8 个字节的返回地址,从而违反了这个约束。基本上,您必须确保堆栈指针的总移动量是 16 的倍数,包括返回地址。因此堆栈指针需要移动 16 + 8 的倍数,以便为返回地址留出空间。

    至于_start,我认为您也不能依靠它在没有手动对齐的情况下工作。碰巧的是,在您的情况下,由于堆栈中已经存在的东西,它可以工作。

    【讨论】:

    • 我查看了abi。它说“当控制权转移到函数入口点时,值(%rsp+ 8)始终是 16(32)的倍数。这就解释了为什么 main 是 16+8 对齐的。但是为什么 _start 不是 16+ 8 对齐?
    • 其实3.4.1部分说rsp: it is guaranteed to be 16-byte aligned at process entry。
    • 哦,好的观察,你的意思是_start。好的,我现在需要做一些阅读和测试。这是从 amd64 汇编开始的缺点,因为您可以在不使用堆栈的情况下做很多事情。如果我从 32 位模式开始,我已经熟悉堆栈了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 2019-02-19
    • 1970-01-01
    • 2018-07-18
    相关资源
    最近更新 更多