【发布时间】:2014-09-17 22:39:17
【问题描述】:
在这里组装新手...我写了以下简单的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: 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
首先,在行中:
400561: 48 83 ec 30 sub $0x30,%rsp
为什么调用 'fun' 时堆栈指针减少了这么多(48 字节)?我认为它与对齐问题有关,但我无法想象为什么它需要这么多空间(我只计算局部变量的 12 个字节(假设 8 个字节指针))?
其次,我认为在 x86_64 中,函数的参数要么存储在特定的寄存器中,要么如果它们很多,就在基指针 %rbp 的“上方”(堆栈向下增长)。就像http://en.wikipedia.org/wiki/Call_stack#Structure 的图片一样,除了“颠倒”。
但台词:
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)
...这是怎么回事?!根据我阅读的每个基本教程,我希望他们从存储在其中的寄存器或基指针上方获取参数,我认为它们“应该是”。我在这里找到的与堆栈框架问题相关的每个答案和帖子都证实了我对堆栈框架“应该”是什么样子的理解,那么为什么我的组装输出如此奇怪?
【问题讨论】:
-
而不是做 objdump 你可以让你的编译器输出它生成的 asm...通常使用 gcc 或 clang:
gcc file.c -S -o file.s(或类似) -
被调用者溢出寄存器是影子空间吗?
-
查看未优化的代码并不是很有用。它看起来很草率,因为,好吧,你说没关系。在使用 AT&T 语法的 64 位代码中看到 32 位 Windows 风格的异常处理肯定会引起一些人的注意,角斗士对恐龙的电影风格;) 你必须更明确地了解你的工具。
-
你忘了开启优化,所以你得到了低效的代码。