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

juR*_*s75 2 x86 assembly

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

MOV ESP, EBP
POP EBP
Run Code Online (Sandbox Code Playgroud)

因此MOV ESP, EBP将ESP向下移动到EBP(堆栈的开始)的水平.

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

但我真的没有看到,这两个操作如何与离开函数的事实相关联(这是目的LEAVE).

你能帮我澄清一下吗?

Mar*_*oom 5

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

push ebp
mov ebp, esp

sub esp, <local_var_size>

push <clobbered_reg1>
push <clobbered_reg2>
...
Run Code Online (Sandbox Code Playgroud)

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

|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
Run Code Online (Sandbox Code Playgroud)

看看如何使用合适的指针ebp(参数为连续正偏移大于或等于8,本地变量为负偏移小于或等于4)轻松访问所有数据,以及此模型如何适应更多数量的本地变量或参数.
因此ebp,称为帧指针.

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

pop <clobbered_regK>
...
pop <clobbered_reg1>

add esp, <local_var_size>

pop ebp
ret n*4
Run Code Online (Sandbox Code Playgroud)

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

pop <clobbered_regK>
...
pop <clobbered_reg1>

mov esp, ebp

pop ebp
ret n*4
Run Code Online (Sandbox Code Playgroud)

但是从结尾开始的第三条和第二条指令就是leave指令的作用.所以:

pop <clobbered_regK>
...
pop <clobbered_reg1>

leave
ret n*4
Run Code Online (Sandbox Code Playgroud)

是等同的序幕.


enter是一个很差的指令,leave可用于优化代码空间.