向下生長指的是從內存的高地址-->低地址的方向拓展。ios
棧有棧底和棧頂,從上面能夠知道棧頂的地址是比棧底的要低的。函數
對於X86體系的CPU而言,大概須要知道如下基礎知識:指針
若函數A調用函數B,那麼A函數通常叫作調用者,B函數通常爲被調用者,函數調用過程能夠作以下描述code
示意圖以下所示
blog
一個簡單的函數調用例子內存
#include <iostream> int __cdecl Add(int a, int b) { return a + b; } int main() { auto res = Add(2, 3); std::cout << "2 + 3 = " << res << std::endl; std::cout << "Hello World!\n"; }
在main函數調用Add函數以前,main函數的棧幀狀況以下所示
io
當main函數調用Add函數的時候,彙編以下stream
auto res = Add(2, 3); 00E12618 push 3 00E1261A push 2 00E1261C call Add (0E111D6h) 00E12621 add esp,8 00E12624 mov dword ptr [res],eax
push parameter_n push parameter_... push parameter_1 call funcName; 調用函數funcName, 加你個返回地址填入棧,而且跳轉到funcName
main函數調用Add函數的棧示意圖以下:
基礎
當call Add (0E111D6h) 進入Add函數以後,彙編語言以下所示bug
int __cdecl Add(int a, int b) { 00E12300 push ebp 00E12301 mov ebp,esp 00E12303 sub esp,0C0h 00E12309 push ebx 00E1230A push esi 00E1230B push edi 00E1230C lea edi,[ebp-0C0h] 00E12312 mov ecx,30h 00E12317 mov eax,0CCCCCCCCh 00E1231C rep stos dword ptr es:[edi] 00E1231E mov ecx,offset _44E0C52E_AnalyseFunc@cpp (0E1F026h) 00E12323 call @__CheckForDebuggerJustMyCode@4 (0E11280h) return a + b; 00E12328 mov eax,dword ptr [a] 00E1232B add eax,dword ptr [b] } 00E1232E pop edi 00E1232F pop esi 00E12330 pop ebx 00E12331 add esp,0C0h 00E12337 cmp ebp,esp 00E12339 call __RTC_CheckEsp (0E1128Ah) 00E1233E mov esp,ebp 00E12340 pop ebp 00E12341 ret
在Add函數的彙編語言中能夠看到開始的前3句,這裏作以下解釋
00E12300 push ebp; 進入新的函數,新函數也須要一個棧幀了,就必須將main函數的棧幀底部所有保存起來,棧頂則是做爲一個新函數的棧底 00E12301 mov ebp,esp;上一個棧幀頂部就是這個棧幀的底部 00E12303 sub esp,0C0h;爲當前棧幀開闢相應的空間
當Add函數執行完以後,將執行ret 指令返回,而且esp指向Add函數棧幀底部(就是main 函數棧幀頂部), 緊接着就是從彈出保存的ebp恢復現場,這樣就回到了調用Add函數以前的狀態。