C Assembly-为什么%rsp减少了这么多,为什么参数存储在堆栈的顶部?
这里是装配新手。。。我编写了以下简单的C程序:C Assembly-为什么%rsp减少了这么多,为什么参数存储在堆栈的顶部?,c,assembly,C,Assembly,这里是装配新手。。。我编写了以下简单的C程序: void fun(int x, int* y) { char arr[4]; int* sp; sp = y; } int main() { int i = 4; fun(i, &i); return 0; } 我用gcc编译它,并用-S运行objdump,但汇编代码输出让我感到困惑: 000000000040055d <fun>: void fun(int x, int* y) { 40055d
void fun(int x, int* y)
{
char arr[4];
int* sp;
sp = y;
}
int main()
{
int i = 4;
fun(i, &i);
return 0;
}
我用gcc编译它,并用-S运行objdump,但汇编代码输出让我感到困惑:
000000000040055d <fun>:
void fun(int x, int* y)
{
40055d: 55 push %rbp
40055e: 48 89 e5 mov %rsp,%rbp
400561: 48 83 ec 30 sub $0x30,%rsp
400565: 89 7d dc mov %edi,-0x24(%rbp)
400568: 48 89 75 d0 mov %rsi,-0x30(%rbp)
40056c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400573: 00 00
400575: 48 89 45 f8 mov %rax,-0x8(%rbp)
400579: 31 c0 xor %eax,%eax
char arr[4];
int* sp;
sp = y;
40057b: 48 8b 45 d0 mov -0x30(%rbp),%rax
40057f: 48 89 45 e8 mov %rax,-0x18(%rbp)
}
400583: 48 8b 45 f8 mov -0x8(%rbp),%rax
400587: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40058e: 00 00
400590: 74 05 je 400597 <fun+0x3a>
400592: e8 a9 fe ff ff callq 400440 <__stack_chk_fail@plt>
400597: c9 leaveq
400598: c3 retq
0000000000400599 <main>:
int main()
{
400599: 55 push %rbp
40059a: 48 89 e5 mov %rsp,%rbp
40059d: 48 83 ec 10 sub $0x10,%rsp
int i = 4;
4005a1: c7 45 fc 04 00 00 00 movl $0x4,-0x4(%rbp)
fun(i, &i);
4005a8: 8b 45 fc mov -0x4(%rbp),%eax
4005ab: 48 8d 55 fc lea -0x4(%rbp),%rdx
4005af: 48 89 d6 mov %rdx,%rsi
4005b2: 89 c7 mov %eax,%edi
4005b4: e8 a4 ff ff ff callq 40055d <fun>
return 0;
4005b9: b8 00 00 00 00 mov $0x0,%eax
}
4005be: c9 leaveq
4005bf: c3 retq
为什么在调用“fun”(48字节)时堆栈指针会减少这么多?我假设它与对齐问题有关,但我无法想象为什么它需要如此多的空间(我只计算12个字节的局部变量(假设8个字节指针))
其次,我认为在x86_64中,函数的参数要么存储在特定的寄存器中,要么如果有很多,就存储在基指针%rbp的“上方”(堆栈向下增长)。除了“倒挂”之外,与图片中的一样
但台词:
400565: 89 7d dc mov %edi,-0x24(%rbp)
400568: 48 89 75 d0 mov %rsi,-0x30(%rbp)
请告诉我,它们是从堆栈底部向下存储的(%rsi和%edi是main放置参数的位置,就在调用“fun”之前,而从%rbp向下的0x30正是堆栈指针指向的位置…)。当我尝试处理它们时,比如将它们的值赋给局部变量,它会从堆栈头部附近的位置获取它们:
sp = y;
40057b: 48 8b 45 d0 mov -0x30(%rbp),%rax
40057f: 48 89 45 e8 mov %rax,-0x18(%rbp)
。。。这是怎么回事?!根据我阅读的每一篇基本教程,我希望它们从存储在其中的寄存器中获取参数,或者在基指针的正上方获取参数,我认为它们“应该在”的位置。我在这里找到的每一个关于堆栈框架问题的答案和帖子都证实了我对堆栈框架“应该”是什么样子的理解,那么为什么我的汇编输出如此奇怪呢?因为这些东西是真实情况的一个极其简化的版本。这就像想知道为什么牛顿力学不把行星的运动建模到毫米。编译器需要堆栈空间来处理各种事情。例如,保存被叫方保存的寄存器 此外,基本事实是调试模式编译包含各种调试和检查机制。编译器输出各种检查代码是否正确的代码,例如调用
\uuuu stack\u chk\u fail
只有两种方法可以理解给定编译器的输出。首先是实现编译器,或者非常熟悉实现。第二,接受你所理解的一切都是一种粗略的简化。选择一个。因为您在编译时没有进行优化,编译器会做很多额外的工作,以使调试更容易,这会占用大量额外的空间
- 它不会试图压缩堆栈帧以重用内存,也不会丢弃任何未使用的内容
- 它冗余地将参数复制到堆栈帧中(这需要更多内存)
- 它将一个“金丝雀”复制到堆栈上,以防止堆栈崩溃缓冲区溢出(即使在这段代码中不会发生)
尝试打开优化,您将看到更多真实的代码。这是64位代码。堆栈空间的0x30对应于堆栈上的6个插槽。您拥有的似乎是:
- 函数参数的2个插槽(碰巧也在寄存器中传递)
- 2个用于局部变量的插槽
- 1个用于保存AX寄存器的插槽
- 1插槽看起来像堆栈保护,可能与调试模式有关
最好的办法是做实验,而不是问问题。尝试以不同的模式(调试、优化等)编译,并使用不同数量和类型的参数和变量。有时候问别人太容易了——你可以通过自己的实验学习更好。你可以让编译器输出它生成的asm,而不是objdump。。。通常使用gcc或clang:
gcc file.c-S-o file.S
(或类似)被调用方溢出寄存器的阴影空间吗?查看未优化的代码不是很有用。看起来很邋遢,因为你说没关系。使用AT&T语法在64位代码中看到32位Windows风格的异常处理肯定会让人大吃一惊,《角斗士vs恐龙》电影风格;)您必须更明确地说明您的工具。您忘了启用优化,所以您的代码效率低下。
sp = y;
40057b: 48 8b 45 d0 mov -0x30(%rbp),%rax
40057f: 48 89 45 e8 mov %rax,-0x18(%rbp)