文章目录
函数栈帧的创建与销毁
- 前置知识
要想理解函数栈帧的创建与销毁的实现过程,我们要对一些寄存器和汇编指令有所了解
如下:
那么接下了,我们将通过简单的代码来理解函数栈帧的形成
代码如下:
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\n", c);
return 0;
}
下面我将详细讲解Add函数的栈帧的创建与销毁过程。
- 变量a,b,c的创建
mov(001718F5)指令是开辟四个字节大小的空间并将0Ah(十六进制)(也就是10)放入ebp-8(地址为0x00F8F74C)处
mov(001718FC)指令是开辟四个字节大小的空间并将14h(十六进制)(也就是20)放入ebp-14h(地址为0x00F8F740)处
mov(00171903)指令是开辟四个字节大小的空间并将0(十六进制)(也就是0)放入ebp-20h(地址为0x00F8F734)处
如下:
- Add函数参数的形成
mov(0017190A)指令是将ebp-14h(变量b)的值赋给eax,push(0017190D)是将eax的值压入栈顶,esp寄存器大小-4。
mov(0017190E)指令是将ebp-8(变量a)的值赋给ecx,push(00171911)是将ecx的值压入栈顶,esp寄存器大小-4。
如下:
也就是说,函数参数的实例化是从左向右依次进行的。
-
Add函数的调用
call(00171912)指令是压入返回地址并跳转入目标函数,也就是先转入001710B9,再转入00171790,压入返回地址(00171917)。
如下:
-
Add函数栈帧的创建
push(00171790)指令压入ebp的值,保存ebp的返回地址。
mov(00171791)指令将esp的值赋给ebp。
sub(00171793)指令将esp-0CCh。
push(00171799),push(0017179A),push(0017179B)就是分别压入ebx,esi,edi。
如下:
lea(0017179C)是加载有效地址,将ebp-0Ch的值放入edi中,mov(0017179F)将3放入ecx中,mov(001717A4)将0cccccccch的值放入eax中,rep stos(001717A9)从ebp到edi(ebp-0Ch),中的内容改为eax,循环ecx次。
如下:
至此,我们就理解了函数栈帧的创建。
-
Add函数内部语句的实现
mov(001717B5)开辟4个字节大小的空间并将0赋给ebp-8(变量z)处。
add(001717BC)将ebp+8处(形参x)的值赋给eax,add(001717BF)让eax的值加上ebp+0Ch处(形参y)的值。
mov(001717C2)将eax的值放入ebp-8处(变量z)。
如下:
假如我们只是定义了变量z,而没有进行初始化,那么其z的值就是cc cc cc cc也就是随机值。也就是未初始化的局部变量的值是随机值。 -
Add函数的返回值
mov(001717C5)将ebp-8处(变量z)的值赋给eax,eax的值并不会随着函数的销毁而改变。 -
Add函数的销毁
pop(001717C8),pop(001717C9),pop(001717CA)连续弹出三次,并将值依次赋给edi,esi,edx。
如下:
mov(001717D8)将ebp的值赋给esp。Add的函数栈帧也就销毁了。
如下:
pop(001717DA)弹出此时esp的值,并赋给ebp。也就是将ebp在main函数的地址赋给ebp。
如下:
ret(001717DB)回复返回地址。返回到add(00171917)处,也就是call(00171712)指令压入的地址。
add(00171917)将esp的值加8。
mov(0017191A)就是将eax的值(0x0000001e)放入ebp-20h处(变量c)。
如下:
至此Add函数栈帧的创建与销毁就全部完成。
总结
以上就是我对于函数栈帧的创建与销毁的理解。如果有那里不对,请大佬们指出。感谢观看!!!