含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 调用约定的栈对齐要求被违反:
调用约定的栈对齐规则:在遵循System V AMD64 ABI的Linux/macOS等系统中,调用函数前(执行
call指令时),栈指针%rsp必须是16字节对齐的。这是因为call指令会将8字节的返回地址压入栈,进入被调用函数后%rsp会变成8字节偏移,此时被调用函数的序言(如pushq %rbp)会再次将%rsp调整回16字节对齐,满足后续操作(比如scanf这类可能使用SSE指令的函数,这类指令要求操作数地址16字节对齐)的要求。现有代码的对齐破坏过程:
- 程序启动后,
_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字节对齐的指令,最终导致段错误。
- 程序启动后,
移除push/pop后的修复逻辑:
移除pushq %rbx和popq %rbx后,调用read_int前%rsp保持16字节对齐状态,完全符合调用约定要求,read_int和内部的scanf都能正常执行,因此段错误消失。
注意:虽然%rbx是被调用者保存寄存器,但本次问题的根源并非%rbx的值被修改,而是栈对齐错误。
内容的提问来源于stack exchange,提问作者capsule
相关产品推荐
相关产品推荐

