试图了解堆栈帧 x86-64 的内容

Trying to understand contents of stack frame x86-64

我正在从程序员的角度研究计算机系统中的练习题 3.34,并试图准确理解正在发生的事情。问题指出“考虑一个函数 P,它生成名为 a0-a7 的局部值。然后它调用函数 Q 使用这些生成的值作为参数。GCC 为第一部分生成以下代码P”。我们得到以下汇编代码:

/* long P(long x)
 * x in %rdi */
P:
  pushq   %r15
  pushq   %r14
  pushq   %r13
  pushq   %r12
  pushq   %rbp
  pushq   %rbx
  subq    , %rsp
  leaq    1(%rdi), %r15
  leaq    2(%rdi), %r14
  leaq    3(%rdi), %r13
  leaq    4(%rdi), %r12
  leaq    5(%rdi), %rbp
  leaq    6(%rdi), %rax
  movq    %rax, (%rsp)
  leaq    7(%rdi), %rdx
  movq    %rdx, 8(%rsp)
  movl    [=10=], %eax
  call    Q

到目前为止,这是我的理解: 指令 pushq %r15pushq %rbx 被压入堆栈以保留这些值,并最终在过程 Preturns 时将它们替换到各自的寄存器中(因为它们是被叫保存的寄存器)。 我看到指令 subq , %rsp 在堆栈上分配了 24 个字节的 space。

不过我有两个问题:

  1. 加载有效地址线在做什么? 在我看来,它正在获取由 long x 寻址的内存位置,并将该新内存地址(在添加 1 或 2 或 ... 7 之后)存储在各种被调用者保存的寄存器中。这个对吗?我对他们存储的价值有点困惑?有什么意义吗?另外,函数 Q 将如何处理这些寄存器?它怎么知道将它们用作参数,因为它们似乎不是参数寄存器?只有 long x 作为参数传递(因为它在寄存器 %rdi.
  2. 堆栈的内容是什么?我看到分配了 24 个字节,但我似乎无法解释所有这些 space :( 我理解堆栈看起来像这样:
???????????????????????????????????:16
The result of 7(%rdi) (argument a7):8
The result of 6(%rdi) (argument a6):0 <--- %rsp

我似乎无法解释字节 16-23 中包含的内容:(

提前谢谢你,我真的很纠结这个问题。

首先,注意这个练习题有an erratum。局部变量不作为参数传递给 Q;而是 Q 被调用时没有参数。所以这就解释了为什么变量不在参数传递寄存器中。

eax奇怪的置零可能是 ; they might have accidentally declared void Q(); instead of void Q(void);. I'm not sure why the compiler emitted movl [=16=], %eax instead of the 解释的,貌似开启了优化,这是一个很基础的优化。)

现在至于lea,它实际上只是一条算术指令,编译器倾向于那样使用它。参见 What's the purpose of the LEA instruction?。所以 leaq 1(%rdi), %r15 只是将 rdi 中的值加 1,并将结果写入 r15rdi 中的值是否表示地址或数字或其他内容与机器无关。由于 rdi 包含参数 x,这实际上是在做

a0 = x + 1;
a1 = x + 2;
a2 = x + 3;
// ...

备选方案类似于 movq %rdi, %r15 ; addq , %r15,它包含更多说明。

当然,这些值被放入被调用者保存的寄存器(或内存,最后两个),这样它们就不会被调用 Q().

破坏

至于堆栈,。在调用 P 之前堆栈指针是 16 的倍数,而当我们 call Q 时它必须再次是 16 的倍数。我们的调用者的 call P 将 8 个字节压入堆栈,而序言中的各种寄存器压入又压入 48 个字节。因此,为了最终得到 16 的倍数,我们必须将堆栈指针调整为比 16 的倍数多 8(即 8 的奇数倍)。我们需要 16 个字节用于局部变量,因此我们必须将堆栈指针调整 24。这样就剩下 8 个字节的堆栈将不会用于任何事情,也就是 ?????? at 16(%rsp)