【问题标题】:Assembly - why is %rsp decremented by so much, and why are arguments stored at the top of the stack?汇编 - 为什么 %rsp 减少了这么多,为什么参数存储在堆栈的顶部?
【发布时间】: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 风格的异常处理肯定会引起一些人的注意,角斗士对恐龙的电影​​风格;) 你必须更明确地了解你的工具。
  • 你忘了开启优化,所以你得到了低效的代码。

标签: c assembly


【解决方案1】:

因为这些东西是真实情况的极其简化的版本。这就像想知道为什么牛顿力学没有将行星的运动建模到毫米级。编译器需要堆栈空间来处理各种事情。例如,保存被调用者保存的寄存器。

此外,基本事实是调试模式编译包含各种调试和检查机制。编译器会输出各种检查代码是否正确的代码,例如对__stack_chk_fail 的调用。

只有两种方法可以理解给定编译器的输出。首先是实现编译器,或者对实现非常熟悉。第二个是接受你所理解的一切都是一种粗略的简化。选择一个。

【讨论】:

    【解决方案2】:

    因为您是在没有优化的情况下进行编译,所以编译器会做很多额外的事情来让调试变得更容易,这会占用大量额外的空间。

    • 它不会尝试压缩堆栈帧以将内存重用于任何东西,或删除任何未使用的东西。
    • 它冗余地将参数复制到堆栈帧中(这需要更多内存)
    • 它将“金丝雀”复制到堆栈上,以防止堆栈粉碎缓冲区溢出(即使在此代码中不会发生)。

    尝试开启优化,你会看到更多真实的代码。

    【讨论】:

      【解决方案3】:

      这是 64 位代码。堆栈空间的 0x30 对应于堆栈上的 6 个插槽。你看起来是这样的:

      • 2 个函数参数槽(也恰好在寄存器中传递)
      • 2 个局部变量槽
      • 1 个用于保存 AX 寄存器的插槽
      • 1 个插槽看起来像一个堆栈保护,可能与 DEBUG 模式有关。

      最好的办法是尝试而不是提出问题。尝试以不同的模式(调试、优化等)编译,并使用不同数量和类型的参数和变量。有时问别人太容易了——通过自己的实验你会学得更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-26
        • 2019-11-23
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 2012-11-18
        • 2012-01-31
        • 1970-01-01
        相关资源
        最近更新 更多