资讯专栏INFORMATION COLUMN

【C语言】函数栈帧——函数调用时发生了什么?

Muninn / 568人阅读

摘要:当函数返回时,系统栈会弹出该函数所对应的栈帧。四寄存器与函数栈帧每一个函数独占自己的栈帧空间。栈帧调整具体包括。


前言

栈是函数执行的内存区域,是C语言运行时最重要的元素之一


一、寄存器

1.寄存器是什么?

寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。

2.寄存器的类型

寄存器有eax,ebx,ecx,edx,还有ebp,esp。本文主要介绍最后两个,由于寄存器不是本次博客的重点,其他请自行了解!

ESP栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
EBP基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部

想要了解函数栈帧,就必须先了解好这两个地址。

ebp,esp这两个寄存器中存放的是地址,且这两个地址是用来维护函数栈帧的

二、栈

1.栈区是什么

栈区:

用于动态地存储函数之间的调用关系,以保证被调用函数在返回时恢复到母函数中继续执行。

2.栈区的常见操作

栈的最常见操作有两种:

压栈(push)
弹栈(pop)

用于标识栈的属性也有两个:

栈顶(top)
栈底( base)。

什么意思?

可以把栈想象成一摞扑克牌。

push为栈增加一个元素的操作叫做推,相当于在这摞扑克牌的最上面再放上一张。
pop从栈中取出一个元素的操作叫做流行,相当于从这摞扑克牌取出最上面的一张。
pop从栈中取出一个元素的操作叫做流行,相当于从这摞扑克牌取出最上面的一张。
base标识栈底位置,它记录着扑克牌最下面一张的位置。base用于防止栈空后继续弹栈(牌发完时就不能再去揭了)。很明显,一般情况下,base是不会变动的。

三、函数栈帧

1.函数调用时发生了什么?

以下面代码为例:

#define _CRT_SECURE_NO_WARNINGS 1#include 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;}

当中央处理器在执行调用Add函数的时候,会从代码区中
主要函数对应的机器指令的区域跳转到Add函数对应的机器指令区域,在那里取指并执行;当Add函数执行完闭,需要返回的时候,又会跳回到主要函数对应的指令区域,紧接着调用Add后面的指令继续执行主要函数的代码。

如下图:

那么中央处理器是怎么知道要去Add函数的代码区取指,
在执行完Add后又是怎么知道跳回到主要函数(而不是其他未知的代码区)的呢?

这是因为

当函数被调用时,系统栈会为这个函数开辟一个新的栈帧,并把它压入栈中。这个栈帧中的内存空间被它所属的函数独占,正常情况下是不会和别的函数共享的。当函数返回时,系统栈会弹出该函数所对应的栈帧。


四、寄存器与函数栈帧

每一个函数独占自己的栈帧空间。当前正在运行的函数的栈帧总是在栈顶。

函数栈帧:

esp和ebp之间的内存空间为当前栈帧,esp标识了当前栈帧的顶部,ebp标识了当前栈帧的底部。

函数调用大致包括以下几个步骤。

(1)参数入栈:将参数从右向左依次压入系统栈中。 (2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行。
(3)代码区跳转:处理器从当前代码区跳转到被调用函数的入口处。
(4)栈帧调整:具体包括。保存当前栈帧状态值,已备后面恢复本栈帧时使用(ebp入栈);将当前栈帧切换到新栈帧(将esp值装入ebp,更新栈帧底部);给新栈帧分配空间(把esp减去所需空间的大小,抬高栈顶);

将上面的代码转到反汇编模式查看

对应观察:


1.函数返回过程
(1)保存返回值:通常将函数的返回值保存在寄存器EAX中。
(2)弹出当前栈帧,恢复上一个栈帧。
1.在堆栈平衡的基础上,给esp加上栈帧的大小,降低栈顶,回收当前栈帧的空间。
2.将当前栈帧底部保存的前栈帧ebp值弹入ebp寄存器,
复出上一个栈帧。
3.将函数返回地址弹给ebi寄存器。
(3)跳转:按照函数返回地址跳回母函数中继续执行。


最后

本文内容来自于《oday安全软件漏洞分析技术》第2版与自己学习笔记所,本文仅记述自己学习历程与仅供自己学习,复习用,不做任何商业用途。

若有兴趣深入了解函数栈帧,请自行查阅王清主编的《Oday安全:软件漏洞分析技术(第2版)》一书。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121674.html

相关文章

  • C语言知识精讲②】函数栈帧的创建和销毁(全程图解)

    摘要:这里分块讲解六函数栈帧的销毁过程一解析的作用是将栈顶的数据弹出,弹出数据储存到相应寄存器中。 ?前言? 读完这篇博客,你可以明白什么? ①局部变量到底是怎么在栈上创建的? ②为什么局部变量不初始化为随机值? ③函数是怎么传参的?传参的先后顺序是什么? ④形参和实参是什么关系? ⑤函数调用是怎...

    davidac 评论0 收藏0
  • C语言笔记(1.2版本,目前22000字)----未完待续

    摘要:局部变量是指代码块内的变量,全局变量是指代码块外定义的变量。强制类型转换的格式是类型变量名而不是类型变量名,后者是变量的定义,编译器会显示对变量的重定义。 目录 前言 0、基础常识 (1)进制 (2)变量与常量 (3)内存 (4)其它零零碎碎的点 (5)运算符 1、关键字 1.switch...

    I_Am 评论0 收藏0
  • 聊聊Java的异常机制及实现

    摘要:是那些可能在虚拟机正常运行期间抛出的异常的超类。运行时异常定义及其子类都被称为运行时异常。对于语言中的关键字和,虚拟机中并没有特殊的字节码指令去支持它们,都是通过编译器生成字节码片段以及不同的异常处理器来实现。 前言 在一些传统的编程语言,如C语言中,并没有专门处理异常的机制,程序员通常用方法的特定返回值来表示异常情况,并且程序的正常流程和异常流程都采用同样的流程控制语句。Java语言...

    Towers 评论0 收藏0
  • 函数栈帧解析

    摘要:函数栈帧的销毁汇编语言了解函数传参函数返回值如何返回函数中变量如何初始化和赋值函数执行结束后系统进行了什么操作 文章目录 一、什么是函数栈帧 1.寄存器2.函数栈帧3.栈帧的作用和维护4.栈帧结构二、函数栈帧的创建  1.汇编2.main函数3.Add函数的创建三、函数...

    MonoLog 评论0 收藏0
  • C函数调用过程原理及函数栈帧分析

    摘要:在函数调用过程中,我们将调用函数的函数称为调用者,将被调用的函数称为被调用者。这两件事都是调用者负责的,因此压入的栈应该属于调用者的栈帧。函数的调用其实不难,只要搞懂了如何保存以及还原和,就能明白函数是如何通过栈帧进行调用和返回的了。 在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等。一切的函数调用都要将不同的数据、地址压入或者弹出栈。因此,为...

    ASCH 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<