【问题标题】:How can RBP point any number of local_variable or parametes?RBP 如何指向任意数量的局部变量或参数?
【发布时间】:2023-04-01 01:05:01
【问题描述】:

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

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

【问题讨论】:

    标签: assembly x86-64 nasm stack-frame


    【解决方案1】:

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

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


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

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

    【讨论】:

      【解决方案2】:

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

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

      Register 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
      

      另请参阅 32bit64bit StdCall 约定示例。

      【讨论】:

      • 都是以 64 位粒度存储在堆栈上的所有项目。不是本地人,他们不是;编译器不会在 int a, b; 局部变量之间留下 4 个字节的未使用空间。在[rbp-1]int[rbp-4] 看到一个字符是完全正常的。也许您正在考虑堆栈参数,其中调用约定确实规定所有内容都有自己的 8 字节堆栈槽。
      • [rbp+4] 第一个参数是您应该选择的;这是 100% 错误的。 (这是保存的 RBP 值的中间值,x86-64 调用约定传递寄存器中的前几个参数。除非它们是不适合 reg 的结构,或者对象需要有地址的某些 C++ 原因.)
      • @PeterCordes Ad [rbp-1] 等等,当然,堆栈上的项目是可寻址字节粒度的。也许我应该用 pushed 替换 stored。例如,PUSH FS zeroextens 16bit 寄存器在 64 位模式下推送之前,AFAIK。
      • 如果这就是你的意思,那么绝对是的,你应该改变你的答案,而不是说什么是错误的!当然,本地变量并没有为每个变量分配一个单独的push,所以这里仍然不相关。这个问题不是很清楚,但确实区分了局部变量和参数。
      • 您仍然错误地声称 [rbp-4] 对于“第一个局部变量”不正确。如果您想选择问题中的任何内容,请针对“第一个参数”抱怨 [rbp+4],因为这是基于 32 位堆栈参数的唯一明显混淆迹象。 (虽然[ebp+4] 当然也不对;[esp+4] 在任何推送之前都是正确的。)
      猜你喜欢
      • 2019-11-07
      • 1970-01-01
      • 2011-11-26
      • 1970-01-01
      • 1970-01-01
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多