x64汇编调用printf触发段错误,请求排查原因
问题分析与修正
你的代码出现段错误的核心原因是误用了32位x86的cdecl调用约定,而x86-64 Linux平台遵循的是System V AMD64调用约定,两者在参数传递、栈要求上差异很大。具体问题和修正方案如下:
错误点拆解
- 参数传递方式错误:System V AMD64约定中,前6个整数/指针参数通过寄存器传递,而非压栈。
printf的第一个参数(格式字符串)需放入rdi,第二个参数(%d对应的整数)需放入rsi。 - 栈对齐违规:调用函数前,栈指针
rsp必须是16字节的整数倍。进入main时,由于call指令已压入8字节的返回地址,rsp处于16n-8的状态,未满足对齐要求,直接调用printf会触发异常。 - 非规范寄存器使用:
rbx是System V AMD64中的非易失性寄存器,调用外部函数前需保存,调用后恢复(虽然这不是段错误的直接原因,但属于编写规范)。
修正后的代码
版本1(规范保存非易失性寄存器)
section .data fmt: db "%d", 10, 0 section .text extern printf global main main: push rbx ; 保存非易失性寄存器rbx mov rbx, 25 mov rdi, fmt ; 第一个参数:格式字符串传入rdi mov rsi, rbx ; 第二个参数:要打印的数值传入rsi call printf ; 此时rsp因push rbx后变为16字节对齐,符合要求 pop rbx ; 恢复rbx寄存器 mov rax, 0 ; 退出码0 ret
版本2(简化版,不使用非易失性寄存器)
section .data fmt: db "%d", 10, 0 section .text extern printf global main main: sub rsp, 8 ; 调整栈指针至16字节对齐(进入main时rsp=16n-8,减8后为16n) mov rsi, 25 ; 第二个参数传入rsi mov rdi, fmt ; 第一个参数传入rdi call printf add rsp, 8 ; 恢复栈指针 mov rax, 0 ret
编译验证
仍使用你原有的编译链接命令即可正常运行:
nasm -f elf64 main.s && gcc -o main main.o
内容的提问来源于stack exchange,提问作者TheSinisterStone
相关产品推荐
相关产品推荐

