在功能结尾混乱之前,堆栈必须是干净的

时间:2015-03-21 16:50:58

标签: c++ assembly visual-studio-2008

我正在从"Assembly Language Step-by-Step: Programming with Linux" by Jeff Dunteman这本书中学习汇编语言,并且在书中遇到了一个我很可能会误解的有趣段落,因此会对以下内容有所了解:

  

“在我们销毁堆栈帧并返回控制之前,堆栈必须是干净的。这只是意味着在程序运行期间我们可能已经推入堆栈的任何临时值都必须消失。堆栈上剩下的所有值都应该消失。是来电者的EBP,EBX,ESI和EDI值。

     

...

     

一旦堆栈干净,为了破坏堆栈帧,我们必须首先将调用者的寄存器值弹回到它们的寄存器中,确保pops的顺序正确。

     

...

     

我们通过将值从EBP移动到ESP来恢复调用者的ESP,最后将调用者的EBP值从堆栈中弹出。“

考虑从Visual Studio 2008生成的以下代码:

int myFuncSum( int a, int b)
{
001B1020  push        ebp  
001B1021  mov         ebp,esp 
001B1023  push        ecx       <------------------
    int c;
    c = a + b;
001B1024  mov         eax,dword ptr [ebp+8] 
001B1027  add         eax,dword ptr [ebp+0Ch] 
001B102A  mov         dword ptr [ebp-4],eax 
    return c;
001B102D  mov         eax,dword ptr [ebp-4] 
}
001B1030  mov         esp,ebp 
001B1032  pop         ebp  
001B1033  ret     

ecx(指示)的值被推送到我的变量c的堆栈上,就我所知,当我们重置ESP时,它只是从堆栈中消失了;然而,正如引用的那样,该书指出在我们重置ESP之前,堆栈必须是干净的 。有人可以澄清我是否遗漏了什么?

2 个答案:

答案 0 :(得分:1)

Visual Studio 2008中的示例与本书并不矛盾。这本书涵盖了最精细的电话案例。请参阅x86-32 Calling Convention作为交叉参考,并将其与图片拼写出来。

在您的示例中,堆栈上没有保存调用者寄存器,因此不会执行pop指令。这是&#34;清理&#34;的一部分。必须在本书所指的mov esp, ebp之前发生。更具体地说,假设被调用者正在为调用者保存sidi,那么函数的前奏和后置可能如下所示:

push  ebp        ; save base pointer
mov   ebp, esp   ; setup stack frame in base pointer
sub   esp, 4     ; reserve 4 bytes of local data
push  si         ; save caller's registers
push  di

; do some stuff, reference 32-bit local variable -4(%ebp), etc
;   Use si and di for our own purposes...

; clean up

pop   di          ; do the stack clean up
pop   si          ;   restoring the caller's values
mov   esp, ebp    ; restore the stack pointer
pop   ebp
ret

在您的简单示例中,没有保存的调用者寄存器,因此最后不需要最终的pop指令。

也许是因为它更简单或更快,编译器选择执行以下指令代替sub esp, 4

push  ecx

但效果是一样的:为局部变量保留4个字节。

答案 1 :(得分:0)

注意说明:

push        ebp  
mov         ebp,esp   ; <<<<=== saves the stack base pointer

和说明:

mov         esp,ebp   ;  <<<<<== restore the stack base pointer 
pop         ebp  

因此,在此序列之后,堆栈再次清洁