在了解函数栈帧的创建和销毁前,先了解一下几个基本概念。
寄存器:是一种储存数据的一个存储部件,集成在cpu上,高效但数量少。在此篇文章中用到的寄存器有,通用寄存器:exa,ebx,ecx,edx。以及栈顶和栈底寄存器:esp,ebp。
函数栈帧: 每一次函数调用都是一个过程这个过程我们称为函数的调用过程,这个过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存,现场保护。
本文将用一个简单的c语言程序进行函数栈帧的分析(上图),并且在调试过程中反编译代码成汇编语言。注意:此次函数栈帧的实现是在vs2013的编译器上,不同编译器实现的效果可能略有不同。且在调试中发现,main函数也是由其他函数(_tmainCRTStartup)所调用的,在此从main函数开始分析。
在栈中,存储的数据是由高地址向低地址存储的,ebp寄存器和esp寄存器将一块空间规划给main函数,并将空间中的数据改为cccccccc(也就是在数组越界登情况下很容易出现的烫烫烫烫的ascii码值),此后通用寄存器会将变量a,b,c的值写入此区域中(具体存储位置视编译器而定)。在调用Add函数时,esp和ebp将会向低地址处开辟一块空间给Add函数所用并将一份变量a,b的临时拷贝复制在上面,在进行完运算后将会将Add函数的空间返回给操作系统,esp,ebp寄存器回到main函数进行维护。以下是对于函数栈帧的分析图,对于汇编语言指令的解释会在文章末尾展示。
对于汇编指令的描述将采用结合具体汇编语句来描述:
push ebp:将_tmainCRTStartup的函数ebp压栈,在栈顶开辟一块空间放入它的ebp。
mov ebp,esp:将esp赋给ebp,esp和ebp同时指向新开辟空间的顶端。
sub ebp,0E4h:栈空间中元素存放顺序是由高地址到低地址,则该步骤在ebp的上面开辟了0e4h大小的内存空间.
lea edi,[ebp-0E4h]:将ebp-0e4h这个地址放到edi中
mov dword ptr [ebp-8],14h:给a赋值,在esp-4处赋上10.
调用call指令:call指令(1)对当前正在执行指令的下一条指令的地址进行保存。
对于栈帧的创建需要一定的汇编指令基础,对于不是太懂的同学可以参考一篇文章https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html