必會2 函數的調用堆棧詳細過程

函數的調用堆棧詳細過程

示例代碼

#include<iostream>
using namespace std;

int sum(int a, int b)
{
    return a+b;
}

int main()
{
	int a = 10;
	int b = 20;
	
	int ret = sum(a,b);
	cout << "sum=" << sum <<endl;
	
	return 0;
}

由代碼引申出來的問題

  1. main函數調用sum,sum執行完,怎麼知道回到哪個函數中?
  2. sum函數執行完,回到main以後,怎麼知道從哪一行指令繼續運行?

代碼反彙編

     1: /*
     2:  * 看反彙編 vs2013中
     3: */
     4: #include<iostream>
     5: using namespace std;
     6: 
     7: int sum(int a, int b)
     8: {
00F42E60  push        ebp  
00F42E61  mov         ebp,esp  
00F42E63  sub         esp,0CCh  
00F42E69  push        ebx  
00F42E6A  push        esi  
00F42E6B  push        edi  
00F42E6C  lea         edi,[ebp+FFFFFF34h]  
00F42E72  mov         ecx,33h  
00F42E77  mov         eax,0CCCCCCCCh  
00F42E7C  rep stos    dword ptr es:[edi]  
     9: 	int temp = 0;
00F42E7E  mov         dword ptr [ebp-8],0  
    10: 	temp = a + b;
00F42E85  mov         eax,dword ptr [ebp+8]  
00F42E88  add         eax,dword ptr [ebp+0Ch]  
00F42E8B  mov         dword ptr [ebp-8],eax  
    11: 	return temp;
00F42E8E  mov         eax,dword ptr [ebp-8]  
    12: }
00F42E91  pop         edi  
00F42E92  pop         esi  
00F42E93  pop         ebx  
00F42E94  mov         esp,ebp  
00F42E96  pop         ebp  
00F42E97  ret  

    13: 
    14: int main()
    15: {
00F440F0  push        ebp  
00F440F1  mov         ebp,esp  
00F440F3  sub         esp,0E4h                //開闢棧空間
00F440F9  push        ebx  
00F440FA  push        esi  
00F440FB  push        edi  
00F440FC  lea         edi,[ebp+FFFFFF1Ch]  
00F44102  mov         ecx,39h  
00F44107  mov         eax,0CCCCCCCCh  
00F4410C  rep stos    dword ptr es:[edi]  
    16: 	int a = 10;
00F4410E  mov         dword ptr [ebp-8],0Ah  
    17: 	int b = 20;
00F44115  mov         dword ptr [ebp-14h],14h  
    18: 
    19: 	int ret = sum(a, b);
00F4411C  mov         eax,dword ptr [ebp-14h]  //壓參數b
00F4411F  push        eax  
00F44120  mov         ecx,dword ptr [ebp-8]    //壓參數a
00F44123  push        ecx  
00F44124  call        00F414B5  
00F44129  add         esp,8                   //回退形參棧
00F4412C  mov         dword ptr [ebp-20h],eax //將子函數返回值給ret
    20: 	cout << "sum=" << sum << endl;
00F4412F  mov         esi,esp  
00F44131  push        0F414D3h  
00F44136  mov         edi,esp  
00F44138  push        0F414B5h  
00F4413D  push        0F4CC70h  
00F44142  mov         eax,dword ptr ds:[00F500A0h]  
00F44147  push        eax  
00F44148  call        00F414C9  
00F4414D  add         esp,8  
00F44150  mov         ecx,eax  
00F44152  call        dword ptr ds:[00F50094h]  
00F44158  cmp         edi,esp  
00F4415A  call        00F41334  
00F4415F  mov         ecx,eax  
00F44161  call        dword ptr ds:[00F50090h]  
00F44167  cmp         esi,esp  
00F44169  call        00F41334  
    21: 
    22: 	return 0;
00F4416E  xor         eax,eax  
    23: }
00F44170  pop         edi  
00F44171  pop         esi  
00F44172  pop         ebx  
00F44173  add         esp,0E4h  
00F44179  cmp         ebp,esp  
00F4417B  call        00F41334  
00F44180  mov         esp,ebp  
00F44182  pop         ebp  
00F44183  ret  

函數調用時詳細過程說明

比對着反彙編代碼看

esp 棧頂 低地址

ebp 棧底 高地址

每次 push pop 時esp ebp會隨之調整

函數調用時

先壓參數 從右向左壓參數

再將本函數內下一行指令地址壓棧(問題2)

進入調用函數後

push ebp 將父函數的棧底地址入棧(問題1)

mov ebp, esp 將棧底寄存器ebp指向現在的棧頂esp

子函數返回值

將返回值保存在寄存器中,由寄存器帶出。

  • 返回值爲四字節,則一個寄存器 eax 可以帶出

  • 八字節則兩個寄存器 eax,edx

  • 超過八個字節用臨時變量帶出

準備返回

mov esp, ebp 讓子函數棧頂寄存器esp指向子函數棧底

pop ebp 出棧,並將棧頂內容給ebp

ret 出棧操作,將棧頂給cpu的pc寄存器,也就是將父函數的下一行指令地址給pc寄存器

返回到父函數後

add esp, 8 就是將兩個形參釋放

mov dword ptr[ebp-0Ch], eax 將子函數返回值給變量ret

問題解答

你會了嘛 : )

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章