我有一个打印文本和浮点数的功能。这是一个不使用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
之前从堆栈指针中减去8个字节,然后在堆栈指针之后添加8个字节,否则会出现分段错误。
查看堆栈指针,我发现没有使用main
它的16字节对齐但是main
它只有8字节对齐。必须减去并添加八个字节的事实表明它总是8字节对齐而且从不16字节对齐(除非我误解了某些东西)。为什么是这样?我认为使用x86_64代码我们可以假设堆栈是16字节对齐的(至少对于标准的库函数调用,我认为它包括main
)。
答案 0 :(得分:7)
根据ABI,在进入函数时,堆栈指针+ 8应保持16字节对齐。你必须减去8的原因是call
本身在堆栈上放置了8个字节的返回地址,从而违反了这个约束。基本上你必须确保总堆栈指针移动是16的倍数,包括返回地址。因此,堆栈指针需要移动16 + 8的倍数才能为返回地址留出空间。
对于_start
,我不认为你可以依赖它而不需要手动对齐。事情就是这样,在你的情况下,由于堆栈上已有的东西,它会起作用。