EBP如何指向任意数量的local_variable或参数?

How can RBP point any number of local_variable or parametes?

我是汇编新手(NASM)。我知道 RBP 指向函数中的任何参数和局部变量。它是通过简单的偏移量实现的。如果我们想获得第一个变量,我们 rbp-4,如果我们想获得第一个参数,我们将添加到 rbp 4。但是如果任何函数可以有任意数量的局部变量或参数,我们怎么能这样做呢?如果我们希望我们可以在一个函数中有 100 个变量,我们如何通过简单的常量偏移量指向任何变量?

谢谢。对不起我的英语不好。我不是母语人士。

函数参数和局部变量的寻址取决于所选择的calling convention。您 获取我们添加到 rbp 4 的第一个参数肯定是错误的,因为在 64 位模式下(通过使用 RBPRSP 进行寻址暗示)可以推送项目仅在 64 位粒度的堆栈上。也许您脑子里有 32 位 StandardCall 约定,函数的典型序言如下所示:

Function: PUSH EBP 
          MOV EBP,ESP 
          SUB ESP,LocalsSize

寄存器 EBP 可以在函数内部使用,用于寻址无限数量的参数和局部变量(我在这个例子中使用了三个参数和四个 DWORD 局部变量):

Invokation      Stack   Address

PUSH Param3     Param3  EBP+4*4
PUSH Param2     Param2  EBP+3*4
PUSH Param1     Param1  EBP+2*4
CALL Function   return
                 EBP  
                Local1  EBP-1*4
                Local2  EBP-2*4
                Local3  EBP-3*4
                Local4  EBP-4*4

另请参阅此 32bit and 64bit StdCall 约定示例。

在像 C 这样的语言中,你不能有可变数量的变量,所以编译器总是知道它把它们放在哪里。因此它知道每个变量的正确常量偏移量。

如果您谈论的是像 printf(char *, ...) 这样的可变参数函数,那么您有来自调用约定的关于它们如何布局的规则,并且您通常会增加一个指针。要按 arg-number 进行索引,您需要知道所有先前 args 的宽度。它们至少为 8,但根据调用约定可以更宽,而不是在 x86-64 System V 中使用隐藏指针。printf 具有 long double 的转换等(比 8 字节宽),因此它必须支持跟踪哪个 arg 在哪里,以防转换按数字

引用 arg

如果您正在谈论将堆栈用作堆栈数据结构,并可能在循环内使用压入/弹出,那么您需要保留一个指针以了解您的堆栈数据结构何时为“空”,并进一步弹出会吃掉其他本地变量。

让你的一些局部变量成为变长数组会让事情变得更棘手,比如 C int foo[n] 其中 n 是另一个变量。许多 C 编译器通过发明一个指向每个 VLA 的指针来处理它。指针具有已知的固定宽度,因此编译器知道在哪里可以找到它们,并且它们可以被初始化,因为 space 是为具有 sub rsp, rax / mov [rbp-16], rsp 的 VLA 保留的,例如。