栈帧步骤解读

各寄存器,指令:
ebp:栈底指针
esp:栈顶指针
eip:程序计数器,为cpu指向下一条应该执行的语句
move:后值赋给前值
push:入栈
pop+ ? :出栈,并将出栈内容赋给后面的寄存器
call:(1)将当前call命令的下一条指令的地址以入栈的方式保存起来,用作恢复
(2)以jmp方式跳转
ret:将当前元素进行出栈处理,并将值赋给eip

下面以一段代码为例:

#include
int fun(int a,int b)
{
   int c=a+b;
   return c;
}
int main()
{  int a=10;
   int b=20;
   int c=fun(a,b);
   printf("hehe\n");
   return 0;
}

1.对ebp进行入栈操作,令esp先等于ebp,再另esp减16进制数字0e4h,在esp与ebp之间形成了一段数据空间,此段空间为main函数的栈帧空间。
栈帧步骤解读_第1张图片
2.依次初始化元素a,b,并存储在ebp-4,ebp-8处(先定义的元素存在高地址,后定义的元素存在低地址)。
栈帧步骤解读_第2张图片
3.接下来就到了调用函数fun这一步了,在调用函数前,先要定义形参(形参实体化),定义的形参以入栈的形式存在main函数栈帧的下方。
(1);定义形参时,总是从右向左定义,即先b后a
(2);每进行一次入栈操作,栈顶指针均下移,根据不同的数据类型下移不同的字节大小,此处均为4字节

栈帧步骤解读_第3张图片

4.为了保证函数调用结束以后cpu仍然能执行main函数的下一步语句,此处故使用call指令
call:(1)将当前call命令的下一条指令的地址以入栈的方式保存起来,用作恢复(2)以jmp方式跳转。
此处call存储了printf(“hehe\n”);语句的地址。

5.上述步骤均做完以后,就可以正式的进入函数调用了,首先与main函数一样,只要函数被调用,就要为他分配一段空间作为栈帧空间:
(1)先对ebp做入栈操作,ebp是指向main函数栈底的指针,此处入栈就是为了保存main函数栈底的位置,为ebp后期返回原处做好标记
栈帧步骤解读_第4张图片
(2)令ebp指向esp所指向的位置
(3)esp减OCCh。(add----加 sub----减 mul----乘 div----除)
此时fun函数的栈帧空间也就分配完毕了。
栈帧步骤解读_第5张图片
6.令a的值存入寄存器,在用add指令将a与b加起来,仍然存在寄存器中

7.将a+b的值存在ebp+4处,此句即int c=a+b;

8将c存入寄存器,用做返回值。
栈帧步骤解读_第6张图片
9.进行到这一步,fun函数的调用就算结束了。接下来要释放fun函数占用的栈帧空间:令esp指向ebp所指向的地址。
栈帧步骤解读_第7张图片
10.做出栈操作,并将出栈元素存储在ebp中(刚好,栈顶元素为最初main函数栈底的地址,此处出栈,令ebp还原的最初的状态----指向main函数的栈底)

栈帧步骤解读_第8张图片

11.最后执行ret操作,刚好栈顶元素是在调用fun函数前存的main函数中的下一条地址。所以cpu会重新返回到main函数执行调用fun的下一条语句,esp+8是栈顶指针也还原到调用函数之前的位置。
栈帧步骤解读_第9张图片

栈帧步骤解读_第10张图片

你可能感兴趣的:(C语言)