首页 > 解决方案 > 我们如何判断堆栈帧当前是空的?

问题描述

假设在一个循环中,我想根据一些比较条件从堆栈中弹出元素(该函数还将根据一些条件将一些元素推送到堆栈中)。如何避免弹出空堆栈?

如果在函数的开头,我做

push    %rbp
movq %rsp, %rbp

在循环内部,我检查是否

cmpq %rbp, %rsp
je emptyStack

这是一个可能的解决方案吗?

标签: assemblystackx86-64callstack

解决方案


是的,如果您想将调用堆栈用作堆栈数据结构,则弹出直到堆栈指针回到起点是正常的。将地址视为未签名:如果要检查 RSP 是否低于 RBP,请使用cmp/ jb。或者只是cmp/jne如果您确定 RSP 不能超过RBP。

如果您需要除数据结构之外的局部变量的任何堆栈空间,则需要除 之外的参考点rbp,或者仅使用rbp 不完全传统的帧指针。例如,在将 RSP复制到 RBP之前,移动 RSP 为当地人腾出空间。无论如何,拥有一个帧指针是可选的。

在 RBP 上方保留一些仍然是堆栈数据结构的一部分的空间还可以简化极端情况的处理,而不会有踩到已保存的 RBP 值或返回地址的风险。无论出于何种原因,您都可以让它更高。

例如

func:
    push  %rbp
    sub   $32, %rsp
    mov   %rsp, %rbp
      # Space from 0(%rbp) to 31(%rbp) can be used for locals separate from your stack data structure

...

    lea    32(%rbp), %rsp       # point RSP at the saved RBP value
    pop    %rbp
    ret

请注意,寻址模式0(%rbp)仍然需要 a disp8,不像0(%rbx)它可以只使用无位移 as (%rbx)。因此,您可能会选择使用不同的寄存器(如 RBX)作为锚点。如果这是一个叶函数,甚至是一个调用破坏寄存器,比如 RDX 或 R8。(如果您不打算在任何寻址模式下使用它,那么选择一个需要 REX 前缀的模式并不重要;无论如何,在与 RSP 进行比较时,您将使用 64 位操作数大小。除非您想通过假设您的堆栈不跨越 4GB 边界并使用cmp %ecx, %esp来节省 1 个字节的代码大小来进行优化。)

func:
    lea   -8(%rsp), %rsi        # or just mov, depending what you want.

 ...

    # once RSP is pointing back where it started
    ret

推荐阅读