栈帧结构

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

java栈帧的构成 栈帧结构_调用函数

java栈帧的构成 栈帧结构_#include_02

从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等。实现上有硬件方式和软件方式(有些体系不支持硬件栈)首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

注意:ebp指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;esp所指的栈帧顶部和系统栈的顶部是同一个位置。

首先画一个地址空间图给大家加深理解:

java栈帧的构成 栈帧结构_#include_03

 

既然今天分析的是栈帧,那么肯定是在栈区展开研究,下面为大家总体上画一张栈帧结构图:

java栈帧的构成 栈帧结构_main函数_04

 

接下来,首先看一下变量压栈的次序以及对应的汇编代码:

 

#include <stdio.h>
#include <windows.h>
int fun(int x, int y)
{
int c = 0xcccccccc;
printf("I am fun function!\n");
return c;
}
int main()
{
int a = 0xaaaaaaaa;
int b = 0xbbbbbbbb;
int ret = fun(a, b);
printf("you should running here!\n");
system("pause");
return 0;
}

 

java栈帧的构成 栈帧结构_调用函数_05

在main函数调用fun函数之前,也就是main的栈帧结构:main函数的栈底ebp,栈顶esp。从低地址esp到高地址ebp,就是main函数的栈帧:

java栈帧的构成 栈帧结构_调用函数_06

 

现在开始调用fun函数:这里用到了一条汇编指令:call:调用函数。

call有两大功能:①保存当前指令的下一条指令②修改栈底指针ebp。换句话说:call指令的效果是将返回地址入栈,并跳转到被调用过程的起始点。返回地址是在程序中紧跟call后面的那条指令的地址,这样被调用过程返回时,执行会从此处开始。

同样的一点,我们首先看一下汇编代码:

java栈帧的构成 栈帧结构_java栈帧的构成_07

 

从代码和图中不难看出,在main函数里面,地址为008B14A4 的call指令调用函数fun,其中指明了栈帧esp和程序计算器pc的值,call指令随即将返回地址008B14A9压栈,并跳转到fun的第一条指令。到此main栈帧结束。

Call命令出现预示着一个旧的栈帧的结束,也印证了新的栈帧的到来:(汇编代码过长,图中将取重要的点来说)

 

#include <stdio.h>
#include <windows.h>
int fun(int x, int y)
{
int c = 0xcccccccc;
int *p = &x;
printf("I am fun function!\n");
p++;
printf("before: %x\n", y);
*p = 0xdddddddd;
printf("after : %x\n", y);
 
return c;
}
 
int main()
{
int a = 0xaaaaaaaa;
int b = 0xbbbbbbbb;
int ret = fun(a, b);
printf("you should running here!\n");
system("pause");
return 0;
}

 

 

int fun(int x, int y)
{
008B13A0  push        ebp  
008B13A1  mov         ebp,esp  
008B13A3  sub         esp,0D8h  
008B13A9  push        ebx  
008B13AA  push        esi  
008B13AB  push        edi  
008B13AC  lea         edi,[ebp-0D8h]  
008B13B2  mov         ecx,36h  
008B13B7  mov         eax,0CCCCCCCCh  
008B13BC  rep stos    dword ptr es:[edi]  
int c = 0xcccccccc;
008B13BE  mov         dword ptr [c],0CCCCCCCCh  
int *p = &x;
008B13C5  lea         eax,[x]  
008B13C8  mov         dword ptr [p],eax  
printf("I am fun function!\n");
008B13CB  mov         esi,esp  
008B13CD  push        offset string "I am fun function!\n" (8B575Ch)  
008B13D2  call        dword ptr [__imp__printf (8B82B8h)]  
008B13D8  add         esp,4  
008B13DB  cmp         esi,esp  
008B13DD  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  
p++;
008B13E2  mov         eax,dword ptr [p]  
008B13E5  add         eax,4  
008B13E8  mov         dword ptr [p],eax  
printf("before: %x\n", y);
008B13EB  mov         esi,esp  
008B13ED  mov         eax,dword ptr [y]  
008B13F0  push        eax  
008B13F1  push        offset string "before: %x\n" (8B574Ch)  
008B13F6  call        dword ptr [__imp__printf (8B82B8h)]  
008B13FC  add         esp,8  
008B13FF  cmp         esi,esp  
008B1401  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  
*p = 0xdddddddd;
008B1406  mov         eax,dword ptr [p]  
008B1409  mov         dword ptr [eax],0DDDDDDDDh  
printf("after : %x\n", y);
008B140F  mov         esi,esp  
008B1411  mov         eax,dword ptr [y]  
008B1414  push        eax  
008B1415  push        offset string "after : %x\n" (8B573Ch)  
008B141A  call        dword ptr [__imp__printf (8B82B8h)]  
008B1420  add         esp,8  
008B1423  cmp         esi,esp  
008B1425  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  
 
return c;
008B142A  mov         eax,dword ptr [c]  
}
008B142D  pop         edi  
008B142E  pop         esi  
008B142F  pop         ebx  
008B1430  add         esp,0D8h  
008B1436  cmp         ebp,esp  
008B1438  call        @ILT+305(__RTC_CheckEsp) (8B1136h)  
008B143D  mov         esp,ebp  
008B143F  pop         ebp  
008B1440  ret

java栈帧的构成 栈帧结构_java栈帧的构成_08