You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源

确实是栈对齐问题加上违反调用约定导致的段错误,属于未定义行为(因此偶尔能运行),具体原因:

  1. 栈对齐要求:Linux遵循x86-64 System V ABI,规定调用函数前栈指针必须是16字节对齐。call指令会将8字节的返回地址压栈,所以进入main时rsp处于8字节偏移状态(非16字节对齐)。原代码中多余的push rbp再次将rsp减8,导致调用printf时栈完全偏离对齐要求,触发栈访问错误。
  2. 可变参数函数调用约定:printf是可变参数函数,ABI要求rax寄存器保存通过XMM寄存器传递的浮点参数数量,无浮点参数时必须将rax设为0,原代码未正确设置,可能导致函数内部栈解析错误。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 11:11:16