栈帧与函数调用过程

来源:互联网 发布:mac外壳磕了能保修吗 编辑:程序博客网 时间:2024/06/16 03:57

程序的地址空间


程序地址空间

程序的地址空间图中,从下向上,地址不断增长,从0x00000000到0xFFFFFFFF。
code:代码区,存储代码
字符、串常量区:存储不能被修改的常量
Init,uninit:全局区,分别为初始化全局区,和未初始化全局区,存储全局变量
heap:堆,使用malloc在堆申请空间,在堆上创建的变量使用完毕后要手动释放空间
栈:在离堆较远处的一块空间叫做栈,用于存放临时变量,函数调用时形成栈帧,函数调用完成时,销毁栈帧

注:堆和栈相对而生,即栈底在较高地址处,在不断的使用对空间,创建临时变量时,栈顶向低地址处移动,堆则相反

栈帧


栈帧:在函数调用的过程中,要为函数开辟空间,用于本次函数调用中临时变量的保存、现场保护,这块栈空间称为栈帧。

栈帧的维护离不开两个寄存器,ebp,esp,当main函数开始执行时,形成main函数的栈帧,esp指向栈帧的栈顶,ebp指向栈帧的栈底,在这个过程中ebp和esp相当于指针

寄存器eip(pc指针),存当前正在执行指令的下一条指令的地址

在c语言中,main函数被_tmainCRTSartup函数调用,当有如下代码时:

#include<stdio.h>int main(){    int a = 1;    int b = 2;    return 0;}

当main函数调用时,要开辟栈空间,画出栈帧图:

这里写图片描述

为了清楚观察这一过程,用vc6.0转到汇编程序,可以清楚地看出,当开辟栈帧完成时,经过小面两行汇编代码,用ebp完成a,b的创建并赋值,ebp-4和ebp-8的原因是整形变量需要四个字节的空间,

这里写图片描述

如果在创建完成a,b之后,要进行Add(a,b),完成a和b相加,并返回结果,代码如下:

#include<stdio.h>int Add(int a,int b){    int z = 0;    z = a + b;    return z;}int main(){    int a = 1;    int b = 2;    int c = Add(a,b);    printf("Add(a,b) = %d\n",c);    return 0;}

这里写图片描述

当开始调函数的时候,将a,b再次创建一份放在main函数的栈帧外面,esp不断移动,注意,先创建的b,即在Add的参数里,从右向左依次实例化,图如下:

这里写图片描述

然后执行call指令,call指令个功能:
1、将当前指令的下一条指令的地址压入栈中
(这里将下一条指令的地址压入栈内)
2、随即跳入Add函数(jmp)

这里写图片描述

进来之后
将main的ebp存的值压入栈内

ebp指向esp的位置
esp-44h
经过前两条指令就形成了Add函数的栈帧
Add创建z的方式和在main函数中创建a,b的方式一致
栈帧图如下:

这里写图片描述

接下来

这里写图片描述

然后通过ebp找到 形参a,b将a+b赋值给z
将z中的值放在eax通用寄存器中
此时,Add函数已经执行完成

不断进行pop指令,edp不断向上移,即所指向地址不断增加
然后将esp指向ebp所指向的位置
然后在进行pop指令将栈顶元素弹人ebp,因为这是栈顶的值是原来main函数ebp的值,所以ebp再次指向main函数的栈帧的底部,如图:

这里写图片描述

最后进行ret指令,返回call的下一条指令除

call的功能:
1、弹出栈顶
2、弹出的值放入eip中

这里写图片描述

给esp+8,esp再次指向main函数的栈帧顶部,main函数的栈帧恢复完成
再把eax中的值(z即a+b)放在c内

函数调用完成,返回值收,打印

注意:在main函数和Add函数的栈帧之间,存在一小块区域,这小块区域存放形参a,b和一些地址。