函数进入时为何执行‘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指令分配空间,从而减少指令开销。
默认编译下的栈帧分析
- 大结构体返回的ABI规则:当函数返回的结构体大小超过16字节时(你的
struct A占256字节/0x100),ABI规定调用方(这里是main函数)会提前在自己的栈帧中分配结构体空间,并将该空间的地址通过rdi寄存器传递给被调用函数。因此good函数不需要在自身栈帧中分配整个struct A的空间,而是直接在rdi指向的调用方空间中操作结构体数据。 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
相关产品推荐
相关产品推荐

