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 位模式下(通过使用 RBP
或 RSP
进行寻址暗示)可以推送项目仅在 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
在像 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 保留的,例如。
我是汇编新手(NASM)。我知道 RBP 指向函数中的任何参数和局部变量。它是通过简单的偏移量实现的。如果我们想获得第一个变量,我们 rbp-4
,如果我们想获得第一个参数,我们将添加到 rbp 4。但是如果任何函数可以有任意数量的局部变量或参数,我们怎么能这样做呢?如果我们希望我们可以在一个函数中有 100 个变量,我们如何通过简单的常量偏移量指向任何变量?
谢谢。对不起我的英语不好。我不是母语人士。
函数参数和局部变量的寻址取决于所选择的calling convention。您 获取我们添加到 rbp 4 的第一个参数肯定是错误的,因为在 64 位模式下(通过使用 RBP
或 RSP
进行寻址暗示)可以推送项目仅在 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
在像 C 这样的语言中,你不能有可变数量的变量,所以编译器总是知道它把它们放在哪里。因此它知道每个变量的正确常量偏移量。
如果您谈论的是像 printf(char *, ...)
这样的可变参数函数,那么您有来自调用约定的关于它们如何布局的规则,并且您通常会增加一个指针。要按 arg-number 进行索引,您需要知道所有先前 args 的宽度。它们至少为 8,但根据调用约定可以更宽,而不是在 x86-64 System V 中使用隐藏指针。printf 具有 long double
的转换等(比 8 字节宽),因此它必须支持跟踪哪个 arg 在哪里,以防转换按数字
如果您正在谈论将堆栈用作堆栈数据结构,并可能在循环内使用压入/弹出,那么您需要保留一个指针以了解您的堆栈数据结构何时为“空”,并进一步弹出会吃掉其他本地变量。
让你的一些局部变量成为变长数组会让事情变得更棘手,比如 C int foo[n]
其中 n
是另一个变量。许多 C 编译器通过发明一个指向每个 VLA 的指针来处理它。指针具有已知的固定宽度,因此编译器知道在哪里可以找到它们,并且它们可以被初始化,因为 space 是为具有 sub rsp, rax
/ mov [rbp-16], rsp
的 VLA 保留的,例如。