您的位置:首页 > 其它

深度剖析函数的调用过程

2017-11-05 21:53 260 查看
我们先从一个简单的小程序看起

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);
printf("%d", ret);
system("pause");
return 0;
}


在函数的执行期间,我们可以打开调用堆栈



我们可以看到其实main函数并不是第一个被调用的函数,程序在运行时第一个被调用的函数是mainCRTStartup,然后mainCRTStartup 函数在调用 ___tmainCRTStartup 函数,之后__tmainCRTStartup函数在调用main函数,由此可见我们知道函数其实是一级一级调用的,这个过程就叫做函数的调用,在函数调用的过程中会开辟栈空间,用来保护函数参数,这就叫做函数的栈桢。

在正式开始分析函数调用之前,我们先来了解几个寄存器

ebp:保存指向栈底指针的寄存器

esp:保存指向栈顶指针的寄存器

eip/pc/ip:程序计数器(永远指向下一条指令(即下一条指令的地址))

eap:临时寄存器

我们用汇编语言来分析,先来了解几个汇编指令的作用

call:通过修改ip来实现函数跳转(当前正在执行指令的下一条指令的地址保存起来)

jmp:将当前正在执行指令的下一条指令的地址压入栈中

pop:单独为出栈后加寄存器为将值放入寄存器中

ret:弹出栈顶返回值到eap

sub:

_ asm _:写汇编(用c控制汇编)

1.先来看main函数的调用(main函数栈桢的调用)



main函数被-tmainCRTStartup调用,栈是自下往上生长,下面是高地址,上面是低地址,所以main函数的栈桢应在-tmainCRTStartup函数栈桢的上面。

main函数栈桢形成时,ebp指向栈底,之后ebx,esi,ebi相继入栈,此时ebi保存三个寄存器入栈之前的指向地址,然后给main函数预开辟空间,然后实参相继入栈,先a后b

2.Add函数的调用



之后形参压栈,从右往左先b后a

之后call指令将函数跳转到Add函数处,执行Add函数

3.Add函数的创建和返回过程



Add函数先将main函数给Add函数预开辟的空间初始化为0xcccccccch

ebx,esi,ebi相继如何入栈

之后首先入栈并初始化为0之后,获取形参x和y相加将结果保存到z中放在eax中,通过eax带回函数的返回值

开始出栈:esp下移,ebi,esi,ebx相继出栈

将ebp的值赋给esp,即都指向Add函数的栈底,之后出栈将出栈的内容保存在ebp中之后回到main函数的栈桢

至此,函数的调用已经全部完成
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: