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

x86_64下GCC编译C程序预留未使用局部变量栈空间问题

问题核心原因

你产生疑惑的本质是混淆了32位x86和64位x86_64的汇编规则,同时误判了栈上指令的作用:

  • 你读的Programming from the Ground Up是基于32位x86架构编写的,32位环境下栈操作以4字节为单位,没有强制的16字节栈对齐要求,预留局部变量确实是通过直接下调栈指针实现。但你当前运行的是x86_64 GNU/Linux环境,遵循System V AMD64调用约定,栈操作规则和32位差异极大。
  • main函数开头的sub $0x8, %rsp不是给局部变量预留空间:你的main函数根本没有定义任何局部变量,不需要分配局部变量栈空间。这条指令的唯一作用是满足x86_64的栈对齐要求:call指令执行时会自动把8字节的返回地址压入栈,进入函数后rsp的偏移是8字节,没有对齐到ABI要求的16字节边界;把rsp减8之后,后续再执行call调用其他函数(比如test、printf)时,栈刚好满足16字节对齐要求,不会触发运行时错误。
  • 你观察到0x7fffffffe390~0x7fffffffe397这8字节内容始终不变是正常现象:这部分就是对齐用的占位空间,整个main函数执行过程中从来没有往这个地址写入过任何数据,它的内容当然不会发生变化,你的测试方式没有问题。
  • 你搞错了main函数返回地址的位置:执行main函数第一条指令前,rsp指向的0x7fffffffe398位置存储的才是main的返回地址(即libc启动代码中调用main的下一条指令地址,也就是你看到的0xf7df20b3...那个值),不是0x7fffffffe390位置。
其他现象补充说明
  • 你执行的6次pushq操作压入的是test函数的第7到第12个参数:x86_64约定前6个整型参数通过rdi、rsi、rdx、rcx、r8、r9寄存器传递,超过6个的参数才需要压栈传递。6次push总共占用6*8=48字节,也就是0x30,和调用完test后add $0x30, %rsp回收栈空间的操作完全对应。
  • 你标注为“local variables”的0x7fffffffe360~0x7fffffffe388区域根本不是main的局部变量,是你为调用test临时压入的栈参数,属于函数调用时的参数传递空间,不属于main的局部变量存储区。
  • 函数返回前的add $0x8, %rsp是回收开头为栈对齐分配的8字节占位空间,之后执行retq就可以正确弹出返回地址,回到libc启动流程。
验证方式

如果想观察真正的局部变量栈预留逻辑,可以在main函数里定义实际的局部变量,比如:

int main(void){
    int a = 0x123;
    int b = 0x456;
    test(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12);
    printf("Wick is me! a=%d,b=%d", a, b);
    return 0;
}

再用同样的gcc -Og -g参数编译反汇编,就能看到开头sub指令给rsp减的数值会变大,同时会生成往rsp对应偏移位置写入0x123、0x456的指令,这部分才是真正的局部变量预留空间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 20:01:22