函数的调用过程(栈帧的创建和销毁)

来源:互联网 发布:移植术后软件 编辑:程序博客网 时间:2024/06/06 18:43

  为了更好地认识函数的调用过程,我们可以用反汇编代码去理解学习。

一、基本概念

1.栈帧(过程活动记录):是编译器用来实现函数调用的一种数据结构,每个栈帧对应一个未运行完的函数,栈帧中保存了该函数的返回地址和局部变量。

2.栈(stack):又名堆栈,是一种运算受限的线性表,只允许在表的一端输入输出,这一端叫栈顶,相对的另一端是栈底,遵循后进先出,栈底是高地址,栈顶是低地址,栈由内存高地址向低地址生长。

3.push: 压栈    pop:出栈

4.寄存器: 位于CPU内部,存放运行程序中的数据和指令。

ebp (栈帧的栈底指针)     esp(栈帧的栈顶指针)

二、调用add函数的代码

#include <stdio.h>int add(int a, int b){int ret = 0;ret = a + b;return ret;}int main(){int a = 10;int b = 20;int ret = add(a, b);return 0;}

三、截取的部分反汇编代码,进行逐语句分析

int main(){00B81410  push        ebp             //在此之前,main函数在mainCRTstartup中调用,先为mainCRTsturtup开辟栈帧,有栈底,有栈顶。现在开始压栈。00B81411  mov         ebp,esp       //让ebp指向esp的位置。00B81413  sub         esp,0E4h     //esp指向[esp-oE4h]的位置,sub为减00B81419  push        ebx             //分别将ebx、esi、edi压栈00B8141A  push        esi  00B8141B  push        edi  00B8141C  lea         edi,[ebp-0E4h]           //加载有效地址,让edi存放[ebp-0E4h]的位置,1C至2C表示对空间进行初始化,从edi所指向的位置开始向高地址进行拷贝,初始内容为0CCCCCCCCh,拷贝39次00B81422  mov         ecx,39h00B81427  mov         eax,0CCCCCCCCh  00B8142C  rep stos    dword ptr es:[edi]  int a = 10;00B8142E  mov         dword ptr [a],0Ah      //将10赋值于给a变量分配的空间int b = 20;00B81435  mov         dword ptr [b],14h       //将20赋值于给b变量分配的空间int ret=add(a, b);00B8143C  mov         eax,dword ptr [b]       //进行传参,让寄存器eax存放b的值00B8143F  push        eax                          //将eax压栈00B81440  mov         ecx,dword ptr [a]       //进行传参,让寄存器ecx存放a的值00B81443  push        ecx                          //将ecx压栈00B81444  call        _add (0B810E6h)       //call指令下一条地址,为add函数的调用及返回做准备——————————————————————————————————————00B810DC  jmp         00B83BBE  __ValidateImageBase:00B810E1  jmp         00B82910  _add:00B810E6  jmp         00B813C0                //即进入add函数_GetProcessHeap@0:00B810EB  jmp         00B83C2A  __RTC_SetErrorFuncW:——————————————————————————————————————int add(int a, int b)     4: {00B813C0  push        ebp                //ebp压栈,是main函数的ebp00B813C1  mov         ebp,esp          //让ebp指向esp的位置。00B813C3  sub         esp,0CCh       //esp指向[esp-0CCh]的位置,sub为减00B813C9  push        ebx                //分别将ebx、esi、edi压栈00B813CA  push        esi  00B813CB  push        edi  00DC13CC  lea         edi,[ebp-0CCh]        //加载有效地址,让edi存放[ebp-0CCh]的位置,CC至DC表示对空间进行初始化,从edi所指向的位置开始向高地址进行拷贝,初始内容为0CCCCCCCCh,拷贝33次00DC13D2  mov         ecx,33h  00DC13D7  mov         eax,0CCCCCCCCh  00DC13DC  rep stos    dword ptr es:[edi]       5: int ret = 0;00DC13DE  mov         dword ptr [ebp-8],0              //将0放入[ebp-8]处     6: ret = a + b;00DC13E5  mov         eax,dword ptr [ebp+8]          //将[ebp+8]处的值10放入eax00DC13E8  add         eax,dword ptr [ebp+0Ch]      //将[ebp+0Ch]处的值20放入eax并相加,得eax中的值为30.00DC13EB  mov         dword ptr [ebp-8],eax          //将eax的内容拷贝在[ebp-8]中,即ret变量     7: return ret;00DC13EE  mov         eax,dword ptr [ebp-8]          //将ret的值存入eax中     8: }00DC13F1  pop         edi                                    //edi、esi、ebx、出栈,栈帧逐渐开始销毁00DC13F2  pop         esi  00DC13F3  pop         ebx  00DC13F4  mov         esp,ebp                            //让esp指向ebp位置00DC13F6  pop         ebp                                    //弹出ebp,00DC13F7  ret                                                    //使其指向调用者调用函数之后的下一条指令的地址00B81444  call        00B810E6                           //下一条指令地址出栈00B81449  add         esp,8                                //将之前放入栈的两个实参从栈中移除,esp下移00B8144C  mov         dword ptr [ebp-20h],eax     //将eax中的值30放入[ebp-20h]中,函数调用也到此结束    14: return 0;

四、函数调用图


阅读全文
2 0