我在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似乎足够聪明,可以将参数c(0x3)紧跟在a(0x1)之后,以保存应为b(0x2)的数据对齐保留的四个字节。有人可以解释一下,并告诉我一些有关为什么gcc这样做的文档吗?
这些参数在寄存器中传递- ,edi
,esi
(edx
那么rcx
,r8
,r9
然后才推向堆栈) -正是在Linux的AMD64调用约定任务。
您在函数中看到的只是使用进行编译时,编译器在输入时如何保存它们-O0
,因此它们位于内存中,调试器可以对其进行修改。它可以按照自己想要的任何方式自由进行操作,并且可以巧妙地进行空间优化。
这样做的唯一原因是,gcc -O0
总是在C语句之间溢出/重新加载所有C变量,以支持修改变量和使用调试器在函数中的各行之间跳转。
最后,所有这些都将在发布版本中进行优化。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句