函数调用栈帧过程带图详解

这里,我们来研究如下代码的栈帧过程。为了初学者理解汇编指令,所以编译环境是在vc++6.0下

#include<stdio.h>
#include<stdlib.h>

int sum(int _a,int _b)
{
    int c=0;
    c=_a+_b;
    return c;
}

int main()
{
    int a=10;
    int b=20;
    int ret=0;

    ret=sum(a,b);
    printf("%d\n",ret);

    system("pause");
    return 0;
}
//如下是从vc++6.0中截取的汇编指令
--- f:\vc\a\a.cpp  ----------------------------------------
10:
11:   int main()
12:   {
00401060   push        ebp
00401061   mov         ebp,esp
00401063   sub         esp,4Ch
00401066   push        ebx
00401067   push        esi
00401068   push        edi
00401069   lea         edi,[ebp-4Ch]
0040106C   mov         ecx,13h
00401071   mov         eax,0CCCCCCCCh
00401076   rep stos    dword ptr [edi]
13:       int a=10;
00401078   mov         dword ptr [ebp-4],0Ah
14:       int b=20;
0040107F   mov         dword ptr [ebp-8],14h
15:       int ret=0;
00401086   mov         dword ptr [ebp-0Ch],0
16:
17:       ret=sum(a,b);
0040108D   mov         eax,dword ptr [ebp-8]
00401090   push        eax
00401091   mov         ecx,dword ptr [ebp-4]
00401094   push        ecx
00401095   call        @ILT+0(sum) (00401005)
0040109A   add         esp,8
0040109D   mov         dword ptr [ebp-0Ch],eax
18:       printf("%d\n",ret);
004010A0   mov         edx,dword ptr [ebp-0Ch]
004010A3   push        edx
004010A4   push        offset string "%d\n" (00424024)
004010A9   call        printf (00401200)
004010AE   add         esp,8
19:
20:       system("pause");
004010B1   push        offset string "pause" (0042401c)
004010B6   call        system (004010f0)
004010BB   add         esp,4
21:   }
004010BE   pop         edi
004010BF   pop         esi
004010C0   pop         ebx
004010C1   add         esp,4Ch
004010C4   cmp         ebp,esp
004010C6   call        __chkesp (00401280)
004010CB   mov         esp,ebp
004010CD   pop         ebp
004010CE   ret

--- f:\vc\a\a.cpp  ----------------------------------------
1:    #include<stdio.h>
2:    #include<stdlib.h>
3:
4:    int sum(int _a,int _b)
5:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
6:        int c=0;
00401038   mov         dword ptr [ebp-4],0
7:        c=_a+_b;
0040103F   mov         eax,dword ptr [ebp+8]
00401042   add         eax,dword ptr [ebp+0Ch]
00401045   mov         dword ptr [ebp-4],eax
8:        return c;
00401048   mov         eax,dword ptr [ebp-4]
9:    }
0040104B   pop         edi
0040104C   pop         esi
0040104D   pop         ebx
0040104E   mov         esp,ebp
00401050   pop         ebp
00401051   ret

注:以下栈帧图是连续的,即可以拼接成一副完整的图,并且,按照图片出现的次序,依次向栈的低地址方向走。
1.
我们知道main函数也是被其它函数调用的,这个函数就是mainCRTStartup(),所以,该函数也有一个栈帧,即运行时堆栈,用esp、ebp维护。
这里写图片描述
2.
00401060 push ebp //把ebp压入栈顶
00401061 mov ebp,esp //把esp的值给ebp,此时,ebp指向esp的位置
这里写图片描述
3.
00401063 sub esp,4Ch //esp的值减去4Ch,esp向低地址方向增长
00401066 push ebx //ebx压入栈顶
00401067 push esi //esi压入栈顶
00401068 push edi //edi压入栈顶
这里写图片描述
4.
00401069 lea edi,[ebp-4Ch] //从[ebp-4Ch] 的地方开始拷贝
0040106C mov ecx,13h //拷贝13h次
00401071 mov eax,0CCCCCCCCh //拷贝的内容为0CCCCCCCCh
00401076 rep stos dword ptr [edi] //每次拷贝双字
在内存中查看:
这里写图片描述
5.
13: int a=10;
00401078 mov dword ptr [ebp-4],0Ah //0Ah压入[ebp-4]表示的空间中
14: int b=20;
0040107F mov dword ptr [ebp-8],14h //14h压入[ebp-8]表示的空间中
15: int ret=0;
00401086 mov dword ptr [ebp-0Ch],0 //0压入[ebp-0Ch]表示的空间中

这里写图片描述
在内存中查看:
这里写图片描述
6.
16:
17: ret=sum(a,b);
0040108D mov eax,dword ptr [ebp-8] //[epb-8]指向的内容b=20放入eax中
00401090 push eax //eax中的内容入栈
00401091 mov ecx,dword ptr [ebp-4] //[ebp-4]指向的内容a=10放入ecx中
00401094 push ecx //ecx中的内容入栈
00401095 call @ILT+0(sum) (00401005) //压入call指令的下一条指令的地址,这样函数才能找到返回地址
这里写图片描述
7.下面,跳转到了sum函数的内部
4: int sum(int _a,int _b)
5: {
00401020 push ebp //ebp中的内容(即mian函数的基地址)入栈
00401021 mov ebp,esp //把esp的值给ebp,此时ebp指向esp指向的地方
这里写图片描述
8.下面的指令同main中的指令
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
这里写图片描述

ret=0
这里写图片描述
9.
6: int c=0; //sum函数中的局部变量c
00401038 mov dword ptr [ebp-4],0 //把c=0放入[ebp-4]指向的空间中
7: c=_a+_b;
0040103F mov eax,dword ptr [ebp+8] //把[ebp+8]指向的内容放入eax中
00401042 add eax,dword ptr [ebp+0Ch] //把[ebp+0Ch]指向的内容与eax中的内容相加放入eax中
00401045 mov dword ptr [ebp-4],eax //把eax中的内容(即30)放入[ebp-4]指向的空间中(即给c变量赋值)
8: return c;
00401048 mov eax,dword ptr [ebp-4] //把[ebp-4]指向的内容(即30)放入eax中
9: }
这里写图片描述

在内存中查看c=0
这里写图片描述
在内存中查看取_a和_b的值
这里写图片描述
在内存中查看c=30
这里写图片描述
10.
0040104B pop edi //出栈,内容放入edi中
0040104C pop esi //出栈,内容放入esi中
0040104D pop ebx //出栈,内容放入ebx中
这里写图片描述
11.
0040104E mov esp,ebp //把ebp的值给esp,此时,esp指向ebp的地方(即保存了main函数的栈基地址处的空间)

sum函数的栈帧全部销毁,注意黄色区域
这里写图片描述
12.
还原main函数的栈帧
00401050 pop ebp
//出栈,内容放入ebp中。注意,此时栈中的内容就是之前保留的main
//函数的栈基地址,经过这条指针,ebp从新回到main函数的栈基地址处
//main函数的栈帧被还原;并且,esp从步骤11处的地方移到保存了“call指令的下一条指令的地址”处
00401051 ret
为了方便看图,我将之前的标记变成了灰色,注意紫色区域
这里写图片描述
13.回到call指令下一条指令
0040109A add esp,8
//返回到call值令的下一条指令后,esp移动到指向形参_a的地方,经过这条,esp指向_b形参的后面(请参照步骤12中的图),也就是说形参这块空间被销毁
0040109D mov dword ptr [ebp-0Ch],eax
14.结束
18: printf(“%d\n”,ret);
004010A0 mov edx,dword ptr [ebp-0Ch]
004010A3 push edx
004010A4 push offset string “%d\n” (00424024)
004010A9 call printf (00401200)
004010AE add esp,8
19:
20: system(“pause”);
004010B1 push offset string “pause” (0042401c)
004010B6 call system (004010f0)
004010BB add esp,4
21: }
004010BE pop edi
004010BF pop esi
004010C0 pop ebx
004010C1 add esp,4Ch
004010C4 cmp ebp,esp
004010C6 call __chkesp (00401280)
004010CB mov esp,ebp
004010CD pop ebp
004010CE ret

总图如下:
这里写图片描述

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值