栈帧与实例
来源:互联网 发布: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寄存器,把下一条指令的地址
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
- 栈帧与实例
- 实例方法与实例事件
- delphi 与XML实例
- php与Ajax实例
- 触发器技巧与实例
- AppDomain浅析与实例
- log4j详解与实例
- prototype与jquery实例
- GetTextExtentExPoint运用与实例。
- menu介绍与实例
- ListView介绍与实例
- 对话框介绍与实例
- oracle 实例与数据库
- nio原理与实例
- Ajax简介与实例
- BDC 入门与实例
- Aop详解与实例
- ref与out实例
- 第十四周实验报告(2)
- 第十四周实验报告(3)
- 如何在各个版本的VC及64位下使用CPUID指令
- 第十四周实验报告1
- 寻物启事的格式怎样写?
- 栈帧与实例
- 【JS--DOM-节点操作1】 基础 (待整理)
- 第十三周实验报告2.2
- IKEv2的密钥生成过程
- 【人生】震撼人生的小短文(转自网络)
- 嘿嘿,写微博了
- IKEv2的认证数据生成过程
- C 函数调用机制 栈帧(stack frame)
- JavaScript变量作用域的一个问题