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

含pushq/popq %rbx的汇编程序运行段错误,求分析原因

段错误原因分析:pushq %rbx/popq %rbx引发的栈对齐问题

现有两个文件program.s和functions.c,执行以下命令编译:

gcc -c -std=c99 functions.c
gcc -o program program.s functions.o

运行./program时出现段错误;移除代码中的pushq %rbx和popq %rbx语句后,段错误消失。以下是两个文件的代码:

program.s 代码

.globl main
    .align 16    
main:
    pushq %rbp
    movq %rsp, %rbp
    pushq %rbx
    call    read_int@PLT
    popq %rbx
    popq %rbp
    retq 

functions.c 代码

#include <stdio.h>
#include <inttypes.h>

int64_t read_int() {
    int64_t i;
    scanf("%" SCNd64, &i);
    return i;
}

问题原因分析

问题的核心是x86-64 System V 调用约定的栈对齐要求被违反:

  1. 调用约定的栈对齐规则:在遵循System V AMD64 ABI的Linux/macOS等系统中,调用函数前(执行call指令时),栈指针%rsp必须是16字节对齐的。这是因为call指令会将8字节的返回地址压入栈,进入被调用函数后%rsp会变成8字节偏移,此时被调用函数的序言(如pushq %rbp)会再次将%rsp调整回16字节对齐,满足后续操作(比如scanf这类可能使用SSE指令的函数,这类指令要求操作数地址16字节对齐)的要求。

  2. 现有代码的对齐破坏过程:

    • 程序启动后,_start调用main时,call main会先将8字节返回地址压栈,因此进入main时%rsp是8字节对齐的。
    • main中执行pushq %rbp后,%rsp减8,回到16字节对齐状态。
    • 执行pushq %rbx后,%rsp再次减8,变成8字节对齐。此时调用read_int(执行call read_int@PLT),直接违反了“调用前%rsp必须16字节对齐”的要求。
    • read_int内部调用scanf时,由于栈未正确对齐,触发了依赖16字节对齐的指令,最终导致段错误。
  3. 移除push/pop后的修复逻辑:
    移除pushq %rbx和popq %rbx后,调用read_int前%rsp保持16字节对齐状态,完全符合调用约定要求,read_int和内部的scanf都能正常执行,因此段错误消失。

注意:虽然%rbx是被调用者保存寄存器,但本次问题的根源并非%rbx的值被修改,而是栈对齐错误。

内容的提问来源于stack exchange,提问作者capsule

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 03:49:59