【问题标题】:how to reference local variables on the stack properly如何正确引用堆栈上的局部变量
【发布时间】:2017-01-25 14:56:03
【问题描述】:

进入函数,标准序言

push rbp
mov rbp, rsp
sub rsp, 128 ; large space for storing doubles, for example

现在如何引用局部变量,通过rsp +正偏移,还是通过rbp +负偏移?

阅读https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames,确实很好理解。 它写道

...esp 的值不能可靠地用于确定(使用适当的偏移量)特定局部变量的内存位置。为了解决这个问题,许多编译器使用 ebp 寄存器的负偏移量来访问局部变量。

为什么不可靠?在那个问题之前,我通过 rsp 访问局部变量,如下所示:

mov rax, [rsp+.length] ; get length of array
mov [rsp+8], rax ; store sum at the stack

使用 rsp 进行堆栈引用,一切都很顺利。

【问题讨论】:

  • 栈指针可以经常用来确定栈上变量的地址。但是,如果函数使用可变长度数组或alloca() 的等效项,则可能不再可能使用堆栈指针的偏移量。
  • @EOF,所以除了这两个之外,在所有情况下都应该使用相对于 rsp 的寻址?

标签: assembly stack x86-64


【解决方案1】:

查看 gcc 输出。优化时默认为-fomit-frame-pointer,仅在函数使用变长数组或需要将栈对齐到16B以上时才制作栈帧。

那个 wiki 页面基本上是错误的。没有可怕的怪异东西使它“不可靠”。唯一不能这样做的情况是,当您需要将 RSP 修改为非汇编时间常数的量时。


但是,如果您确实使用push rbp / mov rbp, rsp 创建堆栈帧,则应使用 RBP 相对寻址模式。它更有效,因为[rsp + 8] 需要一个额外的字节来编码(与[rbp - 8] 相比)。以 RSP 作为基址寄存器的寻址模式总是需要一个 SIB 字节,即使没有索引寄存器。

使用 RSP 相对寻址模式的意义在于您可以避免浪费指令来制作堆栈帧,因此 RBP 只是另一个可以保存/恢复的调用保存寄存器(如 RBX)并随心所欲地使用。


RBP 相对寻址的另一大优势是,从 RBP 到给定变量的偏移量在整个函数中保持不变。与编译器不同,我们这些微不足道的人类很容易被改变函数内部 RSP 的 push 和 pop 弄糊涂。当然,64 位代码几乎不会在序言和结尾之间的函数内更改 RSP,因为两个 ABI 都在寄存器中传递参数。在序言/尾声中保存/恢复一些保留调用的寄存器(如 RBX 或 R12-R15)通常比在函数内部进行 push/pop 更好(并且绝对比在循环内部更好)。当您需要溢出/重新加载时,mov 随机访问通常是最好的。

在 32 位代码中,用手写代码制作堆栈帧通常更有意义,尤其是。为了可维护性。在 64 位代码中,这通常不是什么大问题。尽管使用一对额外的 push/pop 保存/恢复额外的寄存器确实会改变堆栈布局,但如果在堆栈上传递了任何 args(例如,按值计算的大型结构,但编写函数以获取 const 指针),这确实很重要arg 代替!)。

【讨论】:

  • 非常有用,尤其是关于效率的提示。因此,在使用优化(帧指针省略)进行编译的情况下,由于需要更多字节来编码 rsp 寻址,因此代码会稍微大一些?
  • @BulatM.:有时每个函数省去 3 或 4 条指令所节省的成本会平衡它。那是 1 + 3 个字节的序言和 1 + 0 或 3 个字节的尾声。 (可选mov rsp, rbp,然后是pop rbp。或者某些编译器使用 LEAVE,它是 1 个字节,也就是 3 uops IIRC,所以如果 RSP 没有指向保存的 RBP 值,那么它是一个不错的选择。函数。)
  • 当你手写汇编时,堆栈指针可能很难跟踪,因为局部变量可能与堆栈指针没有固定的偏移量。
  • @FUZxxl:我完全同意。这对编译器来说不是问题,但对我们人类来说是:如果你手动推送或弹出,你一定不要忘记考虑新的 RSP 值,或者更改 RSP 以腾出空间。编译器不会忘记这些事情。
  • @RudyVelthuis:更新了我的答案,以解释像我们这样愚蠢的凡人与机器相比的弱点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多