首页 > 编程语言 >从汇编层面看c/c++函数调用过程

从汇编层面看c/c++函数调用过程

时间:2024-08-02 21:06:44浏览次数:16  
标签:汇编 函数 int sum c++ eax ebp 函数调用 ptr

函数调用分析

前置知识:

  • 全局变量:在函数内部定义的变量
  • 局部变量:在函数外部定义的变量
  • 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 寄存器不够用,产生临时量带出返回值

标签:汇编,函数,int,sum,c++,eax,ebp,函数调用,ptr
From: https://www.cnblogs.com/General-xd/p/18339588

相关文章

  • 上海计算机学会2022年5月月赛C++乙组T3狼人游戏(二)
    狼人游戏(二)内存限制: 256 Mb时间限制: 1000 ms题目描述有 n 名玩家在玩狼人游戏,有一些玩家的身份是狼人。其余玩家的身份是预言家。游戏的进程中,陆续出现了 m 句发言,每句发言来自于某个玩家,发言的信息是声称另一个玩家的身份是狼人或者是预言家。小爱猜想,狼人的发......
  • C++语法基础之输入输出(易理解巨详细)
    Unit1C++语法基础之基本输入输出本次分享属于C++语法基础系列课,标准输入输出的理解和使用C++语法基础之输入输出标准输入输出介绍(一)输入输出流(概念比较抽象,可先理解代码,回头进行理解性记忆)1、概念2、输入流(InputStreams)3、输出流(OutputStreams)(二)标准输出1、输......
  • C++中const关键字的作用?
    const关键字的作用?const主要用来定义常量和保护变量不被修改:定义常量:使用const可以定义一个不可修改的常量,const常量的默认链接方式是内部链接(只有该源文件可见),可以将其定义在头文件中而不会引起重复定义问题,每个包含该头文件的源文件都各自拥有一个const常量的副本。//......
  • 【C++】引用和指针的不同点
    引用和指针的不同点:(从使用的角度去对比,按自己的理解的角度去梳理,硬记很难记全,虽然不赢记大概率也记不全)1.引用概念上定义一个变量的别名,指针存储一个变量地址。2.引用在定义时必须初始化,指针没有要求。3.引用在初始化时引用一个实体后,就不能再引用其他实体;而指针可以在......
  • 【C++】运算符重载
    一、示例如果我想实现以下代码,按照下面的写法是不能正常运行的。classPerson{public:intm_A;intm_B;};Personp1;p1.m_A=10;p1.m_B=10;Personp2;p2.m_A=10;p2.m_B=10;Personp3;p3=p1+p2;按照以上学过的内容,可以自己写成员函数,实......
  • C++高级功能
    Lambda匿名函数[只读列表](参数列表){函数体}例如:sort(a+1,a+n,[](constData&x,constData&y){returnx.val<y.val;}constintk=5;autocalc=[k](constint&x){returnx*k;}template模板在struct/namespace/函数前加入template<typ......
  • Windows图形界面(GUI)-MFC-C/C++ - 静态文本框(Static Text) - CStatic
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​链接点击跳转博客主页目录静态文本框(StaticText)-CStatic基本概念成员函数示例代码静态文本框(StaticText)-CStatic基本概念静态文本框是一种用于显示文本的控件,用户不能编辑其中的文本。静态文本框......
  • window配置onnxruntime,运行c++版本
    为了使用ONNX-Runtime-Inference这个项目,但是我缺少onnxruntime这个库,网上找了很多教程,但是大多数都是关于linux的,这里简单记录一下我的配置流程找到onnxruntime的release版本开始想着自己去找源码编译,发现这对于新手来说,是个坑,因为源码里面有些库是缺失的,需要自己去下载,并更改......
  • 【C++】学习笔记——智能指针
    文章目录二十一、智能指针1.内存泄漏2.智能指针的使用及原理RAII智能指针的原理auto_ptrunique_ptrshared_ptrshared_ptr的循环引用weak_ptr删除器未完待续二十一、智能指针1.内存泄漏在上一章的异常中,我们了解到如果出现了异常,会中断执行流,跳转到catch处。但......
  • 【C++】学习笔记——特殊类的设计
    文章目录二十二、特殊类的设计1.请设计一个类,不能被拷贝2.请设计一个类,只能在堆上创建对象3.请设计一个类,只能在栈上创建对象4.请设计一个类,不能被继承5.请设计一个类,只能创建一个对象(单例模式)未完待续二十二、特殊类的设计1.请设计一个类,不能被拷贝拷贝......