函数栈帧的创建与销毁【此一篇,足以让卿彻底扫盲】

news2024/9/28 1:16:23

一、函数栈帧要解决的问题

  刚接触编程语言的的我们,想必都会存在这些问题:

Ⅰ  局部变量不初始化,为什么是随机值;

Ⅱ  形参与实参的关系;

Ⅲ  函数是如何被调用,以及开辟相对应的空间的;

Ⅳ  函数是如何传参的;

Ⅴ  函数的返回值;....等等

  通过对函数栈帧的学习,我相信:只要你理解了函数栈帧是什么?函数栈帧如何创建?函数栈帧如何销毁?你对这些问题,都能达到自我的攻略的地步。下面我们将通过C语言的一些简单的代码,去一块分析函数栈帧的全过程

一、函数栈帧的引入

1.认识栈

  栈(stack)是现代计算机程序里最为重要的概念之一,几乎每一个程序都使用了栈,没有栈就没有函数,没有局部变量,也就没有我们如今看到的所有的计算机语言。

  在经典的计算机科学中,栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈,push),也可以将已经压入栈中的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据后出栈(First In Last Out, FIFO)。

2.认识栈帧

  我们都知道,程序的运行,是在内存上的。而函数的栈帧,即指的是为该函数所开辟的空间,而为该函数开辟的空间是由两个寄存器esp、ebp共同维护的。而esp存储的该函数栈顶空间,而ebp存储的是栈底空间。

3.了解main函数也是被调用的

  其次我们还要知道的时候,程序的入口:main函数也是被另一个函数调用的——__tmaincrtstartup,而__tmaincrtstartup又被tmaincrtstartup调用的。

  最后函数的空间是在栈上开辟的,先使用高地址,在使用低地址。

 4.环境准备:

建议用32平台的,其次在属性,C/C++中,将仅可支持我的代码由否改为是。

 

二、函数栈帧的创建与销毁的全过程

这里我用下列的代码,下面转到反汇编来模拟一下函数栈帧创建与销毁的全过程(因为我刚开始忘记准备环境,为了能够让我们研究函数栈帧的创建与销毁过程足够清晰,所以我中途中断过程序,但这不影响你理解函数栈帧的过程!!!)

int main()
{
00881840  push        ebp  
00881841  mov         ebp,esp  
00881843  sub         esp,0E4h  
00881849  push        ebx  
0088184A  push        esi  
0088184B  push        edi  
0088184C  lea         edi,[ebp-24h]  
0088184F  mov         ecx,9  
00881854  mov         eax,0CCCCCCCCh  
00881859  rep stos    dword ptr es:[edi]  
	 int a = 1328, b = 14,c = 0;
0088185B  mov         dword ptr [ebp-8],530h  
00881862  mov         dword ptr [ebp-14h],0Eh  
00881869  mov         dword ptr [ebp-20h],0  
	 c = Sub(a,b);
00881870  mov         eax,dword ptr [ebp-14h]  
00881873  push        eax  
00881874  mov         ecx,dword ptr [ebp-8]  
00881877  push        ecx  
00881878  call        008811EA  
0088187D  add         esp,8  
00881880  mov         dword ptr [ebp-20h],eax  
	 printf("%d\n", c);
00881883  mov         eax,dword ptr [ebp-20h]  
00881886  push        eax  
00881887  push        887B30h  
0088188C  call        008810D2  
00881891  add         esp,8  
	 return 0;
00881894  xor         eax,eax  
}
00881896  pop         edi  
00881897  pop         esi  
00881898  pop         ebx  
00881899  add         esp,0E4h  
0088189F  cmp         ebp,esp  
008818A1  call        00881253  
008818A6  mov         esp,ebp  
008818A8  pop         ebp  
008818A9  ret  
 模拟函数栈帧创建(汇编指令片段一)

指令1:将ebp的值压进去,esp向上走4个字节。这是__tmaincrtstartup函数栈帧的栈底地址,目的是为了,main函数栈帧销毁后,能找到__tmaincrtstartup的函数栈帧的栈底地址。

指令2:将esp的值赋给ebp。此时ebp的则真正开始维护main函数的栈底地址。

指令3:将esp的值减去0x0E4。这是为main函数预开辟的空间,我们可以类比于局部变量的创建去理解。

指令4:将寄存器ebx,esi,edi的值压入栈中。因为后面可能会使用这些寄存器,而造成当前值的改变,所以,这步指令的目的是为了,之后能够方便找到原先寄存器中的值。

完成图如上所示。

 模拟函数栈帧创建(汇编指令片段二)

指令1:lea,将ebp-0x24的地址的值,加载给edi,

指令2:exc,将9的值给ecx,

指令3:eax,将0xCCCCCCCC的值存给eax,

指令4:将ebp-0x24与ebx之间的空间,全部初始化为0CCCCCCCC

这四个指令的作用,可以类比于局部变量的初始化来进行理解,为了我们更深一步的理解,写下了下面的伪代码:

for(edi = ebp - 0x24;edi < ebp;edi+4)
{
    *(int*)edi = 0xCCCCCCC;
}

模拟函数栈帧创建后变量创建与初始化

指令1:mov,将ebp-8的地址,划定为a的内存空间,并将前4个字节赋值为1328

指令2:mov,将ebp-14的地址,划定为b的内存空间,同理前4个字节赋值为14

指令3:mov,将ebp-20的地址,划定为c的内存空间,同理前4个字节赋值为0

模拟函数栈帧创建后Sub函数的调用与传参 

指令1,指令2:mov,push,将b的值给eax,将eax的值压栈,改变esp的位置,

指令3:指令4:mov,push,将a的值给ecx,将exc的值压栈,改变esp的位置

指令5:call(1.压入下一指令的地址,类似于push 00881870,2.调入子程序,点Fn+F11来到Sub函数)

 模拟函数栈帧创建后Sub函数的创建

 因为Sub函数栈帧空间的创建与main函数一致,故直接展示进行过后的抽象图:

 模拟函数栈帧创建后Sub函数的局部变量

指令1:mov,将ebp-8的位置为z开辟,并将前4个字节赋值为0;

指令2,3,4:将a,b的值给eax寄存器,完成整数的减法,并将exa的值赋给z;

指令5,将z的值交给eax,目的是为了,在函数栈帧销毁后,能带回z的值;

模拟函数栈帧创建后Sub函数栈帧的销毁

指令1,2,3,弹出edi,esi,ebx,得到之前的值

指令4,ebp的值给esp,此时Sub函数栈帧真正销毁

指令5,弹出main函数ebp的值

指令6,恢复返回地址,压入eip,类似于pop eip

模拟函数栈帧销毁

因为main函数栈帧的销毁,与Sub的销毁相似,故不在深入探讨。感觉大家的观看。

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

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

相关文章

MySQL数据库的锁机制

目录 一、引言 二、锁的类型及作用 2.1 行级锁 2.2 间隙锁与临键锁 2.3 共享锁与排他锁 2.4 意向锁 2.5 表级锁 2.6 元数据锁 三、锁的管理与优化 3.1 合理设置事务隔离级别 3.2 避免长事务 3.3 索引优化 3.4 明确锁定范围 3.5 避免不必要的全表扫描 四、实战分…

SpringBoot+beetl idea热更新解决方案

SpringBootbeetl idea热更新解决方案 第一在application中开启&#xff1a; beetl:resource-auto-check: true #热加载beetl模板&#xff0c;开发时候用第二在application中开启&#xff1a; devtools: 这个部分专门用于配置Spring Boot DevTools的相关参数。DevTools…

基于springboot+vue的“衣依”服装销售平台系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

【latex】在Overleaf的IEEE会议模板中,快速插入参考文献

【LaTeX】在Overleaf的IEEE会议模板中&#xff0c;快速插入参考文献 写在最前面第一步&#xff1a;在文献检索网站导出引用文献的bib文件第二步&#xff1a;编辑overleaf模版方法二&#xff1a;EduBirdie生成参考文献&#xff08;补充&#xff09;使用LaTeX在Overleaf的IEEE会议…

Spring 声明式事务 @Transactional(详解)【面试重点,小林出品】

关于 Transactional 注解的基本使用&#xff0c;推荐看Spring 声明式事务 Transactional&#xff08;基本使用&#xff09; 概述 本篇博客主要学习 Transactional 注解当中的三个常⻅属性: 1. rollbackFor:异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型 …

Goby 漏洞发布|GoAnywhere MFT InitialAccountSetup.xhtml 绕过漏洞(CVE-2024-0204)

漏洞名称&#xff1a;GoAnywhere MFT InitialAccountSetup.xhtml 绕过漏洞&#xff08;CVE-2024-0204&#xff09; English Name&#xff1a;GoAnywhere MFT InitialAccountSetup.xhtml Bypass Vulnerability (CVE-2024-0204) CVSS core: 9.8 影响资产数&#xff1a; 4468 …

微信小程序如何获取当前日期时间

Hello大家好&#xff01;我是咕噜铁蛋&#xff0c;获取当前日期时间是小程序中经常会用到的一个功能。因此&#xff0c;在本文中&#xff0c;我通过科技手段给大家收集整理了下&#xff0c;今天我将向大家介绍如何在微信小程序中获取当前日期时间的方法&#xff0c;并分享一些实…

【项目搭建三】SpringBoot引入redis

添加依赖 本文使用spring data redis访问和操作redis&#xff0c;pom文件中加入以下依赖&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </depende…

java开发——《并发编程》

目录 一.jmm 二.并发了什么 1.只有一个核&#xff08;单核&#xff09;并发还有没有意义 2.单核&#xff0c;还有什么可见性问题 3.并发和并行 三.volitaile 1.变量的可见性问题 2.原因是什么 3.本次修改的变量直接刷到主内存 4.声明其他内存对于这个地址的缓存无效 …

Linux:gcc的相关知识

目录 gcc的翻译&#xff08;编译&#xff09;过程&#xff1a; 预处理&#xff1a; 条件编译&#xff1a; 编译&#xff1a; 汇编&链接&#xff1a; 什么是链接&#xff1f; 安装静态库&#xff1a; 静态库的使用&#xff1a; 动态静态的对比&#xff1a; 优缺对比…

HCIE之BGP基础练习(二)

BGP 一、BGP报文和状态机1.EBGP 建立邻居仅需一条命令2.IBGP之间建立邻居只需要三条命令3.EBGP之间用回环口建立邻居需要三条命令4.下一跳5.宣告路由6.full-math邻居7.路由反射器 一、BGP报文和状态机 1.EBGP 建立邻居仅需一条命令 [AR1]bgp 100 [AR1-bgp]router-id 1.1.1.1 […

Qt5项目拆解第一集解决:中文乱码| 全局字体|注册表|QSS/CSS

# 一、乱码解决代码片段 QTextCodec是Qt中用于处理文本编码和字符集转换的类。它提供了一系列静态函数来实现不同编码的文本转换,包括编码转换、字符集检测和转换、以及数据流中的文本编码处理。QTextCodec类使得Qt可以在不同的编码和字符集之间进行无缝转换,从而方便地处理…

帮你找到99%的电子书,这46个免费电子书网站,你还不知道吗?

国内网站&#xff1a;32个 1鸠摩搜书 网址&#xff1a;https://www.jiumodiary.com/ 一个强大的搜书神站&#xff0c;无论是什么类型的书籍&#xff0c;只要你知道书名&#xff0c;就可以轻松的搜到你想要书籍。页面简单明了&#xff0c;书籍种类繁多&#xff0c;格式多种多样…

linux修改文件夹下所有文件的权限(常用)

1、 修改文件夹下所有文件的权限 # filename为要修改的文件夹名字。-R应该是表示递归修改filename文件夹下所有文件的权限 sudo chmod -R 777 filename2、linux修改单个文件夹权限 sudo chmod 600 &#xff08;只有所有者有读和写的权限&#xff09; sudo chmod 644 &#…

基于sentinel-2 遥感数据的水体提取(水体指数法)

本文框架设置如下&#xff1a; 简单介绍senintel-2数据&#xff1b;如何利用sentinel-2数据获取水体边界/范围 1 Sentinel-2数据介绍及下载方式 有Sentinel-2A/2B两颗卫星&#xff0c;其参数基本一致&#xff0c;因此两颗卫星的数据联合使用很方便。 分辨率有&#xff1a;1…

助力医疗数字化转型,贝锐x医百科技案例解析

在医疗数字化这个历史进程的大浪潮中&#xff0c;医药企业扮演着重要的角色&#xff0c;其重要程度恐怕仅次于医疗机构本身。同时&#xff0c;数字化转型对于医药企业的赋能作用也是十分明显的&#xff0c;尤其在营销端&#xff0c;一系列的数字化管理、数字化推广方案已经成为…

前端开发中的那些规范

开发中的那些规范 俗话说&#xff1a;无规矩不成方圆。生活如此、软件开发也如此。 来聊一聊开发中有哪些地方需要规范。 为什么需要规范 现在开发一个应用基本上都是多人协作&#xff0c;一旦涉及到多人&#xff0c;必然不同的开发者的开发习惯、编码方式都是有所不同的&…

常用电子器件学习——三极管

三极管介绍 三极管&#xff0c;全称应为半导体三极管&#xff0c;也称双极型晶体管、晶体三极管&#xff0c;是一种电流控制电流的半导体器件其作用是把微弱信号放大成幅度值较大的电信号&#xff0c; 也用作无触点开关。晶体三极管&#xff0c;是半导体基本元器件之一&#xf…

NAT地址转换协议

目录 NAT应用场景静态NAT动态NATNAPTEasy IPNAT服务器 点击跳转NAT配置&#xff08;动态nat&#xff0c;静态nat&#xff0c;Easy IP&#xff09; NAT应用场景 - 随着网络设备的数量不断增长&#xff0c;对IPv4地址的需求也不断增加&#xff0c;导致可用IPv4地址空间逐渐耗尽…