【c语言】函数栈帧
1.函数栈帧的基本概念
在调用函数时,为函数开辟栈空间,用于本次函数调用中临时变量的保存与现场保护等等。栈是由高地址向低地址延伸的。寄存器ebp指向当前的栈帧的地步(高地址),寄存器esp指向当前栈帧的顶部(低地址)。
2.实例分析
- 代码
#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 ret = Add(a, b);
printf("ret=%d", ret);
return 0;
}
调试模式下使用调用堆栈功能,我们可以发现main函数在调用之前是在函数—_mainCRTStartup中调用,而_mainCRTStartup又是在mainCRTStartup函数中调用。
-
main函数的栈帧
- 解析函数栈帧
1.main函数:
- 首先是_mainCRTStartup()函数的调用,然后在调用main函数;
- 将ebp做压栈处理,保存指向栈底的地址(方便函数返回之后的现场恢复)
- 将esp的值赋给ebp,产生新的ebp;
- 给esp减去一个16进制数0E4H(为main函数预开辟空间)
- 将ebx,esi,edi做压栈处理,对应蓝色部分
- lea指令,加载有效地址,初始化预开辟的空间0xcccccccc,对应橙色部分
- 变量a与变量b的创建
Add函数的调用过程:
- 将实参b存入寄存器eax,再将eax做压栈处理;(传参过程,从右向左传递,先传b)
- 将a存入寄存器ecx,再将ecx压栈;
- call指令的调用,先将call指令下一条指令的地址压栈,然后跳转(jmp)到Add()函数的地方。
2.进入Add函数
- 首先将main()函数ebp压栈处理,保存指向main()函数栈帧底部的地址(方便函数返回之后的现场恢复),此时esp指向新的栈顶位置
- 将esp的值赋给ebp,产生新的ebp,即Add()函数栈帧的ebp;
- 给esp减去一个16进制数0E4H(为Add()函数预开辟空间)
- 将ebx,esi,edi做压栈处理
- lea指令,加载有效地址
- 初始化预开辟的空间为0xcccccccc
- 变量z的创建
- 获取形参的a和b再相加,将结果存储在z中;
- 将结果存储到eax寄存器,通过寄存器带回函数的返回值
3.Add函数返回
- edi,esi,ebx依次出栈,esp向下移动;
- 将ebx赋给esp,使esp指向edp指向的地方
- ebp出栈,将出栈的内容给ebp(即main-ebp),回到main(函数的栈帧)
- ret指令,出栈依次,并将出栈的内容当做地址,并跳转到该地址处(call指令下啊一条执行)