什么是组装框架?

什么是堆栈框架的结构,以及如何在调用汇编函数时使用?

x86-32堆栈帧是通过执行创build的

function_start: push ebp mov ebp, esp 

所以它可以通过ebp访问,看起来像

 ebp+00 (current_frame) : prev_frame ebp+04 : return_address .... prev_frame : prev_prev_frame prev_frame+04 : prev_return_address 

通过汇编指令devise,使用ebp作为堆栈框架有一些优点,所以通常使用ebp寄存器访问参数和本地。

每个例程使用堆栈的一部分,我们称之为堆栈框架。 尽pipe汇编程序员并不是被迫遵循以下风格,但强烈推荐这是一种很好的做法。

每个例程的堆栈框架分为三部分:函数参数,前一个堆栈帧的反指针和局部variables。

第1部分:function参数

例程的堆栈帧的这部分是由调用者设置的。 使用'push'指令,调用者将参数压入堆栈。 不同的语言可能会以不同的顺序推送参数。 C,如果我没有记错的话,把它们从右到左推。 也就是说,如果你在打电话…

 foo (a, b, c); 

来电者将转换为…

 push c push b push a call foo 

随着每个物品被推入堆叠,堆叠增长下降。 也就是说,堆栈指针寄存器减less四(4)字节(在32位模式下),并且该项目被复制到由堆栈指针寄存器指向的存储器位置。 请注意,“调用”指令将隐式地推送堆栈上的返回地址。 清理参数将在第5部分中介绍。

第2部分:Stackframe返回指针

在这个时候,“调用”指令已经发出,我们现在处于被调用例程的开始。 如果我们想访问我们的参数,我们可以像访问它们…

 [esp + 0] - return address [esp + 4] - parameter 'a' [esp + 8] - parameter 'b' [esp + 12] - parameter 'c' 

然而,在我们为局部variables和东西划分空间之后,这会变得笨拙。 所以,除了堆栈指针寄存器以外,我们还使用一个堆栈指针寄存器。 但是,我们希望将堆栈库指针寄存器设置为当前帧,而不是前一个函数。 因此,我们将旧的堆栈保存在堆栈中(这会修改堆栈上的参数的偏移量),然后将当前的堆栈指针寄存器复制到堆栈指针寄存器。

 push ebp ; save previous stackbase-pointer register mov ebp, esp ; ebp = esp 

有时你可能只用“ENTER”命令就可以看到这一点。

第3部分:为局部variables雕刻空间

局部variables被存储在堆栈上。 由于堆栈增长下来,我们减去一些字节(足够存储我们的本地variables):

sub esp, n_bytes ; n_bytes = number of bytes required for local variables

第4部分:把它放在一起。 使用stackbase指针寄存器访问参数…

 [ebp + 16] - parameter 'c' [ebp + 12] - parameter 'b' [ebp + 8] - parameter 'a' [ebp + 4] - return address [ebp + 0] - saved stackbase-pointer register 

使用堆栈指针寄存器访问局部variables…

 [esp + (# - 4)] - top of local variables section [esp + 0] - bottom of local variables section 

第5部分:Stackframe清理

当我们离开例程时,必须清理堆栈框架。

 mov esp, ebp ; undo the carving of space for the local variables pop ebp ; restore the previous stackbase-pointer register 

有时您可能会看到“LEAVE”指令取代这两个指令。

根据您使用的语言,您可能会看到“RET”指令的两种forms之一。

 ret ret <some #> 

无论select哪一种都取决于语言的select(或者如果用汇编语言编写,你希望遵循的风格)。 第一种情况表明,调用者负责从堆栈中删除参数(使用foo(a,b,c)的例子,它将通过… add esp,12来实现),这是'C'的方式它。 第二种情况表明,返回指令会popup#个字(或#字节,我不记得是哪个),当它返回时从堆栈中删除参数。 如果我没有记错的话,这是帕斯卡使用的风格。

这很长,但我希望这可以帮助你更好地理解stackframes。

这取决于所使用的操作系统和语言。 因为在ASM中没有通用的堆栈格式,堆栈在ASM中做的唯一的事情就是在执行跳转子程序时存储返回地址。 当执行从子程序返回时,从堆栈中拾取地址并将其放入程序计数器(存储位置,下一个CPU执行指令将从中读取)

您将需要咨询您正在使用的编译器的文档。

编译器(取决于编译器)可以使用x86堆栈帧来传递参数(或指向参数的指针)并返回值。 看到这个