c/c++函数调用过程(结合汇编指令详细分析)

news/2025/1/17 14:12:59/文章来源:https://www.cnblogs.com/General-xd/p/18339588

函数调用分析

前置知识:

  • 全局变量:在函数内部定义的变量
  • 局部变量:在函数外部定义的变量
  • esp:存储当前函数栈底的地址
  • ebp:存储当前函数栈顶的地址

对于函数形参(实际上):

  • 简单:cpu寄存器中
  • 复杂:栈中开空间

函数调用机制:

  • 局部变量占用的内存是在程序执行过程中“动态”地建立和释放的。这种“动态”是通过栈,由系统自动管理进行的,当任何一个函数调用发生时,系统都要做以下工作:
    1. 建立栈帧空间
    2. 保护现场:主调函数运行状态和返回地址入栈
    3. 为被调函数传递数据(进行实参和形参的结合),同时形参获得存储空间,接着给局部变量分配空间
    4. 执行被调函数函数体
    5. 当被调函数执行完成,释放被调函数中局部变量占用的栈空间
    6. 恢复现场:取主调函数运行状态及返回地址,释放栈帧空间
    7. 继续主调函数后续语句

以下面函数调用为例,进行分析:

#include <stdio.h>
#include <iostream>
using  namespace std;int sum(int a, int b)
{int temp = 0;temp = a + b;return temp;
}int main(void)
{int a = 10; int b = 20;int ret = sum(a, b);cout << "ret:" << ret << endl;return 0;
}

步骤:

  1. main函数开辟栈帧空间,执行下面两条代码,为局部变量ab分配内存

    int a = 10; // mov dword ptr[ebp-4], 0Ah
    int b = 20; // mov dword ptr[ebp-8], 14h
    
    image-20240802190127575
  2. 执行int ret = sum(a, b);,先在栈中为局部变量ret分配空间,然后从右向左将参数压入栈中,即为sum函数形参变量分配内存空间(所以不是进入sum函数后再为形参变量分配空间),转换为汇编指令如下:

    mov eax, dword ptr[ebp-8] ;dword ptr[ebp-8]为main函数中变量b的值
    push eax
    mov eax, dword ptr[ebp-4] ;dword ptr[ebp-4]为main函数中变量a的值
    push eax
    
    image-20240802191141980
  3. 进入sum函数之前,保存下一条指令的地址

    call sum ; 将下一行指令的地址入栈; 以下为sum函数返回后要执行的指令
    add esp, 8 	;地址为0x08124458,将这行指令的地址入栈(作用后面会分析)
    mov dword ptr[ebp-0Ch], eax
    
  4. 进入sum函数,执行sum函数左大括号与第一行语句之间的指令(并不是直接就执行第一行语句),有如下指令:

    1. 保存主调函数main函数的栈底地址

      push ebp  ;将当前的ebp指向的地址入栈
      
      image-20240802192053331
    2. 移动ebp指向sum函数的栈底(即指向当前函数的栈底)

      mov ebp, esp
      sub esp, 4Ch  ;为sum函数开辟栈帧空间,esp指向栈顶,4Ch是用于举例
      
      image-20240802192333985
      • 如果是window编译器下,可能还有一行指令为新开辟的栈帧初始化(gcc和g++不会初始化):

        rep stos ;相当于for循环,将新开辟的栈帧空间的值,置为0xCCCCCCCC
        
      • 定义一个局部变量,如果没有赋值,打印结果如下(如果允许打印,可能报错):

        int a;
        cout << a << endl; // -858993460,即0xCCCCCCCC
        
  5. 执行sum函数的函数体

    int temp = 0; 
    temp = a + b;  
    return temp;
    

    换成汇编指令

    mov dword ptr[ebp-4], 0			;为temp变量分配内存
    mov eax, dword ptr[ebp+0Ch] 	;取出a的值存入eax寄存器
    add eax, dword ptr[ebp+8] 		;执行a+b,并将结果存入eax寄存器
    mov dword ptr[ebp-4], eax		;取出eax寄存器中的值,存入temp变量中
    mov eax,dword ptr[ebp-4]		;再将temp变量的值存入eax寄存器中
    
    image-20240802195721165
  6. 执行sum函数最后一条语句与右大括号之间的指令

    1. 回退栈帧,释放栈空间

      mov esp, ebp 	;回退栈帧
      
      • image-20240802195544235
      • 虽然回退栈帧,并没有清理栈空间中保存的值,可以写出以下代码取得变量的值,但是这是非常危险的行为,所以不要返回局部变量的地址

        int *func()
        {int data = 10;return &data; // 不安全
        }
        int *p = func(); 
        cout << *p << endl; // 10,能正确打印// 在中间调用了其他函数,就有可能会覆盖刚刚的栈空间
        int *p = func();
        func2();
        cout << *p << endl; // 返回未知的值
        
    2. 将当前栈顶元素0x0018ff40出栈,并赋值给ebp, 相当于ebp回到指向主调函数main函数的栈底

      pop ebp	
      
      image-20240802200006386
    3. 将当前栈顶元素0x8124458出栈,并将值存入CPU的PC寄存器(用于存放下一条执行指令)里面,此时已经回到main函数的栈空间

      ret
      
      image-20240802200434710
  7. 执行pc寄存器中保存的指令,即执行进入sum函数时保存的下一行指令

    add esp, 8	;释放形参变量的内存空间
    
    image-20240802200745588
  8. sum函数返回时,保存在eax寄存器中的值赋值给ret变量

    mov dword ptr[ebp-0Ch], eax
    
    image-20240802201305703

补充(不同数据大小使用不同的寄存器):

  • <= 4 eax
  • >4 && <= 8 eax edx
  • >8 寄存器不够用,产生临时量带出返回值

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

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

相关文章

vs2015卸载和安装

vs2015卸载和安装 0. 摘要 可能对大家有帮助的地方:a. vs2015卸载和安装的流程;b. 安装时的error:“team explorer for microsoft visual studio 2015 update 3 ctp1 error” 解决方式;c. vs2015社区版的下载地址; 如果这三点不能解决你遇到的问题,就没必要往下看了。 1. …

CSP13

T1本来是道状压签到题,看成博弈论了,其实是不对的,为什么不对,建图时是存在环的情况的,所以不能建一棵树后跑\(sg\)函数 所以根据数据范围,我们可以状压,这就很简单了,每一次继承的状态为子状态相反的状态(不要试图只表示赢得状态)考试代码(41,43)pts #include <bits…

秒杀防止超卖方案测试(使用JMeter进行压力测试)

不进行任何防超卖操作,实测500个线程跑完不需要1s。虽然卖得快,但是超卖了哈哈哈。那么为了避免超卖我们对确认下单的方法加上synchronized: 实测发现确实没有超卖了,500个线程跑完需要44s,速度也太慢了,另外不支持多节点防止超卖,所以这个方案也不可取!

VirtualBox扩容CentOS-7虚拟机磁盘

1、背景描述如上图所示,根路径“/”所在的文件系统已没有可用的磁盘空间,需要扩容磁盘。 df -h2、VirtualBox操作 2.1、查看当前虚拟磁盘的大小如上图所示,点击打开选中的虚拟机的 Settings 界面。如上图所示,当前虚拟机的虚拟磁盘大小为 8GB 。 2.2、修改虚拟磁盘的大小如…

从零开始学逆向CTF比赛,免费参加,欢迎来玩!

大家好,我是轩辕。 告诉大家一个好消息:我准备了一次逆向CTF比赛,面向所有人开放,无需购买课程,优秀的小伙伴还有奖励,参赛方式在文末会介绍,欢迎大家一起来玩。举办这次CTF比赛,是为了检验大家从零开始学逆向的学习成果。就在不久前,我的这套视频课程终于完结了。 不…

从头装一台 ubuntu 电脑你需要什么?

1. 首先是硬件: cpu: 12600kf pdd 盒装 1040 gpu: 3060 12g 咸鱼 1600 主板:华擎 z690 pg4 d5 pdd 760 内存:阿斯加特海拉DDR5 6400 pdd 603 固态:Ti600 1T pdd 434 机箱:先马朱雀 jd 178 cpu 散热:利民 PA120 pdd 155 电源:玄武550 v4 pdd 190 合计:4960 其中,机箱…

centos7上dpdk绑定vfio-pci失败

记一次使用dpdk中的报错: 运行dpdk/usertools/dpdk-devbind.py -b vfio-pci 02:05.0来绑定设备到vfio-pci时,报出了如下错误: Error: bind failed for 0000:02:05.0 - Cannot bind to driver vfio-pci: [Errno 19] No such deviceError: unbind failed for 0000:02:05.0 - C…

mysql中的left join、right join 、inner join的详细用法

1.inner join,内连接,显示两个表中有联系的所有数据。 当两个表中存在匹配的数据时,‌返回满足条件的SELECT结果。‌内连接只返回两个表中匹配的记录,‌如果某一方没有匹配的记录,‌则不会出现在结果集中。‌ 2.left join,左链接,以左表为参照,显示所有数据,右表中没有则以…

欢欢乐乐赛赛

欢欢乐乐赛赛中文队名:回来吧,我的波波! 英文队名:Come back,my bobo! 队长: @Pursuing_OIer 队员: @hzoi_Shadow , @Charlie_ljk , @ccxswl荣获铜牌🥉。 \(A\) P184. 树构造 \(AC\)强化版: luogu P10678 『STA - R6』月直接考虑直径最小的情况怎么做。设最终得到的直…

NewStarCTF WEEK5|WEB pppython?

对源码进行简单的分析 <?php// 检查 `hint` 请求参数是否等于指定的数组值 if ($_REQUEST[hint] == ["your?", "mine!", "hint!!"]) {// 如果条件满足,设置响应内容类型为纯文本header("Content-type: text/plain");// 执行系统…

力扣--59.螺旋矩阵II

模拟顺时针画矩阵的过程:填充上行从左到右 填充右列从上到下 填充下行从右到左 填充左列从下到上由外向内一圈一圈这么画下去/*** 生成一个包含从1到n*n的数字的矩阵* @param {number} n - 矩阵的大小,为正整数* @return {number[][]} - 返回一个n x n的二维数组,数组中的每个…

轻松搞定 Nginx 在 CentOS 和 Ubuntu 上的安装与配置

本文详细介绍了在 CentOS 和 Ubuntu 系统上安装 Nginx 的全过程,包括下载方法、安装步骤、配置开机自启以及基础配置等重要内容,还提供了常见问题的解决方案和优化配置示例,助您顺利搭建高效的服务器环境。注:这是对我以前博客进行优化后再次发布的,博客中的截图为以前的。…