函数栈帧的创建和销毁在所有编译器中都是大同小异的,不同的编译器会有不同的方式,但是了解到了简单的底层的这些方法后,其他的编译器都是在此基础上修饰,不必深究。
1、寄存器 ebp,esp 这两个寄存器中存放的是地址,用来维护函数栈帧 2、编译器的选择 最好使用visual 6.0来观察,它更加简洁,我们用到的是vs2013,因为越早的编译器观察到的过程越不复杂。
我们来给到一个简单的加法函数
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z=x+y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c =Add(a, b);
printf("%d",c);
return 0;
}
最粗略的整体的逻辑
我们知道每一个函数调用都要在栈区创建一块空间,一般是由高地址向低地址使用,main函数的使用也要开辟栈帧
esp存入函数低位置的地址,叫做栈顶指针,ebp存入函数高位置的地址,叫做栈底指针。 我们用调试的方法来观察过程。
main函数也是被调用的,被 __tmainCRTStartup() 这个函数调用,而这个函数又被下面的 mainCRTStartup() 调用,这样也就是能够解释为什么我们的main方法要return 0 了,它返回到了调用它的函数 __tmainCRTStartup()里面
当然在一开始的时候我们也会为这两个函数创建空间,在main函数之前
调用Add函数时再创建空间
汇编语言的指令 打开反汇编,我们可以看到汇编语言对程序的操作,这里push叫压栈,push ebp就是将一个叫做ebp的量压到栈顶上边(这里涉及到监视窗口可以监视到ebp确实是地址小于的正好在 __tmainCRTStartup() 函数的上方,有兴趣的大家可以打开监视窗口查看一下,这里我们为了缩短篇幅只讲结果)(与push相对的叫做pop,出栈,从栈顶删除一个元素)
在我们创建 __tmainCRTStartup() 这个函数时,接着push ebp 如下两图所示 (因为esp维护的是栈顶,所以push之后esp要跟着变化)
然后mov ebp,esp 是把esp的值给ebp,此时两个值相等,同时指向上图esp所指的地方。
第三条指令sub是减法,就是让esp-0E4h,改变栈顶的地址,esp指向了上边的某一区域(这里不会越出界限)
紫色即main函数的栈帧
然后把ebx,esi,edi 三个push上去
lea 即 load effective address 加载有效地址 第四条指令即将后边的ebp+FFFFFF1Ch加载到edi里边去,即ebp-0E4h 然后将39h和0CCCCCCCCh这个数字分别给到ecx和eax。(这里不需要管这两个数字是什么)
word是两个两个字节,dword 就是 double word 四个字节 然后将这从edi开始向下39h个数字全部变成0CCCCCCCCh,即下图区域
以上就是为了正式的代码做铺垫
然后就是赋值a赋值b 然后进入到Add函数中
传参过程 然后mov push 给到eax和ecx
call是调用函数,它会压栈一个00C21450,这是call指令的下一条指令,以便call返回时继续使用
这里的汇编语言指令在前面都说到过,我们跳过继续说
注意这里先传b再传a,传参的顺序是从右往左的,在汇编指令中我们可以很明显的发现,传参的方式,就是调用实参出来给到形参,而不是形参的单独创建,这有利于我们了解形参和实参的关系。 形参是实参的一份临时拷贝(中肯的一针见血的) 这里我们会有一个疑问,为什么return z 之后这个函数不是销毁了吗?那值不是也会随之销毁吗? 其实这里看到return z 后面的这个汇编指令,它把z的值(z的地址就是ebp-8)赋值给了eax,销毁之后再把eax的值传出去 三个pop将他们逐出去了
它们就被回收了 然后把ebp赋值给ebp,此时两个指针指向相同的地方,上面的函数所占的空间被系统回收了,即Add函数被回收了
此时再pop最上面的ebp,这里面存放的是最开始main函数的栈底指针,这样我们就能很容易的找到main函数栈底的位置,此时ebp指针回到这个位置,然后esp指向00C21450的这个位置,并且此时两指针之间的这一区域就是main函数所占的区域。 然后ret返回到call指令的下一条指令,即00C21450,然后将此地址也弹出,指针指向下一位(标黄)
然后下一步将形参x,y所占的空间释放esp+8,往下走,,然后就把eax的值给到ebp-20h了,也就是z的值给了c:z在销毁前把值传给eax,eax在00C21453这一步时将值传给ebp-20h,在这个位置的值就是c。
到现在,我把函数栈帧的创建和销毁的过程大致梳理了一遍,我在学完之后有一种恍然大悟的感觉,希望这篇能够帮到大家。