天天看點

深度剖析函數的調用過程(棧幀)

當我們在學習C語言的時候,寫代碼一定會用到函數(main函數),但函數在使用過程中是如何調用的,當我們從彙編的角度來剖析函數的調用,會讓我們對函數的認識剛深一層。

注:使用的工具是VS2017

下面我将通過一段代碼來剖析函數是如何調用的

#include <stdio.h>

    int Add(int x, int y)
    {
        int z = ;
        z = x + y;
        return z;
    }

    int main()
    {
        int a = ;
        int b = ;
        int ret = Add(a, b);
        printf("%d\n", ret);
        return ;
    }
           

函數在調用的過程中,是需要為函數來開辟棧空間,用于本次函數的調用過程中臨時變量的儲存、現場保護,這塊空間就被稱為函數棧幀。

首先我們來研究一下main函數的彙編代碼:

int main()
    {
      push        ebp  
      mov         ebp,esp  
      sub         esp,h  
      push        ebx  
    A  push        esi  
    B  push        edi  
    C  lea         edi,[ebp-h]  
      mov         ecx,h  
      mov         eax,CCCCCCCCh  
    C  rep stos    dword ptr es:[edi]  
        int a = ;
    E  mov         dword ptr [a],Ah  
        int b = ;
      mov         dword ptr [b],h  
        int ret = Add(a, b);
    C  mov         eax,dword ptr [b]  
    F  push        eax  
      mov         ecx,dword ptr [a]  
      push        ecx  
      call        _Add (Eh)  
      add         esp,  
           

在還沒有調用main函數之前,當_tmianCRTStartup在還沒有開始調用main函數之前, esp 和 edp 一起維護同一塊空間

深度剖析函數的調用過程(棧幀)

開始調用main函數

剛開始的三行代碼是為了main函數倆開辟棧幀空間的,其中 esp 存放指向函數棧幀棧頂的位址, ebp 存放指向函數棧幀棧底的位址。

81  push        ebp  
           

push 表示壓棧,從棧頂壓入。

深度剖析函數的調用過程(棧幀)
mov         ebp,esp  
           

mov 表示移動,表示的就是将 esp 移動到 ebp 的位置

深度剖析函數的調用過程(棧幀)
sub         esp,h  
           

sub 表示将esp減去 0E4h(0x000000e4),将 esp 上移,因為在計算機中上面為低位址,下面為高位址。

深度剖析函數的調用過程(棧幀)
819  push        ebx  
    81A  push        esi  
    81B  push        edi 
           

這三行 push 就是将 ebx 、 esi 、 edi 先後壓入棧頂,并且 esp 将會上移

深度剖析函數的調用過程(棧幀)
0106181C  lea         edi,[ebp-0E4h]  
    01061822  mov         ecx,39h  
    01061827  mov         eax,0CCCCCCCCh  
    0106182C  rep stos    dword ptr es:[edi]  
           

這四條語句就是,将 eax 的内容重複 ecx 次,重複是從 edi 所指向的位址開始。

深度剖析函數的調用過程(棧幀)
int a = ;
    E  mov         dword ptr [a],Ah  
           

表示将10(0Ah)放入a中,放在 ebp - 8 的位置

深度剖析函數的調用過程(棧幀)
int b = 20;
    01061835  mov         dword ptr [b],14h 
           

表示将20(14h)放入b中,放在 ebp - 20 的位置

深度剖析函數的調用過程(棧幀)
深度剖析函數的調用過程(棧幀)

下面開始調用Add函數

int ret = Add(a, b);
    C  mov         eax,dword ptr [b]  
    F  push        eax  
      mov         ecx,dword ptr [a]  
      push        ecx  
      call        _Add (Eh)  
      add         esp,  
    C  mov         dword ptr [ret],eax  
           
C  mov         eax,dword ptr [b]  
    F  push        eax  
      mov         ecx,dword ptr [a]  
      push        ecx   
           

上述四行代碼就是先将b壓棧,然後再将a壓棧,實際上a,b就是傳給Add函數的形參

深度剖析函數的調用過程(棧幀)
int Add(int x, int y)
    {
      push        ebp  
      mov         ebp,esp  
      sub         esp,CCh  
      push        ebx  
    A  push        esi  
    B  push        edi  
    C  lea         edi,[ebp-CCh]  
      mov         ecx,h  
      mov         eax,CCCCCCCCh  
    C  rep stos    dword ptr es:[edi]  
        int z = ;
    E  mov         dword ptr [z],  
        z = x + y;
      mov         eax,dword ptr [x]  
      add         eax,dword ptr [y]  
    B  mov         dword ptr [z],eax  
        return z;
    E  mov         eax,dword ptr [z]  
    }
      pop         edi  
      pop         esi  
      pop         ebx  
      mov         esp,ebp  
      pop         ebp  
      ret  
           

前面建立棧幀的過程和main函數一樣,其中第一步是先将main函數的 ebp 壓棧,目的是為了在傳回時能夠傳回到main函數的棧底。

深度剖析函數的調用過程(棧幀)
01061725  mov         eax,dword ptr [x]  
    01061728  add         eax,dword ptr [y]  
    0106172B  mov         dword ptr [z],eax  
            return z;
    0106172E  mov         eax,dword ptr [z]  
           

将兩數之和放入z中,并最後将z的值放在寄存器eax中

pop         edi  
      pop         esi  
      pop         ebx  
      mov         esp,ebp  
      pop         ebp  
      ret  
           

接下來将會執行 pop 操作,也就是出棧,按照順序依次将 edi 、 esi 、 ebx 出棧。然後将 ebp 指派給 esp , ebp 出棧。注: ret 指令會使得出棧一次,并将出棧的内容當做位址,将程式跳轉到該位址處。将Add棧幀銷毀。

接下來就會從call指令處繼續向下執行

add         esp,  
    C  mov         dword ptr [ret],eax
           

将 esp 向下移,将形參銷毀,把 eax放入ret( ebp-20h) 中。

下面就是對main棧幀的銷毀,和Add棧幀的銷毀一樣。

繼續閱讀