函数栈帧创建与销毁那些事儿

程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。
函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(callstack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量就是所谓的保护现场。

为了浅显易懂,直接抛代码看反汇编~

int Add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}
int main()
{
    int sum = 0;
    int a = 2;
    int b = 3;
    sum = Add(a, b);
    return 0;
}

我们一步一步通过反汇编来剖析调用过程:
这里写图片描述

反汇编码:

    int sum = 0;
0034532E C7 45 F8 00 00 00 00 mov         dword ptr [sum],0  
    int a = 2;
00345335 C7 45 EC 02 00 00 00 mov         dword ptr [a],2  
    int b = 3;
0034533C C7 45 E0 03 00 00 00 mov         dword ptr [b],3  

分析:
这里写图片描述

接下来看到调用Add函数:

    sum = Add(a, b);
00345343 8B 45 E0             mov         eax,dword ptr [b]  
00345346 50                   push        eax  
00345347 8B 4D EC             mov         ecx,dword ptr [a]  
0034534A 51                   push        ecx  
0034534B E8 BE BD FF FF       call        Add (034110Eh)  
00345350 83 C4 08             add         esp,8  
00345353 89 45 F8             mov         dword ptr [sum],eax  

具体分析:
这里写图片描述

函数出栈的反汇编:

    return z;
010637BE 8B 45 F8         mov       eax,dword ptr [z]  
}
010637C1 5F          pop      edi  //edi返回 弹出栈
010637C2 5E          pop      esi  //esi返回
010637C3 5B          pop      ebx  //esi返回
010637C4 8B E5       mov      esp,ebp  //将原来的ebp的值给esp
010637C6 5D          pop      ebp  
//弹出ebp,使得ebp的值指向之前保存好的main函数的ebp,esp向下指向保存的地址处,此时esp和ebp维护到main函数的栈帧
010637C7 C3          ret  
//隐藏的pop,使Add函数栈底的地址弹出。

下面是函数出栈的图解解析(黑色线条标记)
这里写图片描述

回到call指令的下一条指令

00345350 83 C4 08    add    esp,8  //将形参返回弹出栈
00345353 89 45 F8    mov    dword ptr [sum],eax  

至此,esp,ebp维护到main函数的栈帧,Add函数出栈。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值