前言:本文旨在从汇编代码的角度出发,分析函数调用过程中栈帧的变化。

栈帧的简单介绍:

当某个函数运行时,机器需要分配一定的内存去进行函数内的各种操作,这个过程中分配的那部分栈称为栈帧。下图描述了栈帧的通用结构。栈帧是一段有界限的内存区间,由最顶端的两个指针界定,寄存器%ebp为帧指针,而寄存器%esp为栈指针(也就是说寄存器%ebp保存了所分配内存的最高地址,寄存器%esp保存了所分配内存的最低地址)。当程序执行时,栈指针(栈顶)可以移动,因此大多数信息的访问都是相对于桢指针的。

函数调用前:

在函数被调用之前,调用者会为调用函数做准备,具体来说就是传参。准备被调用函数需要的参数。就上图而言,当前帧是为调用函数而开辟的栈帧,而参数1~参数n就是调用者传给被调用的参数,在当前帧中体现为设置参数构造区域。我们可以看到在当前帧上面仅挨着的是一个返回地址,这个返回地址是什么?
        调用者在调用完函数以后,肯定需要从下一条指令接着执行。而这个地址就是下一条指令的地址。调用函数的指令为:

call <函数名>。这里的call就实现了下一地址的储存。具体来说,call指令执行时,先把下一条指令的地址入栈,再跳转到对应函数执行的起始处。
        再举个具体的例子,如下图:


                
        我们看一下主函数调用sum()函数的过程:

我们看到<+37>这一行再调用sum()函数。我们先看之前的几行:先设置变量值,再设置参数构造区,最后再调用函数。虽然这只是一个简单的例子,但是对于绝大数的调用来说,都符合这个规则,就是在调用函数前的几行,做的事情就是构造函数的参数。设置完成后再执行call。执行call的时候,先把下一条指令的地址入栈,再跳转。
被调用函数运行时:

这里直接对代码进行说明。我们先查看sum函数的汇编代码:

我们看到函数的前两行:

push    %ebp

mov     %esp,%ebp

对于绝大部分函数来说,前面的两行都跟这两行一样,我们来具体分析这两句干了什么:

首先是 push    %ebp。当前%ebp保存的是调用者栈帧的栈底地址,那么push    %ebp就是将调用者栈帧的栈底地址压入栈,即保存旧的%ebp。

接着是mov    %esp,%ebp。我们刚刚把旧的%ebp的值保存了下来,但是%ebp值并没有发生改变,而我们现在在执行一个新的函数,那么%ebp保存的应该是新的栈帧的栈底。所以才把当前%esp储存的地址赋值给%ebp。(这里说明一下,对于每一次push操作,%esp储存的地址会-1)。那么这样一来的话,相当于调用者栈帧的栈顶现在作为了新的栈帧的栈底(并且该栈底保存的是调用者栈帧的栈底地址,请记住这一点)。而此时新的栈帧的栈底和栈底位于同一个位置。

而我们在函数里面是要执行各种操作的,所以我们需要给新栈帧分配一定的内存。这也就是后面接着的:sub $0x10,%esp。将%esp低地址移动16个字节。有了这么多的储存空间,才能支持函数里面的各种操作(也就是图中所述)。其实在这之前,还可能有一些push 语句,比如push %ebx之类的。这些push操作的目的其实同push %ebp差不多,都是保存调用者的值,以便在函数运行完以后再恢复数据。

再接着就是利用储存空间执行具体的操作了。最后说明一点的是,函数的返回值一般储存在%eax寄存器中。这个看上述代码也可以看出来:在执行sum()函数时,最后操作完的结构储存在%eax寄存器中。在执行完sum()函数以后,后面<+45>那一句就是将%eax寄存器的值赋值给主函数中的一个参数。

被调用函数运行结束时:

这里主要对:leave和ret指令进行分析。

首先是leave指令:在许多的地方都可以找到它的解释:用leave指令可以使栈做好返回的准备,它等价于下面的代码序列:

movl    %ebp    %esp

popl    %ebp

然而当时我看了之后还是不是很理解,所以这里再进行进一步解释。

首先是movl    %ebp    %esp。当前%ebp保存的是什么?没错,当前栈帧的栈底地址,所以这一句话的作用就是把%esp给放回到调用者栈帧的栈底。联系到进入函数时的语句movl    %esp    %ebp,其实这就是个逆过程,旨在恢复原来栈顶的状态。

然后是popl    %ebp。popl是对栈顶元素进行出栈,而现在的栈顶(也是栈底)储存的是什么呢(上面请大家记住的东西就派上用场了),储存就是调用者栈帧的栈底地址。popl    %ebp就是把这一地址赋值给%ebp(其实这个也可以看作push    %ebp的逆过程),所以这一句话就是恢复调用者栈帧的栈底。这样一来的话调用者栈帧就基本上是恢复到原来的状态了。
        然后呢,显然上面也说了leave只是做好返回的准备。准备什么呢,我们调用完函数以后,调用者还需要接着向下执行指令,那么调用完函数以后就应该跳转到该函数的下一条指令的地址。这么跳转?还记得我们的call指令吗--先将下一条指令的地址入栈,然后跳转。这里ret的作用就是把哪一个地址给弹出栈,并且跳转到地址对应的语句,再接着执行,这样以来一个函数就完整地运行结束了。
        对照最开始地那个结构图来说,这两条语句地作用就是:(leave指令)先将栈指针%esp移动到桢指针%ebp,然后把被保存地%ebp赋值给寄存器%ebp(此时%esp+1,指向返回地址)。(ret指令)然后把返回地址出栈,并跳转到返回地址对应地指令。

总结:

函数调用过程中的栈帧变化是学习汇编是必须掌握的一个知识点,当时老师将的时候没有听得特别认真。所以线下自己去弄懂还花了点时间的。对于不熟悉的人来说,这个过程可以一遍两遍还是不怎么理解,但是只要多去想想,查查资料,问问同学,还是可以弄懂的。
————————————————
版权声明:本文为CSDN博主「AC-NEWBIE」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xbb224007/article/details/80106961

函数调用过程中的栈帧结构及其变化相关推荐

  1. 汇编中函数调用过程中,栈到底是怎样变化的?call、ret、指令分别有什么样的作用?

    1.栈帧的结构:栈帧主要包括三个部分:被保存的%bp,被保存的寄存器.本地变量等,参数区域 2.call指令的作用: 将程序下一条指令的位置的IP压入堆栈中: 转移到调用的子程序 3.ret指令的作用 ...

  2. 函数调用过程中函数栈详解

    当进程被加载到内存时,会被分成很多段 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写,如果发生写操作则会提示segmentation fault 数据段:保存初始化的全局变量和静 ...

  3. 过程(栈帧结构是干货)

    [0]写在前面 过程(栈帧结构是干货):本文总结于csapp, 加上自己的理解: [1]栈帧结构 每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息. 过程调用:函数调用另 ...

  4. C++ 函数调用过程中栈区的变化——(栈帧、esp、ebp)

    C++ 函数调用过程中栈区的变化 1.C++ 函数调用过程中栈区的变化 1.1.程序的内存分布 1.2.函数调用过程中栈的变化解析 参考 1.C++ 函数调用过程中栈区的变化 1.1.程序的内存分布 ...

  5. 11.JDK8内存模型、本地方法栈、虚拟机栈、栈帧结构(局部变量表、操作数栈、方法出口、虚拟机栈与本地方法栈的关系、寄存器、方法区、堆(Heap)、jvm中的常量池、Metaspace(元空间))

    11.JDK8内存模型 11.1.本地方法栈(Native Method Stacks) 11.2.虚拟机栈(Java Virtual Machine Stacks) 11.3.栈帧结构 11.3.1 ...

  6. java 参数类型不确定_详细解析Java虚拟机的栈帧结构

    什么是栈帧? 正如大家所了解的,Java虚拟机的内存区域被划分为程序计数器.虚拟机栈.本地方法栈.堆和方法区.(什么?你还不知道,赶紧去看看<Java虚拟机内存结构及编码实战>)这次要介绍 ...

  7. java虚拟机栈帧_Java虚拟机,运行时栈帧结构

    业余生活要有意义,不要越轨.--华盛顿 引导语 "虚拟机"是一个相对于"物理机"的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器. ...

  8. JVM007_运行时栈帧结构

    运行时栈帧结构 执行引擎是JVM的核心组件之一. 所有Java虚拟机的执行引擎输入输出都是一致的:输入的是字节码二进制流,处理过程是字节码解析执行的等效过程,输出的是执行结果. JVM以方法作为作基本 ...

  9. Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

最新文章

  1. Linux基础入门 | 目录结构
  2. C 关于使用异或运算操作概述
  3. c++ 弹出菜单在固定的位置_固定资产管理软件操作手册(资产维修)
  4. 【Linux网络编程】无连接和面向连接协议
  5. zend studio 9实用快捷键大全 分享ZEND STUDIO 9的常用快捷键,高亮显示相同变量。...
  6. linux cpio(copy in/out) 命令详解
  7. py程序员写代码的习惯养成 防止想到什么写什么
  8. Wordpress空间和域名的备份
  9. 一把误操作卖出500万股,TCL科技李东生致歉:收益归公司
  10. 2021年中国地下光缆市场趋势报告、技术动态创新及2027年市场预测
  11. Linux环境安装redis
  12. 跟踪算法原理_「综述」目标跟踪算法
  13. SmtpClient SSL 发送邮件异常排查
  14. DC888 : worklist slovers
  15. P1196 [NOI2002] 银河英雄传说
  16. 记事本html特效,很漂亮的网页飘落特效代码
  17. html中bottom的属性,css中bottom是什么意思?
  18. XGBoost 与 信用卡诈骗数据集 三
  19. 差影-Variation Model
  20. python中如何使用sin、cos等三角函数

热门文章

  1. outlook服务器协议,:sina如果用OUTLOOK发邮件,请问在服务器器,协议里都该? 爱问知识人...
  2. 产品规格说明书的测试----《software testing》
  3. PPT中实现长图片滚动条显示!(详解)
  4. vue element-ui 自定义规则 限制图片尺寸
  5. python 图片拼接_使用Python拼接多张图片
  6. 基于Yolov5的二维码QR码识别
  7. APP跳转高德地图并携带参数
  8. 百度热议新增网易虎扑网站作为展现内容
  9. 马蜂窝数据方向面试总结
  10. 手动GHOST安装系统方法教程图解