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

文章目录

  • 前言
  • 一、寄存器
    • 1.寄存器是什么?
    • 2.寄存器的类型
  • 二、栈
    • 1.栈区是什么
    • 2.栈区的常见操作
  • 三、函数栈帧
    • 1.函数调用时发生了什么?
  • 四、寄存器与函数栈帧
  • 最后


前言

栈是函数执行的内存区域,是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后面的指令继续执行主要函数的代码。

如下图:
【C语言】函数栈帧——函数调用时发生了什么?_第1张图片

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

这是因为

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

【C语言】函数栈帧——函数调用时发生了什么?_第2张图片


四、寄存器与函数栈帧

每一个函数独占自己的栈帧空间。当前正在运行的函数的栈帧总是在栈顶。
【C语言】函数栈帧——函数调用时发生了什么?_第3张图片

函数栈帧:

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

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

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

将上面的代码转到反汇编模式查看
【C语言】函数栈帧——函数调用时发生了什么?_第4张图片
对应观察:

【C语言】函数栈帧——函数调用时发生了什么?_第5张图片
1.函数返回过程
(1)保存返回值:通常将函数的返回值保存在寄存器EAX中。
(2)弹出当前栈帧,恢复上一个栈帧。
1.在堆栈平衡的基础上,给esp加上栈帧的大小,降低栈顶,回收当前栈帧的空间。
2.将当前栈帧底部保存的前栈帧ebp值弹入ebp寄存器,
复出上一个栈帧。
3.将函数返回地址弹给ebi寄存器。
(3)跳转:按照函数返回地址跳回母函数中继续执行。

【C语言】函数栈帧——函数调用时发生了什么?_第6张图片


最后

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

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

你可能感兴趣的:(玩转C语言系列,c语言,其他,经验分享)