x64架构NASM汇编调用printf触发段错误问题排查
x64汇编调用printf出现段错误的原因与解决
问题描述
在Ubuntu x64环境下,使用NASM编写汇编代码调用C标准库的printf函数时,偶尔出现段错误,怀疑与栈对齐有关,原代码及编译命令如下:
原代码
extern printf section .data int_string: db "hey, your result is %d", 10, 0 global main main: push rbp mov rbp, rsp mov rax, 78 push rbp mov rdi, int_string mov rsi, rax ; 78 in rsi call printf pop rbp mov rax, 1 leave ret
编译命令
nasm -f elf64 test.asm gcc -no-pie -fno-pie test.o -o test ./test
问题根源
确实是栈对齐问题加上违反调用约定导致的段错误,属于未定义行为(因此偶尔能运行),具体原因:
- 栈对齐要求:Linux遵循x86-64 System V ABI,规定调用函数前栈指针必须是16字节对齐。
call指令会将8字节的返回地址压栈,所以进入main时rsp处于8字节偏移状态(非16字节对齐)。原代码中多余的push rbp再次将rsp减8,导致调用printf时栈完全偏离对齐要求,触发栈访问错误。 - 可变参数函数调用约定:
printf是可变参数函数,ABI要求rax寄存器保存通过XMM寄存器传递的浮点参数数量,无浮点参数时必须将rax设为0,原代码未正确设置,可能导致函数内部栈解析错误。 - main返回值错误:
main函数返回1表示程序异常退出,虽不是段错误直接原因,但不符合C程序规范。
修正后的代码
带栈帧的标准写法
extern printf section .data int_string: db "hey, your result is %d", 10, 0 global main main: push rbp mov rbp, rsp ; 调整栈到16字节对齐(进入main时rsp是8字节对齐,push rbp后偏移8,再减8回到16字节对齐) sub rsp, 8 mov rax, 78 mov rdi, int_string mov rsi, rax mov rax, 0 ; 无浮点参数,必须置0 call printf add rsp, 8 ; 恢复栈 mov rax, 0 ; main正常返回0 leave ret
简洁写法(不建立栈帧)
extern printf section .data int_string: db "hey, your result is %d", 10, 0 global main main: ; 直接保证调用printf前rsp16字节对齐 mov rdi, int_string mov rsi, 78 mov rax, 0 call printf mov rax, 0 ret
关键规范说明
x86-64 System V ABI核心规则:
- 调用函数前,rsp必须是16字节对齐。
- 可变参数函数调用时,rax存储浮点参数的数量(无则为0)。
- 整数参数依次存放在rdi、rsi、rdx、rcx、r8、r9寄存器中,剩余参数压栈。
内容的提问来源于stack exchange,提问作者user13123535
相关产品推荐
相关产品推荐

