向下生长指的是从内存的高地址-->低地址的方向拓展。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函数以前的状态。