我在我的Ubuntu14.04 x86_64系统上有这个test.c。
void foo(int a, long b, int c) {
}
int main() {
foo(0x1, 0x2, 0x3);
}
我使用gcc --no-stack-protector -g test.c -o test
对其进行了编译,并使用objdump -dS test -j .text
00000000004004ed <_Z3fooili>:
void foo(int a, long b, int c) {
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 89 7d fc mov %edi,-0x4(%rbp)
4004f4: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4004f8: 89 55 f8 mov %edx,-0x8(%rbp) // !!Attention here!!
}
4004fb: 5d pop %rbp
4004fc: c3 retq
00000000004004fd <main>:
int main() {
4004fd: 55 push %rbp
4004fe: 48 89 e5 mov %rsp,%rbp
foo(0x1, 0x2, 0x3);
400501: ba 03 00 00 00 mov $0x3,%edx
400506: be 02 00 00 00 mov $0x2,%esi
40050b: bf 01 00 00 00 mov $0x1,%edi
400510: e8 d8 ff ff ff callq 4004ed <_Z3fooili>
}
400515: b8 00 00 00 00 mov $0x0,%eax
40051a: 5d pop %rbp
40051b: c3 retq
40051c: 0f 1f 40 00 nopl 0x0(%rax)
我知道应该按顺序将函数参数从右向左推送到堆栈。所以我期待这个
void foo(int a, long b, int c) {
push %rbp
mov %rsp,%rbp
mov %edi,-0x4(%rbp)
mov %rsi,-0x10(%rbp)
mov %edx,-0x14(%rbp) // c should be push on stack after b, not after a
但是gcc似乎足够聪明,可以在(0x1)之后立即按参数c(0x3)来保存应该为b(0x2)的数据对齐保留的四个字节。有人可以解释一下,并向我展示一些关于为什么gcc这样做的文档?
答案 0 :(得分:4)
参数在寄存器中传递 - edi
,esi
,edx
(然后rcx
,r8
,r9
然后才推送在堆栈上) - 正是Linux amd64 calling convention的任务。
您在函数中看到的是编译器在使用-O0
进行编译时如何保存它们,因此它们可以在内存中调试器可以修改它们。它可以以任何方式自由地进行,并巧妙地进行空间优化。
它执行此操作的唯一原因是gcc -O0
始终溢出/重新加载C语句之间的所有C变量,以支持修改变量并在函数中使用调试器跳转。
所有这些都将在发布版本中进行优化。