函數的調用堆棧詳細過程
示例代碼
#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;
}
由代碼引申出來的問題
- main函數調用sum,sum執行完,怎麼知道回到哪個函數中?
- 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
問題解答
你會了嘛 : )