Linux 堆栈框架如何工作?;

Linux 堆栈框架如何工作?;,linux,assembly,x86-64,callstack,Linux,Assembly,X86 64,Callstack,我正在阅读csapp,一些代码(x86-64)让我困惑 书中说“pushq%rbp”等于: c代码是: long P(long x,long y) { long u = Q(y); long v = Q(x); return u + v; } 书中给出的汇编代码的一部分是: pushq %rbp pushq %rbx subq $8,%rsp “subq”把我弄糊涂了 为什么会这样 堆栈是一个向下扩展的内存块。内存

我正在阅读csapp,一些代码(x86-64)让我困惑

书中说“pushq%rbp”等于:

c代码是:

  long P(long x,long y)
  {
      long u = Q(y);
      long v = Q(x);

      return u + v;
  }
书中给出的汇编代码的一部分是:

    pushq %rbp 
    pushq %rbx
    subq $8,%rsp 
“subq”把我弄糊涂了


为什么会这样

堆栈是一个向下扩展的内存块。内存中有一个点由堆栈顶部的
rsp
/
esp
寄存器指示。上面的所有内存都被放在堆栈上的东西占用,下面的所有内存都是空闲的

如果要在堆栈上放置某些内容,则需要将
rsp
寄存器(这是
sub
指令所做的)减少所需的字节数,
rsp
现在将指向所需的新保留服务器区域

让我们看看这个简单的例子:

rsp
指向地址100。如上所述-地址100以上的整个内存被使用,地址100以下的内存是空闲的。因此,如果需要4个字节,则将rsp减小4,使其指向96。由于您刚刚减少了rsp,您知道存储单元96、97、98和99是您的,您可以使用它们。当堆栈上需要更多字节时,可以再次减少rsp以获得更多字节

有两种方法可以把东西放在堆栈上。 1.您可以如上所示降低rsp
。
2.您可以使用与之完全相同的
push
指令,但只需一步:
push-rax
rsp
减少8个字节(寄存器的大小
rax
),然后将其值保存在保留区域

有时也使用rbp
寄存器对堆栈进行操作。 若您需要堆栈上更大的区域,例如局部变量,则在堆栈上保留所需的数量,然后将当前
rsp
值保存到
rbp
中。因此,
rbp
是一种能记住你所在区域的书签。然后您可以
在堆栈上推送
更多内容,而不会丢失分配区域所在的信息


在离开函数之前,所有放在堆栈上的东西都需要从中取出。它是通过
pop
指令完成的,与
push
相反-从堆栈中获取值并将其移动到寄存器,然后增加
rsp
。或者,如果不需要恢复寄存器值,可以增加rsp。

x86-64使用8字节堆栈“插槽”。您永远不希望堆栈未对齐,因此您总是将其调整为8的倍数。(或者通常要达到一个16字节对齐边界),如果你考虑分配一个可变的空间,比如“代码> ALLACA < /代码>或C99可变长度数组,或者<代码>和$-32,%RSP ,以将堆栈与AVX对齐,则RBP作为书签类比效果更好。对于已知数量的推送或其他分配,编译器仍然会优化帧指针,因为它们知道移动RSP的距离,从而知道当前到任何给定位置的偏移量。我想知道为什么本书中的代码同时使用“pushq”和“subq”,如您所说的“pushq”:“您可以使用与此完全相同的push指令,但只需一步:push rax会将rsp减少8个字节(rax寄存器的大小),然后将其值保存在保留区域。”既然“pushq”已经减少了rsp,为什么还要再次使用“subq”呢?现在我只想知道“subq”“这是用来干什么的@MichałWalenciakIt在希望将寄存器的值保存到堆栈上时使用push,在希望保留未初始化的堆栈空间时使用sub。未初始化的空间通常稍后写入。Sub还用于将堆栈对齐到16的倍数,在这种情况下,可能不使用空间。
    pushq %rbp 
    pushq %rbx
    subq $8,%rsp