进入和离开大会?

时间:2011-05-02 15:24:56

标签: assembly x86

我正在阅读汇编语言艺术(Randall Hyde,link to Amazon),我在那本书中尝试了一个控制台应用程序。这是一个使用Win32 API函数为自己创建一个新控制台的程序。该程序包含一个名为LENSTR的过程,它将字符串的长度存储在EBP寄存器中。该功能的代码如下:

LENSTR PROC
ENTER 0, 0
PUSH  EAX
;----------------------
CLD
MOV   EDI, DWORD PTR [EBP+08H]
MOV   EBX, EDI
MOV   ECX, 100 ; Limit the string length
XOR   AL, AL
REPNE SCASB ; Find the 0 character
SUB   EDI, EBX ; String length including 0
MOV   EBX, EDI

DEC   EBX
;----------------------
POP   EAX
LEAVE
RET   4
LENSTR ENDP

您能在这里解释一下enterleave命令的用法吗?

3 个答案:

答案 0 :(得分:39)

Enter创建堆栈帧,leave销毁堆栈帧。使用0,0上的enter参数,它们基本上等同于:

; enter
push ebp
mov ebp, esp

; leave
mov esp, ebp
pop ebp

虽然在您发布的代码中没有使用它,但enter确实支持比上面显示的简单push / mov组合更多的功能。 enter的第一个参数指定为局部变量分配的空间量。例如,enter 5, 0大致相当于:

push ebp
mov ebp, esp
sub esp, 5

Enter还支持像Pascal这样可以使用嵌套函数/过程的语言:

procedure X;
    procedure Y;
    begin
        { ... }
    end
begin
   { ... }
end

在这种情况下,Y不仅可以访问自己的局部变量,还可以访问X本地的所有变量。这些可以嵌套到任意深度,因此您可以在Z内部Y访问其自己的局部变量,Y的变量和{{1的变量}}。 X的第二个参数指定了嵌套深度,因此enter将使用Xenter Sx, 0将使用Yenter Sy, 1将使用Z 1}}(其中enter Sz, 2SxSy分别表示SzXY本地变量的大小。 / p>

这将创建一个堆栈帧链,以便Z访问ZY的本地变量,依此类推。如果函数是递归的,那么这变得相当重要,因此调用X不能只是将堆栈向上移动到最近的两个堆栈帧 - 它需要跳过之前调用自身的堆栈帧,并直接返回到词汇父级函数/过程的堆栈帧,这与递归时的调用者不同。

这种复杂性也是C和C ++禁止嵌套函数的原因。鉴于进入/离开的存在,它们在英特尔处理器上相当容易支持,但在缺乏这种直接支持的许多其他处理器上可能要困难得多。

这至少也有助于解释Z的另一个......特征 - 对于这里使用的琐碎案例(即enter),它比使用{enter 0, 0的等价物慢得多1}} / push

答案 1 :(得分:13)

这是该功能的堆栈帧(激活记录)的设置。在内部,它通常看起来像这样:

push( ebp );         // Save a copy of the old EBP value

mov( esp, ebp );     // Get ptr to base of activation record into EBP

sub( NumVars, esp ); // Allocate storage for local variables.

然后当要再次销毁堆栈帧时,您必须执行以下操作:

   mov( ebp, esp );    // Deallocate locals and clean up stack.

   pop( ebp );         // Restore pointer to caller's activation record.

   ret();              // Return to the caller.

Here是使用HLA更好地解释它。虽然你正在阅读的书中有很好的解释,因为我也有这本书,我已经阅读了解释它的部分。

答案 2 :(得分:0)

输入并离开,只需设置堆栈帧。通常编译器会生成直接操作堆栈帧指针的代码,因为进入和离开相对于mov / sub并不是很快(过去它们曾经是286天之内:-))。