gpt4 book ai didi

assembly - 如何正确引用栈上的局部变量

转载 作者:行者123 更新时间:2023-12-05 02:20:20 29 4
gpt4 key购买 nike

进入函数,标准序言

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

现在如何引用局部变量,通过 rsp + positive offset,还是通过 rbp + negative offset?

阅读 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 进行堆栈引用一切顺利。

最佳答案

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

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


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

使用 RSP 相对寻址模式的要点是您可以避免浪费指令制作堆栈帧,因此 RBP 只是另一个可以保存/恢复的调用保存寄存器(如 RBX)并用于任何你想要的。


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

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

关于assembly - 如何正确引用栈上的局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39547403/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com