深度剖析函数的调用过程

我们先从一个简单的小程序看起

#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函数的栈桢
至此,函数的调用已经全部完成

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值