函数栈帧的创建与销毁(初级)
这里写目录标题
引言
如果你想知道:
1.局部变量是怎么创建的?
2.为什么是局部变量的值是随机值?
3.函数是怎么传参的?传参的顺序是怎样的?
4.形参和实参是什么关系?
5.函数调用是怎么做的?
6.函数调用结束怎么返回的?
我希望我写的东西对你有帮助。
进入正题
我是使用的环境是VS2013,不要使用太高级的编译器,越高级的编译器,越不容易学习和观察。同时在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,具体细节取决于编译器的实现。
什么是函数栈帧:
栈帧又叫过程活动记录。C语言中,栈帧就是一个函数执行的环境。
函数的每次调用,都有它自己独立的栈帧。栈帧中维持着函数调用所需要的各种信息,包括函数的参数,局部变量,函数执行完成后下一步要执行的指令地址、寄存器信息等。
常见的寄存器:
eax、ebx、ecx、edx、esp、ebp
其中 esp、ebp这两个寄存器中存放的是地址,用来维护函数栈帧,每个函数调用都需要再栈区创建一个空间。
演示函数栈帧的创建与销毁
演示代码:
#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函数将会调用Add()函数,但main函数又是被谁调用的呢?
由图可知main函数是由 mainCRTSartup(),再由_tmainCRTStartup()调用。
开始表演
这两张分别是 main()函数,和Add()函数的反汇编。
1.main()函数栈帧的开辟:
解释:将ebp的值压入栈,同时esp移动一个单位(因为esp存放的地址,所以在32为平台下移动4个字节)
这里是将ebp的值放入栈区上,通常叫做压栈。栈区上数据通常从高地址—>低地址存放。
解释:将esp的值赋给ebp
解释:将esp的值减去0E4h(16进制数,转为10进制就是228)。
解释:压入三个值:
解释:lea:load effective address(加载有效地址),就是将edi的值变为[ebp-0E4h]
将39h的值赋给ecx
将0CCCCCCCCh值赋给eax
dword是4个字节的意思,这条指令的意思是将从edi所指向空间向下,内存赋值0CCCCCCCCh,重复39h次,每次赋值4字节空间。
解释:以第一个为例,将0Ah(10)赋给地址为ebp-8的四字节空间。
2.调用Add()函数前准备:
解释:做传参准备。比如:将ebp-14h地址放的值(b:20)赋给eax,将eax的值压入栈。将ebp-8地址放的值(a:10)赋给ecx,并将ecx的值压入栈中。不要忘记push压栈时,esp所指向空间地址会减去一个单位(32字节下是4字节)而pop是出栈时,会加上一个单位。
将下一条指令IP00F51450压入栈中,并进入Add()函数。
3.Add()函数开辟栈帧
这里同上面为main()函数一样,都是为了开辟栈帧。
解释:将地址ebp-8空间的值赋为0.
解释:将ebp+8地址所指向空间的值赋给eax,再将eax的值加上ebp+0Ch所指向空间的值。最后将eax的值赋给ebp-8所指向空间。
解释:将ebp-8地址所指空间的值赋给30,也就是将返回值z交到一个寄存器手上,由它来返回。
4.Add()函数栈帧的销毁:
这是整个销毁的反汇编。
解释:将esp所指地址的值赋给edi,同时esp加上1个单位(4字节)。
edi就弹出去了。
同理,esi,ebx也会弹出去,也叫出栈。看下图
解释:将ebp的值赋给esp
将esp所指向空间的值交给ebp
解释:执行完这条命令,就会自动返回刚才call指令的下一行 00F51450。
点到为止,今天就说到这。
如果有什么不对的地方,还劳烦指教,谢谢大家。