图解函数的栈帧

前言:本篇文章在汇编语言的角度介绍了函数的调用过程,也就是函数的栈帧,如果有什么问题,欢迎指出,共同进步。

1.什么是函数栈帧

每一次函数的调用都是一个过程,在这个过程中要为函数开辟栈空间,用于本次函数调用中临时变量的保存和现场保护等等。栈是从高地址向低地址延伸的。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

2.以Add函数为例,图解栈帧

  • 测试代码

    #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("ret=%d", ret);
        return 0;
    }

将程序调试起来(如下图),查看【调用堆栈】,我们可以发现main函数在被调用之前是在函数__mainCRTStartup中调用,而__mainCRTStartup又是在mainCRTStartup函数中调用。
这里写图片描述

  • main函数的栈帧:
    这里写图片描述

3.从main函数开始分析函数栈帧

这里写图片描述

  • 从第一句汇编分析对应如上图


    a.首先是__mainCRTStartup()函数的调用,然后在调用main()函数;
    b.将ebp做压栈处理,保存指向栈底的地址(方便函数返回之后的现场恢复;
    c.将esp的值赋给ebp,产生新的ebp;
    d.给esp减去一个16进制数0E4H(为main函数预开辟空间);
    e.将ebx、esi、edi做压栈处理,对应蓝色部分;
    f.lea指令,加载有效地址,初始化预开辟的空间为0xcccccccc,对应橙色部分;
    g.变量a和b的创建。

  • 下边是Add函数的调用过程
    这里写图片描述

a.将实参b存入寄存器eax,再将eax做压栈处理;(传参过程,从右向左传递,先传b)
b.将a存入寄存器ecx,再将ecx压栈;
c.call指令的调用,先将call指令下一条指令的地址压栈,然后跳转(jmp)到Add()函数的地方。

  • 执行call指令

按f11执行call指令,如下图

这里写图片描述

  • 进入Add函数
    这里写图片描述

a.首先将main()函数ebp压栈处理,保存指向main()函数栈帧底部的地址(方便函数返回之后的现场恢复),此时esp指向新的栈顶位置;
b.将esp的值赋给ebp,产生新的ebp,即Add()函数栈帧的ebp;
c.给esp减去一个16进制数0E4H(为Add()函数预开辟空间);
d.将 ebx、esi、edi做压栈处理;
e.lea指令,加载有效地址;
f.初始化预开辟的空间为0xcccccccc;
g.变量z的创建;
h.获取形参的a和b再相加,将结果存储到z中;
i.将结果存储到eax寄存器,通过寄存器带回函数的返回值。

  • Add函数返回
    这里写图片描述

a.edi、esi、ebx依次出栈,esp向下移动;
b.将ebp赋给esp,使esp指向ebp指向的地方;
c.ebp 出栈,将出栈的内容给ebp(即main-ebp),回到main()函数的栈帧;
d.ret指令,出栈一次,并将出栈的内容当做地址,并跳转到该地址处(call指令下一条指令)。

注:函数栈帧在不同编译器上实现存在差异,偏移量不同,但思路都是一样的。

例子:下面是我代码,在VC6.0++输出结果是什么。


    #include<stdio.h>

    void fun()
    {
        int tmp = 10;
        int *p = (int*)(*(&tmp + 1));
        *(p - 1) = 20;
    }
    int main()
    {
        int a = 0;
        fun();
        printf("a=%d\n", a);
        return 0;
    }

这段代码看似和fun函数没有什么关系,但是输出结果却是20。它实际上是与函数栈帧有关系,感兴趣的朋友可以根据上边的栈帧图解做一做题目。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值