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

来源:互联网 发布:linux for循环用法 编辑:程序博客网 时间:2024/06/07 04:50

程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接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的值给esp010637C6 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函数出栈。。。

原创粉丝点击