栈帧与实例

来源:互联网 发布:js强制转换成数字 编辑:程序博客网 时间:2024/05/18 09:59

    每个任务(进程)有一个栈,在这个进程中每个函数被调用时分别从这个栈占用一段区域,称为帧(frame).栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。下图为典型的存取器安排,观察栈在其中的位置


 

入栈操作:push eax; 等价于 esp=esp-4,eax->[esp];如下图

出栈操作:pop eax; 等价于 [esp]->eax,esp=esp+4;如下图

我们来看下面这个C程序在执行过程中,栈的变化情况

void func(int m, int n) {

    int a, b;

    a = m;

    b = n;

}

main() {

...

    func(m, n);

L:  下一条语句

...
}

在main调用func函数前,栈的情况,也就是说main的栈帧:

从低地址esp到高地址ebp的这块区域,就是当前main函数的栈帧。当main中调用func时,写成汇编大致是:

push m

push n; 两个参数压入栈

call func; 调用func,将返回地址填入栈,并跳转到func

当跳转到了func,来看看func的汇编大致的样子:

__func:

        push ebp; 这个很重要,因为现在到了一个新的函数,也就是说要有自己的栈帧了,那么,必须把上面的函数main的栈帧底部保存起                        ; 来,栈顶是不用保存的,因为上一个栈帧的顶部讲会是func的栈帧底部。(两栈帧相邻的)

        mov ebp, esp; 上一栈帧的顶部,就是这个栈帧的底部

        ;暂时先看现在的栈的情况

                 ;到这里,新的栈帧开始了

                 sub esp, 8   ;  int a, b 这里声明了两个int,所以esp减小8个字节来为a,b分配空间

                 mov dword ptr [esp+4], [ebp+12];   a=m

                 mov dword ptr [esp], [ebp+8]; b=n         

   这样,栈的情况变为:

                    ret 8     ;  返回,然后8是什么意思呢,就是参数占用的字节数,当返回后,esp-8,释放参数m,n的空间

 

由此可见,通过ebp,能够很容易定位到上面的参数。当从func函数返回时,首先esp移动到栈帧底部(即释放局部变量),然后把上一个函数的栈帧底部指针弹出到ebp,再弹出返回地址到cs:ip上,esp继续移动划过参数,这样,ebp,esp就回到了调用函数前的状态,即现在恢复了原来的main的栈帧。

   esp寄存器指向当前整个栈的栈顶,ebp指向当前帧的帧底。不是当前帧(调用者)的帧底都已经被压栈。上一级调用者的帧底被压入当前ebp内容所指的地址,也就是当前帧的帧底位置保存了上一级调用者的ebp指针值(帧底),而每个ebp的前一个单元存放的就是当前函数的返回地址(它是由调用者在call指令中入的栈),保证是在上以及帧的最后一个空间单元。这样就可以根据当前%ebp的值回溯出整个任务的调用栈(调用过程)。

 

栈帧操作的例子(基于POWERPC):

void vSetTask(int task, int pri)
{
            int c = iAdd(1, 6);
            Output("Error:%d/n", c);
            return;
}
int iAdd(int a, int b)
{
    return a + b;
}

-> l vSetTask,50
                 vSetTask:
0xb0f56c  9421ffd0    stwu        r1,-48(r1)       //保存老堆栈指针内容并更新堆栈

                                                                              //指针:mem(r1-48) <- (r1);r1 <- r1-48;


0xb0f570  7c0802a6    mfspr       r0,LR        //由于后面还要调用其它函数,会修改LR寄存器的值,

                                                                            //因此需要保存LR寄存器到堆栈中,注意保存地址在本

                                                                            //函数的调用者堆栈区,这个地址在每个函数的堆栈区都

                                                                           //是预留的,因为不知道被调用者是否需要保存LR。而后面

                                                                           //的iAdd函数由于不会再改变LR的值因此就不需要保存LR。
0xb0f574  93e1002c    stw         r31,44(r1)
0xb0f578  90010034    stw         r0,52(r1)
0xb0f57c  7c3f0b78    or          r31,r1,r1
0xb0f580  907f0008    stw         r3,8(r31)        //r3、r4分别保存了本函数的两个输入参数,虽然没有使用

                                                                              //,这里也没有被优化掉,需要保存。
0xb0f584  909f000c    stw         r4,12(r31)
0xb0f588  38600001    li          r3,0x1 # 1
0xb0f58c  38800006    li          r4,0x6 # 6
0xb0f590  48000045    bl          0xb0f5d4 # iAdd      //bl:最低位为1影响LR寄存器,把下一条指令的地址

                                                                                        //存入LR,然后跳到目的地址执行
0xb0f594  7c601b78    or          r0,r3,r3
0xb0f598  901f0010    stw         r0,16(r31)
0xb0f59c  3d20012f    lis         r9,0x12f # 303
0xb0f5a0  38692e6c    addi        r3,r9,0x2e6c # 11884
0xb0f5a4  809f0010    lwz         r4,16(r31)
0xb0f5a8  4cc63182    crxor       crb6,crb6,crb6
0xb0f5ac  4b7f35ed    bl          0x302b98 # Output(char *,...)
0xb0f5b0  4800000c    b           0xb0f5bc # 0x00b0f5bc
0xb0f5b4  48000008    b           0xb0f5bc # 0x00b0f5bc
0xb0f5b8  48000004    b           0xb0f5bc # 0x00b0f5bc
0xb0f5bc  81610000    lwz         r11,0(r1)
0xb0f5c0  800b0004    lwz         r0,4(r11)
0xb0f5c4  7c0803a6    mtspr       LR,r0
0xb0f5c8  83ebfffc    lwz         r31,-4(r11)
0xb0f5cc  7d615b78    or          r1,r11,r11    //r1 <- (r11)
0xb0f5d0  4e800020    blr                       //blr是bclr的简写
                 iAdd:
0xb0f5d4  9421ffe0    stwu        r1,-32(r1)
0xb0f5d8  93e1001c    stw         r31,28(r1)
0xb0f5dc  7c3f0b78    or          r31,r1,r1
0xb0f5e0  907f0008    stw         r3,8(r31)
0xb0f5e4  909f000c    stw         r4,12(r31)
0xb0f5e8  801f0008    lwz         r0,8(r31)
0xb0f5ec  813f000c    lwz         r9,12(r31)
0xb0f5f0  7c004a14    add         r0,r0,r9
0xb0f5f4  7c030378    or          r3,r0,r0
0xb0f5f8  4800000c    b           0xb0f604 # 0x00b0f604
0xb0f5fc  48000008    b           0xb0f604 # 0x00b0f604
0xb0f600  48000004    b           0xb0f604 # 0x00b0f604
0xb0f604  81610000    lwz         r11,0(r1)
0xb0f608  83ebfffc    lwz         r31,-4(r11)
0xb0f60c  7d615b78    or          r1,r11,r11
0xb0f610  4e800020    blr        
0xb0f614  9421fff0    stwu        r1,-16(r1)
0xb0f618  7c0802a6    mfspr       r0,LR
0xb0f61c  93e1000c    stw         r31,12(r1)
0xb0f620  90010014    stw         r0,20(r1)
0xb0f624  7c3f0b78    or          r31,r1,r1
0xb0f628  3d20015c    lis         r9,0x15c # 348
0xb0f62c  8009b478    lwz         r0,-19336(r9)
0xb0f630  2c000000    cmpi        crf0,0,r0,0x0 # 0
value = 11597364 = 0xb0f634 = iAdd + 0x60



原创粉丝点击