1.什么是函数栈帧
在C语言中,每一个正在运行的函数都有一个栈帧与其对应,栈帧中存储的是该函数的返回地址和局部变量。从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等。
2.函数栈帧的创建与销毁
在调用函数时,会为该函数在栈上开辟一块空间,在该函数运行的过程中,寄存器ebp保存的是栈底地址,寄存器esp保存的是栈顶地址。而且还应该明确一点,esp和ebp寄存器一次只能存储一个地址,所以,任何时候,这一对指针都会指向同一个函数的栈帧结构。并且ebp一般由系统改变它的值,而esp会随着数据的入栈和出栈而移动,也就是说esp始终指向栈顶。下面我们来看一个例子:
#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\n", c);
return 0;
}
下面的代码为main函数中对应的反汇编代码:
int main()
{
00B118B0 push ebp //ebp压栈
00B118B1 mov ebp,esp //将esp的值赋给ebp
00B118B3 sub esp,0E4h //将esp的值减去0E4h,给main函数栈帧分配空间
00B118B9 push ebx //压栈
00B118BA push esi //压栈
00B118BB push edi //压栈
00B118BC lea edi,[ebp-24h]
00B118BF mov ecx,9
00B118C4 mov eax,0CCCCCCCCh
00B118C9 rep stos dword ptr es:[edi] //从edi开始到向下的9个空间的值全部改为eax,即0CCCCCCCCh
00B118CB mov ecx,0B1C003h
00B118D0 call 00B1131B //进入main函数
int a = 10;
00B118D5 mov dword ptr [ebp-8],0Ah //ebp-8为a的位置,将a的值赋为10
int b = 20;
00B118DC mov dword ptr [ebp-14h],14h //ebp-14h为b的位置,将b的值赋为20
int c = 0;
00B118E3 mov dword ptr [ebp-20h],0 //同上
c = Add(a, b);
00B118EA mov eax,dword ptr [ebp-14h] //传参的过程,先将b的值传给eax
00B118ED push eax //再将eax压栈
00B118EE mov ecx,dword ptr [ebp-8] //传参,先将a的值传给ecx
00B118F1 push ecx //再将ecx压栈
00B118F2 call 00B110B4
00B118F7 add esp,8
00B118FA mov dword ptr [ebp-20h],eax
printf("%d\n", c);
00B118FD mov eax,dword ptr [ebp-20h]
00B11900 push eax
00B11901 push 0B17B30h
00B11906 call 00B110D2
00B1190B add esp,8
return 0;
00B1190E xor eax,eax
}
下面是Add函数的反汇编代码:
int Add(int x, int y)
{
00B11770 push ebp //记录上一个ebp的值
00B11771 mov ebp,esp //赋值
00B11773 sub esp,0CCh
00B11779 push ebx
00B1177A push esi
00B1177B push edi
00B1177C lea edi,[ebp-0Ch]
00B1177F mov ecx,3
00B11784 mov eax,0CCCCCCCCh
00B11789 rep stos dword ptr es:[edi] //与main函数一样,从edi开始向下的3个单位全部赋值为CCC
00B1178B mov ecx,0B1C003h
00B11790 call 00B1131B
int z = 0;
00B11795 mov dword ptr [ebp-8],0
z = x + y;
00B1179C mov eax,dword ptr [ebp+8]
00B1179F add eax,dword ptr [ebp+0Ch] //进行加法计算
00B117A2 mov dword ptr [ebp-8],eax
return z;
00B117A5 mov eax,dword ptr [ebp-8]
}
栈帧销毁过程如下:
00B117A8 pop edi //出栈
00B117A9 pop esi //出栈
00B117AA pop ebx //出栈
00B117AB add esp,0CCh //销毁Add函数
00B117B1 cmp ebp,esp
00B117B3 call 00B11244 //回到main函数
00B117B8 mov esp,ebp
00B117BA pop ebp //出栈
00B117BB ret
说实话,本人表达能力有限,再加上涉及的底层内容太多,难度较大,难免会有出错和不妥的地方,欢迎大家指正~~