主要和堆栈对齐

Z b*_*son 2 linux assembly gcc x86-64 nasm

我有一个打印文本和浮点数的功能.这是一个不使用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
Run Code Online (Sandbox Code Playgroud)

我像这样组装和链接

nasm -felf64 hello.asm
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -melf_x86_64
Run Code Online (Sandbox Code Playgroud)

这很好.但是,现在我想用这个来做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
Run Code Online (Sandbox Code Playgroud)

我像这样装配和链接

nasm -felf64 hello_main.asm
gcc hello_main.o
Run Code Online (Sandbox Code Playgroud)

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

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

Jes*_*ter 8

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

至于_start,我不认为你可以依赖它而不需要手动对齐.只是碰巧在你的情况下,由于堆栈上已经存在的东西,它才起作用.

  • 实际上`3.4.1`节说`rsp:保证在进程入口处对齐16字节. (3认同)