需要对汇编语言中的LEAVE指令进行一些解释

时间:2017-01-28 08:09:55

标签: assembly x86

我没有清楚地理解LEAVE功能,它是这两条指令的缩写:

MOV ESP, EBP
POP EBP

所以MOV ESP, EBP将ESP向下移动到EBP的水平(堆栈的开始)。

然后POP EBP,移动ESP指向的值并将其影响到EBP,并将ESP向下移动一步。

但我真的不明白,这两个操作如何与离开函数的事实联系起来(这是LEAVE的目的)。

你能帮我澄清一下吗?

2 个答案:

答案 0 :(得分:5)

一个常见的序言,例程开始时的指令序列,在32位和16位时代是

push ebp
mov ebp, esp

sub esp, <local_var_size>

push <clobbered_reg1>
push <clobbered_reg2>
...

这里没有什么是随意的,指令的顺序很重要,我们最终会

|parN | <-- EBP + 04 + n*4                 par1..parN = Routine parameters
...     ...                                ra = Return address
|par2 | <-- EBP + 0ch                      o ebp = Original (caller) EBP
|par1 | <-- EBP + 08h                      lvar1..lavarM = Local variables
|ra   | <-- EBP + 04h                      creg1..cregK = Clobbered registers
|o ebp| <-- EBP
|lvar1| <-- EBP - 04h
|lvar2| <-- EBP - 08h
...    ...
|lvarM| <-- EBP - m*4
|creg1|
|creg2|
...
|cregK| <-- ESP

了解如何使用ebp中的合适指针轻松访问所有数据(参数为连续正偏移大于或等于8,局部变量为负偏移小于或等于4)以及此模型的缩放程度对于更多的本地变量或参数。
因此,ebp称为帧指针。

结语必须撤消所有这一切 一种可能的变体是

pop <clobbered_regK>
...
pop <clobbered_reg1>

add esp, <local_var_size>

pop ebp
ret n*4

然而,这涉及重复<local_var_size> - 很容易忘记保持两个版本同步 在分配本地变量之前,我们可以利用ebpesp的值这一事实,因此通过恢复该值,我们可以有效地将它们全部解除分配。

pop <clobbered_regK>
...
pop <clobbered_reg1>

mov esp, ebp

pop ebp
ret n*4

但是最后的第三条和第二条指令是leave指令的作用。所以:

pop <clobbered_regK>
...
pop <clobbered_reg1>

leave
ret n*4

是等同的序幕。

enter是一条糟糕的指令,而leave可用于优化代码空间。

答案 1 :(得分:0)

  

但是我真的不明白,这两个操作如何与离开函数这一事实联系在一起(这是LEAVE的目的)。

这不是LEAVE的目的。 RET的目的。离开实际上并不会做任何事情,除了修改栈。实际上,您可以LEAVE然后设置另一个堆栈框架,并且仍然保留在相同的功能中。