78-C++函数调用过程

当前演示的函数调用过程是依赖于C语言默认的调用约定——_cdecl

其他的调用约定还有__stdcall和__fastcall,三种都是C、C++使用的调用规则,三种调用约定的差异并不大,只是负责的事情有所不同

一:参数入栈

1.4字节参数入栈,顺序:从右向左入栈(先入最后一个参数),方式:使用寄存器push带入

78-C++函数调用过程

先将b入栈,同时esp往上走,再将a入栈(从右向左入栈)

78-C++函数调用过程

2.8字节参数入栈,顺序:从右向左,方式:使用寄存器push带入

78-C++函数调用过程

先将b._2入栈,然后esp往上移(向低地址移动),然后将b._1入栈,然后esp往上移(向低地址移动),然后将a._2入栈,然后esp往上移(向低地址移动),然后将a._1入栈,然后esp往上移(向低地址移动),方式仍然是从右向左

78-C++函数调用过程

3.12字节参数入栈,顺序:从右向左,方式:现在栈顶开辟足够该参数的空间,之后将数据复制进去

78-C++函数调用过程

先开辟12个字节的空间(第一行表示esp=esp-0c,高地址往低地址增长,所以用减),然后先后存入b._1,b._2,b._3,然后再开辟12个字节的空间然后先后存入a._1,a._2,a._378-C++函数调用过程

4.C语言中,大于8字节以后,都是采用3的方式,小于8字节,采用1 2方式

5.在C++中,只要是自定义类型, 无论多大字节,都采用3的方式

6.寄存器:CPU中的变量

7.esp寄存器:栈顶寄存器(低地址)

8.ebp寄存去:栈底寄存器(高地址)

9.唯一标志一段空间就需要有一个头有一个尾

10.mov是取值(把后面的值赋值给前面),push是入栈,sub是减,call是调用函数

二:函数栈帧开辟

78-C++函数调用过程

1.在参数入栈之后,调用函数的时候,esp往上(往低地址)移动了4个字节,保存的是下一行指令的地址(就是函数调用完成之后,main函数要从那里继续执行)

2.然后把main函数的ebp(栈底寄存器)入栈

3.然后让ebp=esp

4.然后开辟整个fun函数的栈帧(esp=esp-0cc)

5.然后将其他使用的寄存器入栈

6.然后循环初始化成0xcccc cccc

7.然后进行校验

78-C++函数调用过程

三:返回值返回

4字节的返回值:

方式:将返回值放入寄存器带回

8字节返回值:

方式:将放回值放入两个寄存器带回

大于8字节返回值:

方式:首先在函数参数入栈之后,入栈一个调用方栈帧上的地址(靠近栈顶位置)在返回值返回的时候将返回数据写入到之前入栈的调用方地址上,返回之后将从该地址上将数据取出

注意:在C++中,自定义类型都按照入栈调用方地址的方式(即大于8字节返回值的方式)

四:函数栈退出

78-C++函数调用过程
78-C++函数调用过程

1.在之前现场保护入的三个寄存器吐出

2.进行当前函数栈帧的校验

3.将现场保护的寄存器吐出(出栈)

4.让esp=ebp

5.ebp=Pop(把先前入栈的main函数的ebp还原回来)

6.将下一行指令的地址还原

7.清除参数

上一篇:C++在使用Qt中SLOT宏需要注意的一个小细节


下一篇:Python hashlib模块 (主要记录md5加密)