#include <stdio.h>int add(int a, int b) {int c = 0;c = a + b;return c;
}int main()
{int r = add(1, 2);return 0;
}

我们使用Visual Studio 2017编译上面代码,并在在工程配置中将函数调用约定设置为__cdecl

在程序调试过程中,可以在Visual Studio反汇编窗口中看到C++代码对应的汇编代码,以及寄存器窗口中看到各个寄存器的值。


main函数的反汇编代码如下:

int main()
{
00DD1720  push        ebp          // 参见add函数中关于这一部分的解析
00DD1721  mov         ebp,esp
00DD1723  sub         esp,0CCh
00DD1729  push        ebx
00DD172A  push        esi
00DD172B  push        edi
00DD172C  lea         edi,[ebp-0CCh]
00DD1732  mov         ecx,33h
00DD1737  mov         eax,0CCCCCCCCh
00DD173C  rep stos    dword ptr es:[edi]  int r = add(1, 2);
00DD173E  push        2                 // 参数b入栈
00DD1740  push        1                 // 参数a入栈
00DD1742  call        add (0DD1276h)    // 调用add函数。CALL指令相当于执行一条PUSH指令加一条JMP指令,PUSH指令用于压入该指令的下一条指令地址到栈中,用于执行完子函数之后返回来。JMP指令用于跳转到子函数所在位置开始执行子函数。
00DD1747  add         esp,8            // 因为是__cdecl,所以由调用者来平衡堆栈.
00DD174A  mov         dword ptr [r],eax  return 0;
00DD174D  xor         eax,eax
}

执行进入add函数后,add函数内的汇编代码如下:

int add(int a, int b) {
00DD16D0  push        ebp      // ebp入栈,相当于暂存ebp的值
00DD16D1  mov         ebp,esp  // 将esp赋值给ebp,在该函数之后的执行过程中不会再改变ebp的值。
00DD16D3  sub         esp,0CCh // 在栈上分配0xCC大小的局部变量存储区域
00DD16D9  push        ebx      // 暂存ebx
00DD16DA  push        esi      // 暂存esi
00DD16DB  push        edi      // 暂存edi
00DD16DC  lea         edi,[ebp-0CCh]  //下面4行代码(含该条)实现将0xCC大小的局部变量存储区域全部赋值为0xCC
00DD16E2  mov         ecx,33h         // ecx存储循环次数,结合rep指令使用。为什么是0x33次了?因为是按照4个字节赋值的,0x33 * 0x4 = 0xCC
00DD16E7  mov         eax,0CCCCCCCCh
00DD16EC  rep stos    dword ptr es:[edi]  // 循环赋值int c = 0;
00DD16EE  mov         dword ptr [c],0     // 将局部变量c赋值为0c = a + b;
00DD16F5  mov         eax,dword ptr [a]
00DD16F8  add         eax,dword ptr [b]
00DD16FB  mov         dword ptr [c],eax  return c;
00DD16FE  mov         eax,dword ptr [c]  // 将结果存储到eax中。在函数调用中返回结果都是存储在eax中的。
}
01191701  pop         edi  // 将edi的值还原到函数调用前
01191702  pop         esi  // 将esi的值还原到函数调用前
01191703  pop         ebx  // 将ebx的值还原到函数调用前
01191704  mov         esp,ebp  // 移动栈顶到ebp位置,从而跳过了局部变量存储区域
01191706  pop         ebp      // 将ebp的值还原到函数调用前
01191707  ret                  // ret指令等同于:弹出此时栈顶的值给eip,// 因为此时栈顶存储的刚好是函数返回地址,所以相当于将返回地址赋值给eip,从而实现了返回到函数调用的地方。

在上面代码的注释中已经包含了详细的解释,特别值得注意的几个地方是:
1. rep stos dword ptr es:[edi]结合edi, ecx来初始化局部存储区域。
2. 函数call指令之前的参数压栈顺序。
3. CALL指令相当于执行一条PUSH指令加一条JMP指令,PUSH指令用于压入该指令的下一条指令地址到栈中,用于执行完子函数之后返回来。JMP指令用于跳转到子函数所在位置开始执行子函数。
4. 因为是__cdecl,函数调用完之后,调用方使用add esp,8来平衡堆栈。
5. ret指令等同于:弹出此时栈顶的值给eip,巧妙之处在于此时栈顶存储的刚好是函数返回地址。

从汇编的角度分析函数调用过程(2)相关推荐

  1. 从汇编的角度分析函数调用过程(1)

    一. 函数参数传递形式 函数的参数传递有2种方式:堆栈方式.寄存器方式. 如果是堆栈方式传递的,就需要定义函数参数在堆栈中的传递顺序,并约定函数被调用之后,由谁来平衡堆栈: 如果是寄存器方式传递的,就 ...

  2. C语言的函数调用过程(栈帧的创建与销毁)

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: 1 int Add(int x,int y)2 {3 int sum = 0;4 sum = x + y;5 return sum;6 ...

  3. 函数调用过程简单分析

    C/C++函数调用过程分析 这里以一个简单的C语言代码为例,来分析函数调用过程 代码: 1 #include <stdio.h> 2 3 int func(int param1 ,int ...

  4. 函数调用过程中的栈帧结构及其变化

    前言:本文旨在从汇编代码的角度出发,分析函数调用过程中栈帧的变化. 栈帧的简单介绍: 当某个函数运行时,机器需要分配一定的内存去进行函数内的各种操作,这个过程中分配的那部分栈称为栈帧.下图描述了栈帧的 ...

  5. 37.Linux驱动调试-根据oops的栈信息,确定函数调用过程

    上章链接入口: http://www.cnblogs.com/lifexy/p/8006748.html 在上章里,我们分析了oops的PC值在哪个函数出错的 本章便通过栈信息来分析函数调用过程 1. ...

  6. 深入浅出根据函数调用过程谈栈回溯原理

                通过分析函数调用过程的堆栈变化,可以看出在被调函数的EBP寄存器地址存放的是调用函数的EBP寄存器地址,EBP地址+4存放的是函数调用完成后的下一条指令存放地址,该指令的前一条 ...

  7. JVM系列之:从汇编角度分析Volatile

    文章目录 简介 重排序 写的内存屏障 非lock和LazySet 读的性能 总结 简介 Volatile关键字对熟悉java多线程的朋友来说,应该很熟悉了.Volatile是JMM(Java Memo ...

  8. C函数调用过程原理及函数栈帧分析

    在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...

  9. 从函数调用过程中的堆栈变化理解缓冲区溢出

    一.说明 本来是想直接写一个缓冲区溢出的例子,但是一是当前编译器和操作系统有溢出的保护措施没有完全弄清怎么取消,二是strcpy等遇到00会截断需要进行编码这比较难搞,所以最终没有实现. 但已经双看了 ...

  10. 函数调用过程实例详解

    原文标题:<函数调用过程探究> 引言 如何定义函数.调用函数,是每个程序员学习编程的入门课.调用函数(caller)向被调函数(callee)传入参数,被调函数返回结果,看似简单的过程,其 ...

最新文章

  1. PAGER set to stdout_Python || 学习笔记(4):dictamp;amp;set
  2. 二代三代转录组测序分析实战班
  3. 【Leetcode】79.单词搜索
  4. SpringMVC(SSM)框架搭建JavaWeb项目时,前端页面文件上传,后台Java下载功能实现及相关问题记录说明
  5. A multi-faceted language for the Java platform
  6. Struts2的控制器(Controller)的工作流程图
  7. uiautomator +python 安卓UI自动化尝试
  8. SQL服务器名称更改
  9. kakfa怎么看消息是否堆积_不停的打开微信,只为看你是否更新了消息
  10. Android线程池的简单使用
  11. RocketMQ-0.1
  12. ubuntu下Pure-FTPd的安装和配置
  13. 蚂蚁金服:开源增强版 SpringBoot 的研发框架!
  14. 解决idea使用jdbc连接数据库失败的方法(针对驱动导入失败)
  15. php酒店系统论文,基于PHP的酒店管理系统PHP1008(毕业设计+论文)
  16. c++中文件打开失败
  17. 2021年中国不间断电源(UPS)行业市场规模、产品结构及发展趋势分析:UPS电源向节能环保方向发展 [图]
  18. 优秀课程案例:母亲节!用Scratch编程送给母亲最好的贺卡礼物!
  19. arduino实现rgb灯循环亮起
  20. Spark(四)— Spark Streaming

热门文章

  1. CwRsync实现文件同步(windows或linux服务器通用)
  2. 活在当下,谋在未来,国内外新能源汽车城市物流配送运营模式创新
  3. java 读文件内容_Java 如何读取txt文件的内容?
  4. android logo在线生成工具,在线生成logo
  5. 1037: 求一元二次方程的根(带虚根)
  6. ArcGIS操作系列(一)之地理配准
  7. esp8266接入小爱同学,通过mqtt
  8. 英语词性的分类及用法详述
  9. 读书笔记-计算机视觉
  10. TunePat Amazon Video Downloader使用教程