函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)

news2024/9/27 7:21:06

函数栈帧的创建和销毁[以C语言代码为例,汇编代码的角度分析]

  • 一.前言
    • 1.几个问题
    • 2.几个说明
  • 二.相关寄存器和汇编命令的简要说明
  • 三.从汇编代码调试的角度逐步分析函数栈帧的创建于销毁
    • 1.函数栈区的知识:
    • 2.逐步调试分析
      • 1.保存__tmainCRTStartup这个函数栈帧的栈底地址
      • 2.正式进入main函数
      • 3.开辟main函数栈帧
      • 4.将main函数栈帧中的数据置为随机值
      • 5.函数传参的准备
        • 1.创建形参
        • 2.将call指令的下一条指令的地址压栈
      • 6.正式进入MyAdd函数并且开辟栈帧,置为随机值
      • 7.取出形参的值
      • 8.销毁MyAdd函数栈帧
      • 9.返回
  • 四.总结

一.前言

1.几个问题

在C语言学习阶段,我们可能会遇到下面几个问题,
在学习完函数栈帧的创建和销毁之后,我们就能更加深刻地理解下面几个问题了
在这里插入图片描述

2.几个说明

其次,我们要说明的是:不同编译器下汇编指令的样子是有所差异的
下面给大家看一下同样的代码在VS2013中的样子
在这里插入图片描述
同样的代码在Linux中的样子
在这里插入图片描述
而且在观察汇编代码学习函数栈帧的创建和销毁的过程中.
不要使用太高级的编译器,越高级的编译器越不容易学习和观察

同时在不同的编译器下,函数的调用过程是略有差异的,具体细节取决于编译器的实现

我们这一篇博客以VS2013为例,学习函数栈帧的创建和销毁的过程

二.相关寄存器和汇编命令的简要说明

在这里插入图片描述

三.从汇编代码调试的角度逐步分析函数栈帧的创建于销毁

我们以这份代码为例:

#include <stdio.h>
int MyAdd(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int x = 0xA;
	int y = 0xB;

	int z = MyAdd(x, y);

	return 0;
}

1.函数栈区的知识:

首先我们要说明两点:

1.函数是开辟在栈区的,栈区空间的使用习惯是
先使用高地址,后使用低地址

也就是说函数栈区是从高到低去开辟的

2.main函数是也是被调用的
具体调用流程如下
在这里插入图片描述

2.逐步调试分析

在这里插入图片描述
在这里插入图片描述
初始情况:
esp(栈顶指针)
ebp(栈底指针)
esp和ebp之间有一块空间
这块空间其实就是__tmainCRTStartup的栈帧
在这里插入图片描述

1.保存__tmainCRTStartup这个函数栈帧的栈底地址

执行第一条指令:

push ebp
把ebp的值入栈,并且esp减小

在这里插入图片描述

2.正式进入main函数

执行第二条指令:

mov ebp esp
就是把esp的值给ebp

在这里插入图片描述

3.开辟main函数栈帧

执行第3条指令

sub esp 0E4h

esp = esp-0E4h
也就是esp减少0E4h大的空间

这就是为main函数开辟栈帧
也就是说在汇编中开辟栈帧的方式就是栈顶指针减少

其中
函数的栈帧大小是由编译器决定的
根据什么决定的呢?
编译器可以通过sizeof求出该函数栈帧中的所有变量的具体大小
并进行合理分配栈帧的大小

在这里插入图片描述

4.将main函数栈帧中的数据置为随机值

接下来是3条push指令

push ebx
push esi
push edi

在这里插入图片描述
请注意:esp每一次push之后的值都减4
为什么呢?
因为push是压栈操作.我们可以理解为push进去的数据在内存空间上是紧密相邻的

接下来是:

lea edi,[ebp+FFFFFF1Ch]
就是把ebp+FFFFFF1Ch这个地址加载到edi中

其实这个ebp+FFFFFF1Ch就是ebp-0E4h
而这个0E4h就是第三条指令中
sub esp 0E4h
这个esp减去的大小
也就是这个main函数的栈帧大小

在这里插入图片描述
下面两条指令:

mov ecx,39h
mov eax,0CCCCCCCCh
把16进制数字:39h给ecx
把0CCCCCCCCh给eax

在这里插入图片描述
下面这个指令:

rep stos    dword ptr es:[edi]

在这里插入图片描述
因此我们就可以回答

为什么局部变量的值是随机值呢?

因为函数栈帧创建之后,会对栈帧中的数据进行初始化,
而局部变量是开辟在栈帧中的,

如果没有对该局部变量进行初始化
那么该局部变量的值就是随机值

而VS2013中的随机值就是0CCCCCCCCh
这也就解释了为什么我们经常会见到烫烫烫烫烫烫这样的字符
在这里插入图片描述
这就是我们初始化后的栈帧空间
在这里插入图片描述
执行完刚才那条指令之后ecx被清0
edi会指向ebp

5.函数传参的准备

1.创建形参

下面两条指令在main函数的栈帧中创建了x和y这两个局部变量

mov   dword ptr [ebp-8],0Ah
mov   dword ptr [ebp-14h],0Bh

把0Ah(就是10进制的10)赋值给ebp-8内存空间的值
把0Bh(就是10进制的11)赋值给ebp-14h内存空间的值

这里我们就可以回答第一个问题了:局部变量是如何创建的?

局部变量是通过栈底指针的偏移定位到空间
将对应空间分配给局部变量
然后将该空间内的值修改为对应初始化的值
在这里插入图片描述
下面四条指令是:

mov     eax,dword ptr [ebp-14h]
push    eax
mov     ecx,dword ptr [ebp-8]
push    ecx
本质是形成形参进行函数传参
其中这个eax中放的是y的值
ecx中放的是x的值

因此这也就解释了第三条问题:

函数是如何传参的?

通过栈底指针的偏移找到实参的值,
把对应实参的值传递给寄存器eax和ecx
然后把eax和ecx的值入栈
就形成了形参

也就是说形参是实参的一份拷贝,改变形参不会影响实参

传参的顺序是怎样的?

顺序是从右向左传参
在这里插入图片描述
eax变为11
ecx变为10

2.将call指令的下一条指令的地址压栈

下面就要进行call指令调用MyAdd函数了
首先编译器会先把call指令的下一条指令保存下来
也就是说这里实际上是把add指令的地址压入了栈帧当中
也就是执行了
push add的地址
在这里插入图片描述
在这里插入图片描述
下一条是jmp命令
这个命令就很简单
执行完jmp命令后就正式进入了MyAdd函数

jmp  00bb13c0

这样我们就能回答第5个问题了:

函数调用是怎么做的?

函数调用时,先形成形参,
然后把main函数的栈底指针的地址保存下来
然后把对应函数调用指令的下一条指令的地址压入栈中

然后正式进入MyAdd函数

6.正式进入MyAdd函数并且开辟栈帧,置为随机值

然后我们进入MyAdd函数
在这里插入图片描述
下面这10条指令跟创建main函数栈帧的指令如出一辙
就是先把main函数的栈底地址入栈
然后把esp的值赋给ebp:其实就是正式进入MyAdd这个函数的栈帧
然后sub esp,0CCh就是为MyAdd函数创建栈帧
push ebx,esi,edi 为后续初始化MyAdd函数栈帧做准备

rep stos dword ptr es:[edi]: 就是把MyAdd函数栈帧中的空间初始化为随机值0CCCCCCCCh

push        ebp  
mov         ebp,esp  
sub         esp,0CCh  
push        ebx  
push        esi  
push        edi  
lea         edi,[ebp+FFFFFF34h]  
mov         ecx,33h  
mov         eax,0CCCCCCCCh  
rep stos    dword ptr es:[edi] 

在这里插入图片描述
在这里插入图片描述

7.取出形参的值

然后下面这三条指令就是:

mov         eax,dword ptr [ebp+8]  
add         eax,dword ptr [ebp+0Ch]  
mov         dword ptr [ebp-8],eax  

其实就是通过ebp栈底指针的偏移找到创建的形参,取出形参的值
赋值,相加给eax
然后通过eax的赋值和ebp栈底指针的偏移创建MyAdd函数栈帧中的局部变量c

在这里插入图片描述

8.销毁MyAdd函数栈帧

下面这5条指令就是

mov eax,dword ptr [ebp-8]
pop edi
pop dsi
pop ebx
mov esp,ebp

保存返回值并且销毁MyAdd这个函数栈帧
在这里插入图片描述

9.返回

其中:

返回的本质是:
1.返回到main函数的栈帧
2.返回到call指令的下一条指令的地址处
3.如果有返回值,把返回值进行返回

下面两条指令就会
分别将ebp和esp返回到main函数中的对应的位置

pop     ebp
ret

在这里插入图片描述
最后两条指令

add esp,8
mov    dword ptr [ebp-20h] eax

在这里插入图片描述
这样就能回答第6个问题了

函数调用结束之后是怎么返回的呢?

首先先把返回值进行临时拷贝,在MyAdd这个函数中是用寄存器eax来保存这个返回值
然后通过弹出main函数的栈底地址,将栈底指针ebp恢复为main函数的栈底指针

通过弹出call指令的下一条指令的地址并将这个地址存入eip中
保证call指令结束后能够回到call指令的下一条指令的位置

我们同时也可以看出:
形参先创建,然后再调用函数
函数先销毁,然后再销毁形参

函数的销毁和形参的销毁都是通过栈顶指针的增大实现的

函数栈帧的创建是通过栈顶指针的减小实现的

在函数中创建局部变量是通过栈底指针的偏移定位到要创建的位置
通过eax,ecx等等寄存器来完成变量值的传递

以上就是以MyAdd为例,
整个函数栈帧的创建和销毁过程的分析

四.总结

综上,我们可以得出
在这里插入图片描述

以上就是函数栈帧的创建和销毁的全部内容,希望能对大家有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1151519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】centOS7安装配置及Linux的常用命令---超详细

一&#xff0c;centOS 1.1 centOS的概念 CentOS&#xff08;Community Enterprise Operating System&#xff09;是一个由社区支持的企业级操作系统&#xff0c;它是以Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码为基础构建的。CentOS提供了一个稳定、可靠且…

解决计算机msvcp120.dll文件丢失的5种方法,亲测有效

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp120.dll丢失”。这个错误提示可能会给我们带来很大的困扰&#xff0c;影响我们的正常使用。本文将详细介绍msvcp120.dll丢失的原因、解决方法以及预防措施&#xff0c;帮助大家更好地…

python读取shadow文件脚本

python读取shadow文件脚本 该脚本源代码为kali中执行的源代码 from dataclasses import fieldswith open(/etc/shadow,r)as file:for line in file:listline.split(:)if list[1]!"*" and list[1]!"!" and list[1]!"!*":paslist[1].split($)sal…

基于FMCW雷达的人体复杂动作识别

基于FMCW雷达的人体复杂动作识别

【Python算法】算法练习(一)

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 目录 1、输出n以内的质数 2、求n以内最大的m个质数的和&#xff0c;并打印这些质数以及它们的和 方法一 方法二…

合肥中科深谷嵌入式项目实战——人工智能与机械臂(三)

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 作者&#xff1a;爱吃饼干的小白鼠。Python领域优质创作者&#xff0c;2022年度博客新星top100入围&#xff0c;荣获多家平台专家称号。…

postgresql14-sql基础(一)

初始化 在“开始”中运行pgadmin4&#xff0c;输入密码&#xff0c;连接实例&#xff0c;创建测试数据库&#xff1a;hrdb 开启查询工具面板。 https://download.csdn.net/download/hy19930118/88419281 SELECT version() AS "pg版本"简单查询 SELECT first_na…

性能测试支持结果抽样分析,执行机新增运行状态和CPU监控,MeterSphere开源持续测试平台v2.10.8 LTS版本发布

2023年10月30日&#xff0c;MeterSphere一站式开源持续测试平台正式发布v2.10.8 LTS版本。自2023年5月发布v2.10 LTS版本后&#xff0c;MeterSphere开源项目组坚持发布小版本&#xff0c;持续进行问题的修复更新&#xff0c;并针对部分功能进行优化。 本次发布的MeterSphere v…

C/C++ 作业题笔记

请计算下列代码运行结果 解析:

获取服务器或域控登录日志工具

SharpUserIP 功能简介 服务器登陆日志 (需管理员权限) 在域控或远程提取登录日志&#xff0c;快速获取域用户对应的 IP 地址 项目地址&#xff1a;https://github.com/lele8/SharpUserIP 使用说明 ___ _ _ _ ___ ___/ __| |_ __ _ _ _ _ _…

docker解决oracle中ORA-12514和ORA-03113问题

ORA-12514&#xff1a;TNS&#xff1a;监听程序当前无法识别连接描述符中请求的服务; 1、进入docke容器 docker exec -it 容器id bash 2、找到并修改listener.ora文件 查看oracle的位置 命令&#xff1a;cat /etc/profile 这是listener.ora文件的位置 /home/oracle/app/oracl…

输入输出缓冲区的作用,c++io流介绍,转换运算符(operator+类型)

目录 引入 输入输出缓冲区的作用 流 cio流 介绍 为什么要把流进行面向对象的设计呢? 原理 使用的注意点 istream类型对象转换为逻辑条件判断值 引入 转换运算符 文件io 介绍 示例 注意点 说明 利用字节流特性 字符串io 介绍 istringstream ostringstrea…

七、【图像添加水印】

文章目录 一、制作水印1、先新建图层2、新建文字图层并调好水印文字的大小与角度3、添加图层样式4、添加定义图案 二、添加水印 一、制作水印 1、先新建图层 2、新建文字图层并调好水印文字的大小与角度 3、添加图层样式 1、打开“描边” 2、选择“颜色” 4、添加定义图案 二…

【AI视野·今日NLP 自然语言处理论文速览 第六十期】Mon, 23 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 23 Oct 2023 (showing first 100 of 108 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Exploring Linguistic Probes for Morphological Generalization Autho…

电商数据采集抓取封装数据、淘宝、天猫、京东等平台商品详情API接口参数详解

电商数据采集抓取数据、淘宝、天猫、京东等平台的电商数据抓取&#xff0c;网页爬虫、采集网站数据、网页数据采集软件、python爬虫、HTM网页提取、APP数据抓包、APP数据采集、一站式网站采集技术、BI数据的数据分析、数据标注等成为大数据发展中的热门技术关键词。那么电商数据…

Android开发笔记(三)—Activity篇

活动组件Activity 启动和结束生命周期启动模式信息传递Intent显式Intent隐式Intent 向下一个Activity发送数据向上一个Activity返回数据 附加信息利用资源文件配置字符串利用元数据传递配置信息给应用页面注册快捷方式 启动和结束 &#xff08;1&#xff09;从当前页面跳到新页…

数据库软考知识

分布式数据库透明性 封锁 加上共享锁之后只能加共享锁&#xff0c;加上排他锁之后&#xff0c;啥锁都不能加。 分布式数据库特性 伪传递定理 SQL函数定义&#xff0c;有点冷 来了奥&#xff0c;更冷 存储过程 很重要&#xff0c;下午第二大题也是数据库

解决QtCreator11及更高版本使用JDK17时无法生成APK

1.修改gradle文件&#xff0c;增加如下内容&#xff1a; DEFAULT_JVM_OPTS"-Xmx5120m --add-exportsjava.base/sun.nio.chALL-UNNAMED \ --add-opensjava.base/java.langALL-UNNAMED \ --add-opensjava.base/java.lang.reflectALL-UNNAMED \ --add-opensjava.base/java.io…

用户优先:确保微信小程序手机号授权在新旧版本中无缝衔接

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

SQL注入——二次注入漏洞

文章目录 SQL注入——二次注入漏洞1. 二次注入原理2. 二次注入需要具备的两个条件3. 二次注入实例4. 总结 SQL注入——二次注入漏洞 1. 二次注入原理 在第一次插入恶意数据的时候&#xff0c;只是对其中的特殊字符进行了转义&#xff0c;在写入数据库的时候还是原来的字符&am…