Linux 64位环境下NASM编写printf程序出现段错误问题排查
问题分析与解决方案
你的这段NASM汇编代码在64位Linux下运行出现段错误,核心原因是违反了x86-64 System V AMD64调用约定,还有几个关键细节错误,我来逐一拆解:
1. 参数传递方式错误(最核心问题)
在64位Linux的System V ABI规范中,C标准库函数(比如printf)的前6个参数是通过寄存器传递的,而非32位环境里的栈传参:
- 第一个参数(格式字符串地址)要存入
rdi寄存器 - 第二个参数(
%d对应的整数)要存入rsi寄存器
你直接把参数push到栈上,printf会去寄存器里找参数,自然读不到正确值,甚至会访问非法内存触发段错误。
2. 栈未满足16字节对齐要求
x86-64架构要求调用函数前,栈指针rsp必须是16字节对齐的。当main被gcc启动代码调用时,call指令已经压入了8字节的返回地址,此时rsp的偏移是8字节(即rsp % 16 == 8),调用printf前必须调整栈到16字节对齐,否则会触发未定义行为。
3. 格式字符串缺少null终止符
C语言的字符串必须以0字节结尾,你的mymsg只定义了'Hello World %d!', 0xa,没有追加0,printf会一直读取内存直到遇到0,这会导致越界访问,也是段错误的潜在诱因。
4. 64位系统调用方式错误
你用的int 80h是32位Linux的系统调用入口,在64位程序中应该使用syscall指令:
sys_exit的系统调用号是60(不是32位的1)- 退出码要存入
rdi寄存器(不是32位的rbx)
修正后的代码
section .text extern printf global main main: ; 调整栈到16字节对齐:进入main时rsp是%16=8,sub 8后变为%16=0 sub rsp, 8 ; 准备printf的参数:符合x86-64调用约定 mov rsi, 10 ; 第二个参数:%d对应的值 lea rdi, [mymsg] ; 第一个参数:格式字符串地址 call printf ; 恢复栈 add rsp, 8 ; 64位系统调用:sys_exit(0) mov rax, 60 mov rdi, 0 syscall section .data mymsg db 'Hello World %d!', 0xa, 0 ; 加上0作为字符串终止符
编译运行验证
用你原来的编译命令即可正常运行:
nasm -f elf64 printf.asm gcc printf.o -o printf ./printf
运行后会输出:Hello World 10!
内容的提问来源于stack exchange,提问作者user8800991
相关产品推荐
相关产品推荐

