堆栈帧上的gcc函数参数对齐

时间:2014-05-07 08:55:38

标签: c gcc assembly callstack

我在我的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这样做的文档?

1 个答案:

答案 0 :(得分:4)

参数在寄存器中传递 - ediesiedx(然后rcxr8r9然后才推送在堆栈上) - 正是Linux amd64 calling convention的任务。

您在函数中看到的是编译器在使用-O0进行编译时如何保存它们,因此它们可以在内存中调试器可以修改它们。它可以以任何方式自由地进行,并巧妙地进行空间优化。

它执行此操作的唯一原因是gcc -O0 始终溢出/重新加载C语句之间的所有C变量,以支持修改变量并在函数中使用调试器跳转。

所有这些都将在发布版本中进行优化。