您的位置:首页 > 其它

函数的调用过程(栈帧的创建和销毁)

2017-05-22 17:46 423 查看
  为了更好地认识函数的调用过程,我们可以用反汇编代码去理解学习。

一、基本概念

1.栈帧(过程活动记录):是编译器用来实现函数调用的一种数据结构,每个栈帧对应一个未运行完的函数,栈帧中保存了该函数的返回地址和局部变量。

2.栈(stack):又名堆栈,是一种运算受限的线性表,只允许在表的一端输入输出,这一端叫栈顶,相对的另一端是栈底,遵循后进先出,栈底是高地址,栈顶是低地址,栈由内存高地址向低地址生长。

3.push: 压栈    pop:出栈

4.寄存器: 位于CPU内部,存放运行程序中的数据和指令。

ebp (栈帧的栈底指针)     esp(栈帧的栈顶指针)

二、调用add函数的代码

#include <stdio.h>
int add(int a, int b)
{
int ret = 0;
ret = a + b;
return ret;
}
int main()
{
int a = 10;
int b = 20;
int ret = add(a, b);
return 0;
}
三、截取的部分反汇编代码,进行逐语句分析

int main()
{
00B81410 push ebp //在此之前,main函数在mainCRTstartup中调用,先为mainCRTsturtup开辟栈帧,有栈底,有栈顶。现在开始压栈。
00B81411 mov ebp,esp //让ebp指向esp的位置。
00B81413 sub esp,0E4h //esp指向[esp-oE4h]的位置,sub为减
00B81419 push ebx //分别将ebx、esi、edi压栈
00B8141A push esi
00B8141B push edi

00B8141C lea edi,[ebp-0E4h] //加载有效地址,让edi存放[ebp-0E4h]的位置,1C至2C表示对空间进行初始化,从edi所指向的位置开始向高地址进行拷贝,初始内容为0CCCCCCCCh,拷贝39次
00B81422 mov ecx,39h
00B81427 mov eax,0CCCCCCCCh
00B8142C rep stos dword ptr es:[edi]

int a = 10;
00B8142E mov dword ptr [a],0Ah //将10赋值于给a变量分配的空间
int b = 20;
00B81435 mov dword ptr [b],14h //将20赋值于给b变量分配的空间
int ret=add(a, b);
00B8143C mov eax,dword ptr [b] //进行传参,让寄存器eax存放b的值
00B8143F push eax //将eax压栈
00B81440 mov ecx,dword ptr [a] //进行传参,让寄存器ecx存放a的值
00B81443 push ecx //将ecx压栈
00B81444 call _add (0B810E6h) //call指令下一条地址,为add函数的调用及返回做准备

——————————————————————————————————————

00B810DC jmp 00B83BBE
__ValidateImageBase:
00B810E1 jmp 00B82910
_add:
00B810E6 jmp 00B813C0 //即进入add函数
_GetProcessHeap@0:
00B810EB jmp 00B83C2A
__RTC_SetErrorFuncW:

——————————————————————————————————————
int add(int a, int b)
4: {
00B813C0 push ebp //ebp压栈,是main函数的ebp
00B813C1 mov ebp,esp //让ebp指向esp的位置。
00B813C3 sub esp,0CCh //esp指向[esp-0CCh]的位置,sub为减
00B813C9 push ebx //分别将ebx、esi、edi压栈
00B813CA push esi
00B813CB push edi

00DC13CC lea edi,[ebp-0CCh] //加载有效地址,让edi存放[ebp-0CCh]的位置,CC至DC表示对空间进行初始化,从edi所指向的位置开始向高地址进行拷贝,初始内容为0CCCCCCCCh,拷贝33次
00DC13D2 mov ecx,33h
00DC13D7 mov eax,0CCCCCCCCh
00DC13DC rep stos dword ptr es:[edi]
5: int ret = 0;
00DC13DE mov dword ptr [ebp-8],0 //将0放入[ebp-8]处
6: ret = a + b;
00DC13E5 mov eax,dword ptr [ebp+8] //将[ebp+8]处的值10放入eax
00DC13E8 add eax,dword ptr [ebp+0Ch] //将[ebp+0Ch]处的值20放入eax并相加,得eax中的值为30.
00DC13EB mov dword ptr [ebp-8],eax //将eax的内容拷贝在[ebp-8]中,即ret变量
7: return ret;
00DC13EE mov eax,dword ptr [ebp-8] //将ret的值存入eax中
8: }
00DC13F1 pop edi //edi、esi、ebx、出栈,栈帧逐渐开始销毁
00DC13F2 pop esi
00DC13F3 pop ebx
00DC13F4 mov esp,ebp //让esp指向ebp位置
00DC13F6 pop ebp //弹出ebp,
00DC13F7 ret //使其指向调用者调用函数之后的下一条指令的地址

00B81444 call 00B810E6 //下一条指令地址出栈
00B81449 add esp,8 //将之前放入栈的两个实参从栈中移除,esp下移
00B8144C mov dword ptr [ebp-20h],eax //将eax中的值30放入[ebp-20h]中,函数调用也到此结束
14: return 0;

四、函数调用图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: