成都网站建设 3e网站建设网络服务费

news/2025/9/23 12:58:44/文章来源:
成都网站建设 3e网站建设,网络服务费,杭州哪里做网站,校园网站建设的需求分析上两篇文章我们初步接触了ABI-应用程序二进制接口的概念#xff0c;点击链接查看上一篇文章#xff1a;【软件开发底层知识修炼】二十二 ABI-应用程序二进制接口 二。了解了为什么会有ABI的存在。本篇文章继续学习ABI 的内容。学习在ABI规范下#xff0c;函数栈帧的结构与函… 上两篇文章我们初步接触了ABI-应用程序二进制接口的概念点击链接查看上一篇文章【软件开发底层知识修炼】二十二 ABI-应用程序二进制接口 二。了解了为什么会有ABI的存在。本篇文章继续学习ABI 的内容。学习在ABI规范下函数栈帧的结构与函数调用时函数栈帧的详细变化。 文章目录1 什么是函数栈帧1.1 函数栈帧中ebp寄存器1.2 Linux系统中的栈帧布局2 函数调用时的‘前言’和‘后序’2.1 函数调用时发生的细节操作2.2 函数调用时的前言和后序3 函数栈帧结构的实际代码案例分析3.1 代码3.2 分析函数栈帧的形成与摧毁3.21 函数栈帧的形成3.22 函数栈帧的摧毁4 总结 1 什么是函数栈帧 早在之前我们就已经认识到了函数栈帧的作用。只不过一直没有拿出来说。那么到底什么是函数栈帧呢下面这幅图很多人应该看过的 图一上图是函数运行时函数所需要的栈内存的结构。每个运行的函数都有这么一个栈结构。它记录了函数的运行状态信息等。至于为什么是上图的这种结构这其实就是ABI的规范所规定的了。现在可能有的人还不懂上述这种结构的作用。不用着急在后面我们就会详细说明上述结构的具体作用了。在那之前我们先要知道以下三点; ABI定义了函数调用时 栈帧的内存布局就是上图的布局栈帧的形成方式上图的形成方式栈帧的销毁方式函数调用结束后上述栈帧就会消失 以上都是ABI的规范内容。对于不同的平台很有可能上述的三点都不一样。那么相应的编译器一定要满足相应的ABI规范才可以。由此可见ABI是多么重要。 1.1 函数栈帧中ebp寄存器 我们学过x86汇编的话就应该知道寄存器是个什么东西。如果不懂可以看我另一个专栏《x86汇编》–点击链接查看 在函数栈帧中最重要的寄存器有两个一个是栈顶指针寄存器esp,一个是函数帧帧基址寄存器ebp。由于esp的作用很容易理解它就是指向栈顶的寄存器这里不再多说。我们想说的是ebp寄存器。它在函数调用的过程中可谓是一个纽带。用于连接调用函数和被调用函数的。 ebp为当前栈帧的基准它存储的数据是上一个栈帧即当前函数调用者的栈帧的ebp的值。有时候我们喜欢叫做old_ebp。通过当前函数的ebp可以获得当前函数的栈帧中存的当前函数的参数以及当前函数的局部变量。同时还可以通过ebp这个基准找到上一个函数即调用者函数的返回地址。 这就是为什么ebp被称为当前函数栈帧的基准。 上述的说的两段话很多人都听过也都知道。但是并不是所有人都知道如何使用ebp来定位其他的参数。下面我们看一下图 图二注在x8632位系统中栈中的最小存储单位一般是4字节。所以每次偏移都是直接偏移4字节 上图中就是我们使用ebp这个基准来找到函数栈帧中的其他参数的。ebp作为基准存储的是上一个栈帧的ebp值。ebp向上偏移4字节存储的是函数返回地址。这也是当前函数栈帧形的第一个存储的值但是一定不是函数发生调用时第一个入栈的后面会说用于在当前函数执行完之后能够返回到调用者的函数中继续执行ebp向下偏移4字节就开始存储一些需要保存的寄存器的值。这些寄存器的值往往是调用者在之前执行的时候可能正在使用但是现在突然跳转到其他函数执行被调用的函数可能也需要使用一些调用者之前正在使用的寄存器所以现在先要将那些寄存器压入栈中存起来等被调用函数执行完返回给调用者时再将其弹出好能够让调用函数能够继续正常执行。在往下偏移就是存储的当前运行函数的局部变量以及临时变量了。主意这里不是存储的函数参数函数参数在ebp往上偏移8字节处我们可以看到上面都是说的被调用者函数栈帧中内容。被调用者函数栈帧中并没有被调用者的参数。参数在哪里实际上参数存储在ebp往上偏移8字节处但是这个地方已经不属于当前函数的栈帧了而是属于调用者的栈帧。其实这里估计很多人不明白。我们要记住函数参数是存在于调用者的函数栈帧中而并非是存在被调用的函数的栈帧中。ebp8存储的是第一个参数ebp4(n1)位置存储的是第n个参数。ABI规范中还涉及到参数的入栈顺序后面还会说明。 我认为上述唯一需要注意的就是函数的参数是存储在调用者的函数栈帧中而不是被调用函数的函数栈帧中。这一点在面试中也有问到问你发生函数调用时是函数参数先入栈还是返回地址先入栈?乍一看以为返回地址是在函数栈帧的第一个位置就以为是函数栈帧先入栈实际上是错的。发生函数调用时函数的参数先入栈只不过入的栈是属于调用者的栈而已。 1.2 Linux系统中的栈帧布局 中所周知一般来说栈的增长方向是向下的下面就给出一个图表示在Linux系统下的栈帧的布局。由于与上线的中文的图几乎一样只是画反了这里就不再用过多的语言来描述下图 图三2 函数调用时的‘前言’和‘后序’ 上一节内容我们很清晰的认识了函数栈帧的结构以及函数栈帧中的重要的寄存器ebp的作用。下面就来详细说说函数发生调用时具体的一些细节操作。我们先说调用过程中的一些细节后面再给出具体的代码案例。看过代码案例再结合回来看就基恩完全掌握了函数栈帧的作用了。 2.1 函数调用时发生的细节操作 函数调用时发生的细节操作 调用者一般通过call指令调用函数调用的函数有参数的话先将参数以某种顺序压入到调用者的函数栈帧中然后将返回地址压入栈中。从这个返回地址开始往后就是新的被调用的函数的函数栈帧了。函数所需要的栈帧的空间大小首先肯定是由编译器计算出来了此时函数栈的大小已经是一个固定值是一个字面常量了所以函数栈帧的大小是固定的。函数结束时leave指令恢复上一个栈帧esp和ebp的值。函数返回时ret指令将返回地址恢复到eip寄存器即PC指针寄存器。 上一面的leave和ret可能还没讲明白它们主要表现为下面的具体行为 我们来解释一下上面几条指令的矩形行为 move ebp esp 。是将ebp这个ebp是当前函数的ebp它存的是上一个函数栈帧的ebp的值赋值给esp。也就是说此时esp存的是上一个函数栈帧调用者的函数栈帧的ebp的值pop ebp。是将栈顶指针也就是esp指向的值上面第一步的操作导致现在esp的值存储的是调用者的old_ebp的值弹出给ebp寄存器。这一步操作完此时ebp寄存器存的是调用者的基准了不再是被调用函数的基准了。同时还需要注意在发生pop之后esp就会向上偏移4字节此时esp就是指向返回地址的存储地址了看上面函数栈帧的结构。pop eip 。 是将当前栈顶也就是esp指向的值由上两步知此时esp指向的值返回地址也就是调用者当时发生函数调用时压入的下一条即将要执行但是却因为发生函数调用而没有执行的指令的地址弹出给eip寄存器。而eip寄存器的主要作用是它保存的永远是CPU下一次即将要执行的指令的地址。 刚刚好此时eip保存就是调用者当时发生函数调用时压入的下一条即将要执行但是却因为发生函数调用而没有执行的指令的地址那么顺理成章CPU开始执行这条指令我们又返回到了调用者开始继续执行函数。 2.2 函数调用时的前言和后序 什么是前言什么是后序 前言 函数发生调用时总会保存调用者之前正在使用的一些通用寄存器的值为了能够在函数调用返回时调用者能够继续正常执行程序。这个保存这些寄存器的值就是前言。 后序 如上所说函数调用返回时会把之前在前言的过程中保存的寄存器的值给pop出来好让调用者继续正常执行程序。这就是后序。 前言和后序的具体汇编上的行为大概就是下面表格中所列的一些行为 图四其中push的操作就是保存寄存器的值。如果不理解上面的指令那还需要加强一下汇编指令的学习。参考我其他的文章。 3 函数栈帧结构的实际代码案例分析 3.1 代码 #include stdio.h#define PRINT_STACK_FRAME_INFO() do \ { \char* ebp NULL; \char* esp NULL; \\\asm volatile ( \movl %%ebp, %0\n \movl %%esp, %1\n \: r(ebp), r(esp) \); \\printf(ebp %p\n, ebp); \printf(previous ebp 0x%x\n, *((int*)ebp)); \printf(return address 0x%x\n, *((int*)(ebp 4))); \printf(previous esp %p\n, ebp 8);//调用者函数栈帧最后一个值也就是被调用者函数栈帧的第一个参数 \printf(esp %p\n, esp); \printf(ebp %p\n, ebp); \printf(esp %p\n, esp); \ } while(0)void test(int a, int b) {int c 3;printf(test() : \n);PRINT_STACK_FRAME_INFO();//打印test函数的函数栈帧信息printf(a %p\n, a);printf(b %p\n, b);printf(c %p\n, c); }void func() {int a 1;int b 2;printf(func() : \n);PRINT_STACK_FRAME_INFO();//打印func函数的函数栈帧信息。printf(a %p\n, a);printf(b %p\n, b);test(a, b); //func函数中发生函数调用 }int main() {printf(main() : \n);PRINT_STACK_FRAME_INFO(); //打印main函数的函数栈帧信息func(); //main函数中发生函数调用。return 0; }先将该函数编译运行得到结果再慢慢分析 3.2 分析函数栈帧的形成与摧毁 下面我们来根据上述代码的运行结果来分析上述代码中的函数栈帧结构的形成过程与摧毁过程 3.21 函数栈帧的形成 上述代码比较简单可以看出函数的调用关系为main— func—test 首先在程序运行起来后内存中只有main函数的函数栈帧。这里我们忽略main函数的参数并且main函数也没有局部变量这里就不看main函数的函数栈帧。 func函数中有两个局部变量a和b。首先开始构建func函数的函数栈帧。如下图 图五-func函数栈帧的形成图注释上面图中main函数的.text段说法有误应该是text段中的main函数即将要执行的下一条指令 上面的func函数栈帧行程图中已经标示的非常详细可以对比文章前面的图一与上面frame.c程序的运行结果看看各个地址值是否正确。当然你自己运行的结果的各个值有可能与我的不一样。可以自己画图看看。 上述我只是给出了最终形成的func函数栈帧具体的形成过程其实也很简单 main函数使用call指令调用函数func因为没有传参数给func函数所以就没有入参这一步骤首先将main函数下一条即将要马上执行的指令的地址压入栈中如上图的的return address然后将main函数栈帧的ebp的值注意不是ebp对应内存存的值而是ebp本身的值 压入栈中此时这个地址就是func函数栈帧的ebp的值。在将main函数的ebp的值压栈后就会将此时的存main函数栈帧ebp的地址也就是上图的0xbfa370b8这个值赋值给ebp寄存器。此时的ebp的值立马就变了。然后就是将main函数可能正在使用的通用寄存器压栈保存。这里到底是谁来做的实际上是在当初编译程序的时候由于函数栈帧的大小就已经由编译器计算出来了编译器也就生成了一些指令这些指令负责此时的寄存器的压栈操作。注意这些动作实际上都可以说成是编译器的行为。虽然编译该程序是在执行这些动作之前完成的但是毕竟那些指令是编译器产生的。然后保存func函数的局部变量。如上图5 然后保存的是一些其他信息。这些其他信息一直没有搞明白的是什么。这次看得出来保存的有ebp的值。至于为什么保存它目前我还没有详细研究。有待考证。 因为func函数在最后调用了test()函数并且test函数有参数所以在调用test函数时test函数的函数栈帧开始形成形成过程与上述的func函数的形成过程类似。形成后test函数的函数栈帧大致如下图所示: 图六-test函数栈帧的形成图下面大致说一下上面的test函数中的栈帧形成的过程。 在func函数中调用test函数func使用call指令调用test函数。因为func给test函数传了参数ab。在Linux系统中ABI规范参数入栈顺序是从右向→左所以先将参数入栈且参数b先入栈a再入栈。然后将func函数本身接下啦要执行的指令的地址压入栈中。然后将func函数的ebp值的地址也就是0xbfa370b8压入栈中此时test函数栈帧中的ebp就指向这个值。然后压入一些寄存器比如func函数使用的通用寄存器需要保存起来。可以看到teat栈帧中寄存器的大小占8字节很有可能是因为func函数有两个局部变量所以func函数使用了两个通用寄存器此时都需要保存起来。然后就是将test函数的局部变量c压入栈中。然后将ebp这个地址压入栈中以方便找到当前函数栈帧的ebp在哪。实际上这里我也不太明白为什么还要将ebp这个地址压入栈毕竟当前的寄存器EBP肯定已经存的就是这个地址了。如果有其他信息还需要压入其他信息的。 3.22 函数栈帧的摧毁 上面一小节讲了函数栈帧的形成。当函数执行完之后相应的函数栈帧就会销毁。下面我们来看看函数栈帧是如何销毁的 由上线的fram.c代码知道函数的调用时main—func—test 那么当test函数执行完之后就会返回到func函数中继续执行返回到func函数后test的栈帧就销毁了。那么如何从test栈帧返回到func栈帧如下图 不知是否还记得上面讲过这些指令的意思不记得的话最好回去看看。那么在函数结束并且返回就是执行上述指令的。 test函数栈帧的摧毁过程 move ebp, esp 就是将ebp存的值也就是0xbfa37088赋值给esp寄存器。那么现在由于esp的值变了如下图 图七-test函数栈帧的摧毁一pop ebp 就是将当前栈顶指针指向的值也就是0xbfa370b8弹出并赋值给ebp寄存器此时ebp等于0xbfa370b8它所在位置存储的是func函数的ebp值。并且esp指针向上偏移4字节。如下图 图七-test函数栈帧的摧毁二此时由于esp已经指向了func函数的栈帧的顶部ebp也是func函数栈帧的ebp了。所以此时test函数栈帧就差一步就要摧毁了。看下一步 pop eip 就是将当前栈顶指针指向的值弹出并赋值给eip寄存器。同时esp向上偏移4字节。如下图 图七-test函数栈帧的摧毁三注意上面的eip寄存器的用处eip保存的始终是CPU即将要执行的指令地址。所以此时保存的是func函数中某一条指令的地址此时开始在func函数中执行。 好了现在test函数栈帧已经摧毁。并且也返回到了func函数中执行。那么就回到了下图的样式 注意上面的esp指向的应该是test函数的参数这里没有显示出来。 func函数栈帧的摧毁过程 现在回到func函数中执行由于此时func也是最后一条指令func函数也要结束并返回了。 同样是需要先执行move ebp esp。得到如下样式的栈帧图 然后执行pop ebp指令得到如下图所示: 最后执行ret指令pop eip 。得到如下图 好了最终func函数也返回结束了接下来就指向下main函数的栈帧了 至于main函数栈帧的摧毁与上线两个一样。只不过main函数的返回是返回给操作系统的了。这里就不再赘述。 4 总结 本文学习起来异常艰难但是学会了受益匪浅。 本文使我学会了以下 栈帧是函数调用时形成的链式内存结构ebp是构成栈帧的核心基准寄存器深入掌握了函数栈帧的形成与摧毁 欢迎加我好友共同探讨学习交流欢迎指正文章中的错误

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

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

相关文章

详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)

详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; fon…

中国建设银行的网站地产网站开发公司

以文章multi-label learning from single positive为例; 手动print打印出你不懂的地方;把不会的地方单独拎出来,重新创建一个小文件单独运行;问神奇的chatgpt,github上有个学术型chatgpt可以帮你读懂项目代码&#xf…

榆林做网站的公司电话遵义交通建设网站

神经网络的三个特征是层次结构、权重共享和非线性激活函数。 层次结构:神经网络由多个层组成,包括输入层、隐藏层和输出层。这种层次结构使得神经网络能够逐层提取数据的特征,并且通过调整每一层的权重来学习数据的表征。 权重共享&#xff…

中小企业商务网站建设如何建英文网站

dc3 windows 本地搭建步骤: ​​ 必要软件环境 进入原网页# 务必保证至少需要给 docker 分配:1 核 CPU 以及 4G 以上的运行内存! JDK : 推荐使用 Oracle JDK 1.8 或者 OpenJDK8,理论来说其他版本也行; Maven : 推荐…

江西做网站的公司有哪些网站改标题

1、Struts2和SpringMVC的区别(1)设计理念:前者为有状态的Action(均为多例),Action对象属性字段承载请求、响应,后者一般为无状态的Controller,请求直接封装到方法的参数中;(2)集中访问点不同:都属于前端控制…

郑州做网站公司有多少12380网站建设情况总结

一、环境准备 安装 JDK 1.7 二、下载 Nexus 压缩文件 下载地址:http://www.sonatype.org/nexus/archived/ 三、上传压缩文件 四、解压缩文件 五、配置环境变量 1、临时配置 mvn clean package -Dmaven.test.skiptrue -Dmaven.javadoc.skiptrue命令:expor…

购物网站开发项目意义企业融资贷款

数学规划 (最速下降法,c语言编程).doc数 学 规 划 课 程 设 计题目:用最速下降法求解无约束非线性规划问题姓名:学号:成绩:2011年6月用最速下降法求解无约束非线性规划问题摘要:无约束非线性规划问题是一类重要的数学规…

企业如何选择适合自身行业的ERP系统?

企业如何选择适合自身行业的ERP系统?在整理企业信息化选型资料时发现,不同行业因业务模式、管理重点和流程特点的差异,对ERP系统的需求存在显著区别。例如,离散制造企业关注生产排程与物料齐套,商贸流通类企业更重…

Rust 登堂 之 Sized和不定长类型 DST(七) - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Screaming Architecture:让架构自己说话

什么是 Screaming Architecture? "Screaming Architecture"(呐喊架构)是由著名软件架构师 Robert C. Martin(Uncle Bob)提出的一个概念。这个概念的核心思想是:一个好的软件架构应该能够清晰地表达出这…

BOE(京东方)携手UNESCO联合主办WCBR“科学十年”分会 彰显中国科技企业可持续发展实力

9月22日,第五届世界生物圈保护区大会(简称WCBR)在杭州隆重开幕,这不仅是世界生物圈保护区大会第一次在中国举办,也是首次在亚太地区举办。作为联合国教科文组织(UNESCO)“人与生物圈计划”体系内规模最大、覆盖…

使用Cyclops.PdfKit根据pdf模板生成pdf文件

一、技术准备环境配置依赖库安装:使用NuGet包管理器集成Cyclops.PdfKit组件(最低要求.NET 6 SDK) dotnet add package cyclops-pdfkit --version 2.3.1 注意:生产环境建议锁定版本号以避免兼容性问题模板预处理:…

不关闭网站 备案seo搜索引擎官网

一、实验目的 熟练运用Python运算符。熟练运用Python内置函数。掌握Python的基本输入输出方法。了解lambda表达式作为函数参数的用法。掌握列表、元组、字典、集合的概念和基本用法。了解Python函数式编程模式。 二、实验内容: 1. 在命令模式测试如下命令&#x…

帮人做彩票网站淘特app推广代理

因为苹果后台的调整,电脑端的自签工具 Cydia Impactor 一直无法使用,如今虽然没有等到大胡子对 Cydia Impactor 适配更新,却等到了全新的替代工具。先说下为什么 Cydia Impactor 为什么让那么多人惦记,虽然对于不越狱安装越狱工具…

陕西省建设集团公司网站网站标题与关键词

服务器能运行什么应用 服务器是一种应用范围很广的网络技术产品,它在影视、视频以及医疗和金融等多个领域,都可以发挥使用价值,那么服务器能运行什么应用?大家跟着壹基比小鑫一起来了解吧! 服务器的作用是什么? 服…

微信公众号和微网站个人域名免费网站

光伏EPC项目管理系统是一种适用于工程项目的管理软件,它强调在整个项目周期中的综合性管理理念,涵盖了从规划、设计、采购、施工到交付等全过程,帮助用户实现高效的项目管理。 1.增强项目团队之间的协作与沟通:光伏EPC项目管理系统…

新网站如何才做被百度收录网站上线倒计时页面

在我们之前的基础篇中,我们已经初步了解了DSL的架构与基础结构。现在,我们将进一步学习DSL的查询语句,这些查询语句对于我们的工作和学习而言至关重要。 DSL(Domain Specific Language)是一种专门用于特定领域的编程语言。在Elasticsearch(ES)中,DSL被广泛用于构建灵活…

做网站怎么安装数据库网站建设属于什么专业

一:连接 1:本地连接 mysql -u用户名 -p密码 2:连接远程服务器 mysql -u用户名 -p密码 -hip地址 -P端口号 线下修改远程服务端上部署的mysql服务器 二:创建数据库 create database 名字 utf8; 三:显示数据库 show datab…

免费企业查询网站时代设计网 新网站

Lightbend最近对2000多个JVM开发人员进行了调查,结果刚刚发布。 开展该调查的目的是发现:发展趋势与IT基础设施趋势之间的相关性,处于数字化转型前沿的组织如何使他们的应用程序现代化以及当今对新兴开发人员技术最为关注的实际生产使用情况细…