函数栈帧(上)

简介: 函数栈帧(上)

一 函数栈帧的创建及销毁


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 预备知识


  1. 每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。
  2. 这块空间的维护是使用了2个寄存器: esp和 ebp ebp记录的是栈底的地址esp记录的是栈顶的地址。

如下图所示:


9221dce2290b4a6e8b04928dce0368c9.png


函数调用堆栈演示代码:


#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函数后,我们就可以观察到函数的调用堆栈 (右击勾选【显示外部代码】),如下图:


d63277fa3f724b0bb43065a0194608e5.png

函数调用堆栈是反馈函数调用逻辑的,那我们可以清晰的观察到, main 函数调用之前,是由 invoke_main 函数来调用main函数。

在 invoke_main 函数之前的函数调用我们就暂时不考虑了。


那我们可以确定, invoke_main 函数应该会有自己的栈帧, main 函数和 Add 函数也会维护自己的栈 帧,每个函数栈帧都有自己的 ebp和 esp来维护栈帧空间。

那接下来我们从main函数的栈帧创建开始讲解


为了让我们研究函数栈帧的过程足够清晰,不要太多干扰,我们可以关闭下面的选项,让汇编代码中排 除一些编译器附加的代码:


35e17e90fb364f8fbaaf9d5f635d7dee.png


转到反汇编


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
}
相关文章
|
6月前
|
编译器
函数栈帧的创建和销毁
函数栈帧的创建和销毁
32 0
|
7月前
|
存储 程序员 编译器
深入理解函数调用--函数栈帧
深入理解函数调用--函数栈帧
|
存储
函数栈帧的创建和销毁(下)
函数栈帧的创建和销毁(下)
54 0
|
7月前
|
容器
函数栈帧的创建和销毁介绍
函数栈帧的创建和销毁介绍
42 0
|
编译器 程序员 C语言
函数栈帧的创建与销毁(超详解)
函数栈帧的创建与销毁(超详解)
110 0
|
存储 缓存 编译器
函数栈帧的创建与销毁
函数栈帧的创建与销毁
41 0
|
编译器 C语言 容器
函数栈帧的创建和销毁(一)
函数栈帧的创建和销毁
118 1
|
存储 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
118 0
|
存储 机器学习/深度学习 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
726 0
|
存储 编译器 C语言
C/函数栈帧
C/函数栈帧