子程序的压栈方式

当指定子程序的语言模式,或者使用.model中指定的语言模式时,如stdcall、pascal等,子程序的参数压栈方式是不同的,例如stdcall模式下,参数是从右向左压栈,而在pascal模式下,参数是从左向右压栈。

 

下面,以stdcall模式为例,说明调用一个子程序时,是如何压栈的,假设压栈前,esp的值为addr:

 

addr ……
addr - 4 ebp + 16 参数三
addr - 8 ebp + 12 参数二
addr - 12 ebp + 8 参数一
addr - 16 ebp + 4 返回地址
addr - 20 ebp 保存原ebp值,并且mov ebp, esp
addr - 24 ebp - 4 局部变量1
addr - 28 ebp - 8 局部变量2
……

 

如上表所示,如果栈是向下生长的,则在将参数和返回地址进栈后,需要保存当前的ebp,并且将当前的esp值赋予ebp,从而可以用ebp访问参数或者局部变量。同时,编译器在编译时,会在ret前,加上leave这条指令, 实现mov esp,ebp, pop ebp的功能。

 

而在子程序中如果要用uses或是pushad/popad对实现环境变量保存时,如果返回结果是保存在eax中,则一定要记得将返回结果保存起来,否则eax中的值会被重置为调用前的值,如下列代码所示:

 

.386  
.model flat, stdcall  
  
include    windows.inc  
include    kernel32.inc  
includelib kernel32.lib 
include	   user32.inc
includelib user32.lib

include    masm32.inc 
includelib masm32.lib  
include    debug.inc 
includelib debug.lib  
  
.data  
ddResult  dd  ?  
  
.code  
calc proc one, two, three  
    pushad  
    
    mov eax, one  
    add eax, two  
    add eax, three  
  
    mov ddResult, eax ;如果不在这里保存,返回的eax会是原来的值  
      
    PrintDec eax  
    PrintLine
    
    popad  
    
    PrintDec eax  
    PrintLine
    ret  
calc endp  
  
start proc  
    invoke calc, 1, 2, 3  
    PrintDec ddResult  ; 6 
     
    PrintLine
    PrintDec eax  ;
      
    ret  
start endp  
  
end start

 输出结果:


你可能感兴趣的:(Win32,汇编,栈,子程序,EBP)