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

函数进入时为何执行‘sub rsp,0xa0’?x86_64架构栈空间疑问

为什么x86_64下GCC编译生成sub rsp,0xa0而非对应结构体大小的指令?

首先看你提供的C代码:

struct A {
    int data[64];
};

struct A good(int x, int y) {
    struct A a;
    a.data[x] = y;
    return a;
}

int main() {
    struct A a;
    a = good(10,11);
}

编译反汇编后的关键指令:

$ gcc good.c -g --no-stack-protector
$ gdb -batch -ex 'disassemble good' ./a.out
Dump of assembler code for function good:
   0x0000000000001129 <+0>: endbr64 
   0x000000000000112d <+4>: push   rbp
   0x000000000000112e <+5>: mov    rbp,rsp
   0x0000000000001131 <+8>: push   rbx
   0x0000000000001132 <+9>: sub    rsp,0xa0         <- 该行令我困惑,为何是0xa0?
   0x0000000000001139 <+16>:    mov    QWORD PTR [rbp-0x118],rdi
   0x0000000000001140 <+23>:    mov    DWORD PTR [rbp-0x11c],esi
   0x0000000000001146 <+29>:    mov    DWORD PTR [rbp-0x120],edx
   ...

核心原因:x86_64 System V ABI的红区(Red Zone)机制

x86_64的System V应用二进制接口(ABI)定义了红区:栈指针rsp以下128字节(0x80)的内存区域,这个区域不会被信号处理函数或中断程序修改。编译器可以直接使用红区存放局部变量、临时数据或寄存器副本,无需通过sub rsp指令分配空间,从而减少指令开销。

默认编译下的栈帧分析

  1. 大结构体返回的ABI规则:当函数返回的结构体大小超过16字节时(你的struct A占256字节/0x100),ABI规定调用方(这里是main函数)会提前在自己的栈帧中分配结构体空间,并将该空间的地址通过rdi寄存器传递给被调用函数。因此good函数不需要在自身栈帧中分配整个struct A的空间,而是直接在rdi指向的调用方空间中操作结构体数据。
  2. sub rsp,0xa0的作用:good函数的栈帧仅需要保存被调用者保存寄存器(如rbx)、调试用的参数副本(rdi/esi/edx)以及少量临时空间。其中参数副本被存放在红区内(比如[rbp-0x118]对应rsp以下0x70字节,小于红区的0x80限制),无需分配栈空间。sub rsp,0xa0是为了分配栈帧中超出红区的额外空间,具体大小由编译器结合调试选项(-g)和优化策略决定。

禁用红区后的变化

当添加-mno-red-zone选项时,编译器无法使用红区,所有局部数据和寄存器副本必须通过调整rsp在栈帧中分配:

  • 栈帧需要包含:保存的rbx寄存器(8字节)、调试用的参数副本(rdi8字节+esi4字节+edx4字节,对齐后占16字节)、临时空间以及栈对齐填充。
  • 最终sub rsp,0x118加上push rbx的8字节,总栈帧大小为0x120,满足x86_64 ABI的16字节对齐要求。

内容的提问来源于stack exchange,提问作者L. Ouyang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:03:21