一 函数栈帧的创建及销毁
1 什么是函数栈帧?
1 什么是函数?
函数就是对于一个独立功能的抽象。
2 什么是函数栈帧?
函数栈帧是函数调用时在栈区中临时开辟的一块内存空间
3 这些空间的作用是什么?
存放函数参数和函数返回值
存放临时变量
保存上下文信息(寄存器)
2 理解函数栈帧能够解决什么问题呢?
1 局部变量是如何创建的?
2 为什么局部变量不初始化内容是随机的?
3 函数调用时参数时如何传递的?传参的顺序是怎样的?
4 函数的形参和实参分别是怎样实例化的?
5 函数的返回值是如何带会的?
这五个问题都会在文章的最后意义解答
3 函数栈帧的创建和销毁解析
1 什么是栈区?
就是那些由编译器在需要的时候分配,在不需要的时候自动清除变量的存储区
2 什么是压栈?什么是出栈?
压栈:向栈区中添加一个元素
出栈:从栈区中删除一个元素
3 栈区原则 先进后出
二 认识相关寄存器和汇编指令
1 预备知识
1 寄存器的概念:寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果
重点是 1 在cpu中
2 暂时存放参与运算的数据和运算结果
2 相关寄存器
eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
ebp:栈底寄存器
esp:栈顶寄存器
eip:指令寄存器,保存当前指令的下一条指令的地址
这里我们暂时只需要记住ebp栈底寄存器 和esp栈顶寄存器就好
3 相关汇编命令
mov:数据转移指令
push:数据入栈,同时esp栈顶寄存器也要发生改变
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
sub:减法命令
add:加法命令
call:函数调用,1. 压入返回地址 2. 转入目标函数
jump:通过修改eip,转入目标函数,进行调用
ret:恢复返回地址,压入eip,类似pop eip命令
4 预备知识
- 每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。
- 这块空间的维护是使用了2个寄存器: esp和 ebp ebp记录的是栈底的地址esp记录的是栈顶的地址。
函数调用堆栈演示代码:
#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 3;
int b = 5;
int ret = 0;
ret = Add(a, b);
printf("%d\n", ret);
return 0;
}
2 准备环境
这段代码,如果我们在VS2019编译器上调试,调试进入Add函数后,我们就可以观察到函数的调用堆栈 (右击勾选【显示外部代码】),如下图:
函数调用堆栈是反馈函数调用逻辑的,那我们可以清晰的观察到, main 函数调用之前,是由 invoke_main 函数来调用main函数。
在 invoke_main 函数之前的函数调用我们就暂时不考虑了。
那我们可以确定, invoke_main 函数应该会有自己的栈帧, main 函数和 Add 函数也会维护自己的栈 帧,每个函数栈帧都有自己的 ebp和 esp来维护栈帧空间。
那接下来我们从main函数的栈帧创建开始讲解
为了让我们研究函数栈帧的过程足够清晰,不要太多干扰,我们可以关闭下面的选项,让汇编代码中排 除一些编译器附加的代码:
转到反汇编
int main()
{
//函数栈帧的创建
00BE1820 push ebp
00BE1821 mov ebp,esp
00BE1823 sub esp,0E4h
00BE1829 push ebx
00BE182A push esi
00BE182B push edi
00BE182C lea edi,[ebp-24h]
00BE182F mov ecx,9
00BE1834 mov eax,0CCCCCCCCh
00BE1839 rep stos dword ptr es:[edi]
//main函数中的核心代码
int a = 3;
00BE183B mov dword ptr [ebp-8],3
int b = 5;
00BE1842 mov dword ptr [ebp-14h],5
int ret = 0;
00BE1849 mov dword ptr [ebp-20h],0
ret = Add(a, b);
00BE1850 mov eax,dword ptr [ebp-14h]
00BE1853 push eax
00BE1854 mov ecx,dword ptr [ebp-8]
00BE1857 push ecx
00BE1858 call 00BE10B4
00BE185D add esp,8
00BE1860 mov dword ptr [ebp-20h],eax
printf("%d\n", ret);
00BE1863 mov eax,dword ptr [ebp-20h]
00BE1866 push eax
00BE1867 push 0BE7B30h
00BE186C call 00BE10D2
00BE1871 add esp,8
return 0;
00BE1874 xor eax,eax
}