从汇编看函数调用

news2024/11/19 23:33:47

文章目录

    • 函数调用流程
    • 栈相关寄存器及的作用简介
      • 寄存器功能
      • 指令功能
    • 函数的括号{}
      • 正括号
      • 反括号
      • 函数调用
      • 函数内部处理
    • 参数传递
        • 传值,变量不可改
        • 传指针,变量可改
        • C++ 传引用

函数调用流程

目标:函数调用前后栈保持不变
在这里插入图片描述

  1. 保存main函数的寄存器上下文
  2. 移动栈指针,到新栈
  3. 调用新函数:新函数会开辟内存然后操作
  4. 恢复栈指针

栈相关寄存器及的作用简介

寄存器功能

在这里插入图片描述ESP/RSP:堆栈指针寄存器,指向栈顶。栈顶指针
EBP/RBP:栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量

rax:通常用于存储函数调用返回值
rdi:第一个入参
rsi:第二个入参
rdx:第三个入参
rcx:第四个入参
r8:第五个入参
r9:第六个入参

寄存器ebp作为当前函数的“栈帧”基地址,配合一定的偏移,就可以读、写函数体的临时变量。如果一个变量是通过ebp寄存器间接访问的,那么它往往是临时变量,也叫“栈”变量。

指令功能

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

push rbp 保存上下文,保存rbp值
1.rbp里面的值放到当前rsp指向的位置,保存当前栈底指针的值
2. 然后rsp–,栈顶指针向上移动

pop eax 恢复栈帧
1. 栈顶指针向下移动,这里的值保存的是原函数的栈底位置
2. ebp指向esp里面值的位置,移动栈底指针到原函数位置

在这里插入图片描述
call
1. 会把call指令的下一条指令压入栈,把下一条指令的地址,也就是函数func的返回地址(0x401105e)压入堆栈
2.CPU跳转到函数func的首地址。
在这里插入图片描述

函数的括号{}

正负括号都对应三条指令
在这里插入图片描述

正括号

先看正括号,三条指令,作用是保存原栈,并分配新的栈空间

  1. push指令把CPU寄存器ebp的值压入“栈顶”,然后将“栈顶”红色水位线(CPU寄存器esp)上移,扩大栈空间。至此,main函数的“栈帧”保护工作完成!
  2. 然后通过mov指令,更新一下“栈帧”基准线,让ebp指向esp,这里就是新的func的栈了
  3. 然后是sub指令为临时变量a和b开辟空间
    在这里插入图片描述

在这里插入图片描述

反括号

然后看反括号三条指令:反括号作用是恢复栈

  1. 通过mov指令,把红色水位线(寄存器esp)先降低到蓝色基准线(寄存器ebp)的位置,释放调用函数func栈
  2. pop, 把事先压入“栈顶”的ebp值返还给CPU寄存器ebp。这样蓝色基准线就恢复到了最开始的位置。然后esp红色水位线也随之下降。esp和ebp的值就都恢复了。
  3. ret指令,把“栈顶”处的返回值传给CPU寄存器rip,这样,CPU就可以跳转到主调函数main被打断的地方0x401105e继续执行了。同时,随着“栈顶”的下降,红色水位线也随之下降。这样,红、蓝两条线都恢复到了最开始的位置
    在这里插入图片描述

在这里插入图片描述

栈是存储临时数据的区域,在普通内存中,它的特点是通过push指令和pop指令进行数据的存储和读出。往栈中存储数据称为“入栈”,从栈中读出数据称为“出栈”。32位x86系列的CPU中,进行1次push或pop,即可处理32位(4字节)的数据。push指令和pop指令中只有一个操作数。该操作数表示的是“push的是什么及pop的是什么”,而不需要指定“对哪一个地址编号的内存进行push或pop”。
这是因为,对栈进行读写的内存地址是由esp寄存器(栈指针)进行管理的。push指令和pop指令运行后,esp寄存器的值会自动进行更新(push指令是-4, pop命令是+4),因而程序员就没有必要指定内存地址了。

栈是由大地址向小地址递减,而堆和普通内存是小地址到大地址递增

操作系统会为每个任务(进程或线程)分配一段内存当作任务“堆栈”;CPU则提供两个寄存器esp、ebp,用来标识当前函数对“堆栈”的使用情况。随着函数的逐层调用,函数的“栈帧”会逐次堆叠,互不重合;随着函数的逐层返回,函数的“栈帧”会被就地放弃,但不会清理内存

函数调用

    //返回两个参数值之和的函数
    int AddNum(int a, int b)	
    {
        return a + b;
    }
    //调用AddNum函数的函数
    void MyFunc()
    {
        int c;
        c =AddNum(123, 456);
    }

c =AddNum(123, 456);
在这里插入图片描述1. (1)、(2)、(7)、(8)的处理适用于C语言中所有的函数,我们会在后面展示AddNum函数处理内容时进行说明
2.(3)和(4)表示的是将传递给AddNum函数的参数通过push入栈。在C语言的源代码中,虽然记述为函数AddNum(123,456),但入栈时则会按照456、123这样的顺序,也就是位于后面的数值先入栈。这是C语言的规定。
3. (5)的call指令,把程序流程跳转到了操作数中指定的AddNum函数所在的内存地址处。在汇编语言中,函数名表示的是函数所在的内存地址。这时就回去处理AddNum函数。
4. AddNum函数处理完毕后,程序流程必须要返回到编号(6)这一行。call指令运行后,call指令的下一行((6)这一行)的内存地址(调用函数完毕后要返回的内存地址)会自动地push入栈。该值会在AddNum函数处理的最后通过ret指令pop出栈,然后程序流程就会返回到(6)这一行。

函数内部处理

在这里插入图片描述

  1. (1)(2)和(5)(6)是每个函数都会使用的,我们来详细解释一下
  2. ebp寄存器的值需要保存一下,所以在(1)中入栈,在(5)中出栈恢复到函数调用前的状态。这主要是为了把函数中用到的ebp寄存器的内容,恢复到函数调用前的状态。为此,我们就需要将其暂时保存在栈中,然后再在函数处理完毕之前出栈,使其返回到原来的状态。
  3. (2)中把负责管理栈地址的esp寄存器的值赋值到了ebp寄存器中,不允许使用esp寄存器,这里就采用了不直接通过esp,而是用ebp寄存器来读写栈内容的方法。
  4. (3)是取[ebp+8]指定栈中的地址中存储的第1个参数123,并将其读出到eax寄存器中。像这样,不使用pop指令,也可以查看栈的内容。而之所以从多个寄存器中选择了eax寄存器,是因为eax寄存器是负责运算的累加寄存器。
  5. 通过(4)的add指令,把当前eax寄存器的值同第2个参数相加后的结果存储在eax寄存器中。[ebp+12]是用来指定第2个参数456的。在C语言中,函数的返回值必须通过eax寄存器返回,这也是规定。不过,和ebp寄存器不同的是,eax寄存器的值不用还原到原始状态。至此,我们进行了很多细节的说明,其实就是希望大家了解“函数的参数是通过栈来传递,返回值是通过寄存器来返回的”这一点。
  6. (6)中ret指令运行后,函数返回目的地的内存地址会自动出栈,据此,程序流程就会跳转返回到代码清单10-4的(6)(Call _AddNum的下一行)。

参数传递

先看下传递参数的汇编:
在这里插入图片描述

  1. 传值调用和传指针其实都是将值传递到函数中,只不过这个值含义不同指针是一个地址的值。
  2. 还可以看出用作传参的寄存器是哪几个。
传值,变量不可改

我们接着看函数中,对参数赋值的汇编:
在这里插入图片描述1. 这里会将参数寄存器中的值,放入栈中。然后释放参数寄存器。
2. 然后将内存地址数据赋值。
3. 这也就说明原来参数的值被复制了一份到内存中,修改当前形参的值,实际是修改栈中内存的值,原变量不会被修改

传指针,变量可改

在这里插入图片描述

  1. 首先还是将参数的值放入内存中,释放寄存器
  2. 然后将参数x的内存地址传给寄存器,寄存器当前存储的是该地址
  3. 然后向该寄存器中存储的地址中,写入0.
    这也就直接修改了内存中原变量的值,这里的寄存器rax起到了一个中间过渡作用。

Q:为什么传递参数是通过CPU寄存器,而不是直接压入堆栈呢?
A:传递参数,也可以不通过CPU寄存器,而通过压入堆栈的方式,一些老版本的编译器,也是如此操作的。但通过寄存器传递,可以避免一些内存操作,一定程度上有利于提高函数的执行效率。

C++ 传引用

C++ 传引用和传指针的汇编相同,所以传引用只是一个语法糖
在这里插入图片描述

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

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

相关文章

爬虫实战三、PyCharm搭建Scrapy开发调试环境

#一、环境准备 Python开发环境以及Scrapy框架安装,参考:爬虫实战一、Scrapy开发环境(Win10Anaconda)搭建 PyCharm安装和破解,参考:爬虫实战二、2019年PyCharm安装(激活到2100年) …

【机器学习】“强化机器学习模型:Bagging与Boosting详解“

1. 引言 在当今数据驱动的世界里,机器学习技术已成为解决复杂问题和提升决策制定效率的关键工具。随着数据的增长和计算能力的提升,传统的单一模型方法已逐渐无法满足高精度和泛化能力的双重要求。集成学习,作为一种结合多个学习算法以获得比…

WPS二次开发专题:如何获取应用签名SHA256值

作者持续关注WPS二次开发专题系列,持续为大家带来更多有价值的WPS开发技术细节,如果能够帮助到您,请帮忙来个一键三连,更多问题请联系我(QQ:250325397) 在申请WPS SDK授权版时候需要开发者提供应用包名和签…

如何处理Jenkins打包npm install没有拉取到最新依赖的问题

问题背景: 我们项目中有私有依赖包 frame,是私有服务器上通过 npm 去管理。frame包 publish 之后,通过Jenkins打包时,npm install 一直没有拉取最新的代码。 思考:通过在本地直接替换 node_modules 里的 frame 包&…

推理端框架简介 高通SNPE 神经网络处理引擎 阿里巴巴 MNN 腾讯 FeatherCNN 端侧推理引擎 卷积计算优化 卷积计算优化

高性能计算(High performance computing, 缩写HPC) 指通常使用很多处理器(作为单个机器的一部分) 或者某一集群中组织的几台计算机(作为单个计 算资源操作)的计算系统和环境。 有许多类型的HPC 系统,其范围从标准计算机的大型集群,到高度专用的硬件。 大多数基于集群的H…

复现带积分柱状图+多个分类注释

Original research: Tumor microenvironment evaluation promotes precise checkpoint immunotherapy of advanced gastric cancer - PMC (nih.gov) 补充文件位置:https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8356190/bin/jitc-2021-002467supp001.xlsx 原图 该…

【JavaSE】类和对象详解(下)

前言 面向对象程序的三大特性:封装、继承、多态~ 书接上回 类和对象(上)~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 封装 private public 快速生成可访问封装的方法 包…

29-控制流(下):iam-apiserver服务核心功能实现讲解

我们再来看下 iam-apiserver 中的核心功能实现。 这些关键代码设计分为 3 类,分别是应用框架相关的特性、编程规范相关的特性和其他特性。 应用框架相关的特性 应用框架相关的特性包括三个,分别是优雅关停、健康检查和插件化加载中间件。 优雅关停 …

尚硅谷2024最新Git企业实战教程 | Git与GitLab的企业实战

这篇博客是尚硅谷2024最新Git企业实战教程,全方位学习git与gitlab的完整笔记。 这不仅仅是一套Git的入门教程,更是全方位的极狐GitLab企业任务流开发实战!作为一应俱全的一站式DevOps平台,极狐GitLab的高阶功能全面覆盖&#xff0…

Python实现BOA蝴蝶优化算法优化卷积神经网络分类模型(CNN分类算法)项目实战

说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝴蝶优化算法(butterfly optimization algorithm, BOA)是Arora 等人于2019年提出的一种元启发式智能算…

云存储中常用的相同子策略的高效、安全的基于属性的访问控制的论文阅读

参考文献为2022年发表的Efficient and Secure Attribute-Based Access Control With Identical Sub-Policies Frequently Used in Cloud Storage 动机 ABE是实现在云存储中一种很好的访问控制手段,但是其本身的计算开销导致在实际场景中应用收到限制。本论文研究了…

Wireshark TS | HTTP 传输文件慢问题

问题背景 之前有几篇文章写过关于应用传输慢的问题,延用之前的老套话,应用传输慢是一种比较常见的问题,慢在哪,为什么慢,有时候光从网络数据包分析方面很难回答的一清二楚,毕竟应用的定义范围实在太广&…

蓝桥杯嵌入式学习笔记(9):RTC程序设计

目录 前言 1. RTC介绍 2. 使用CubeMx进行源工程配置 3. 代码编程 3.1 准备工作 3.2 进行bsp_rtc.h编写 3.3 进行bsp_rtc.c编写 3.4 main.c编写 3.4.1 头文件引用 3.4.2 变量声明 3.4.3 子函数声明 3.4.4 函数实现 3.4.5 main函数编写 4. 代码实验 5. 总结 前言 因本人备赛蓝…

企业微信企业主体变更认证介绍

企业微信变更主体有什么作用? 说一个自己亲身经历的事情,当时我在一家教育公司做运营,公司所有客户都是通过企业微信对接的。后来行业整顿,公司不得不注销,换了营业执照打算做技能培训,但发现注销后原来的企…

高斯消元详解

算法概述 高斯消元法是一个用来求解线性方程组的算法 那么什么是线性方程组呢? 线性:每个未知数次数都为1次方程组:多个方程,多个未知数。 (a1x1a2x2..anxnbn)x为一次的 当x是平方的时候就不是线性 简而言之就是有多个未知数&#xff…

STL是什么?如何理解STL?

文章目录 1. 什么是STL2. STL的版本3. STL的六大组件4. 如何学习STL5.STL的缺陷 1. 什么是STL STL(standard template libaray-标准模板库):是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 2. …

XSS 与 CSRF 攻击——有什么区别,如何加以防护

跨站脚本(XSS)和跨站请求伪造(CSRF),它们将恶意脚本注入目标系统,以进一步利用技术栈或窃取用户数据。 什么是 XSS 和 CSRF? CSRF和XSS都是客户端攻击,它们滥用同源策略,利用web应用程序和受害用户之间的信任关系。XSS和跨站脚…

特征增强自蒸馏卷积神经网络

目录 1.1 模型总体架构 1.2 特征增强金字塔模块 1.3 辅助分类器 1.1 模型总体架构 与自然图像相比,遥感场景图像地物较为复杂,具有类间相似度高和类内差异大的特点,这导致常用的网络模型难以有效学习遥感场景图像的表征特征。此外&#xf…

分布式链路追踪与云原生可观测性

分布式链路追踪系统历史 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure - Google Dapper,大规模分布式系统的跟踪系统大规模分布式系统的跟踪系统:Dapper设计给我们的启示 阿里巴巴鹰眼技术解密 - 周小帆京东云分布式链路追踪在金…

全面的Docker快速入门教程(详细)

前言: 都2024年了,你还在为了安装一个开发或者部署环境、软件而花费半天的时间吗?你还在解决开发环境能够正常访问,而发布测试环境无法正常访问的问题吗?你还在为持续集成和持续交付(CI / CD)工…