PowerPC栈帧分析Word下载.doc

上传人:wj 文档编号:3966078 上传时间:2023-05-02 格式:DOC 页数:8 大小:486KB
下载 相关 举报
PowerPC栈帧分析Word下载.doc_第1页
第1页 / 共8页
PowerPC栈帧分析Word下载.doc_第2页
第2页 / 共8页
PowerPC栈帧分析Word下载.doc_第3页
第3页 / 共8页
PowerPC栈帧分析Word下载.doc_第4页
第4页 / 共8页
PowerPC栈帧分析Word下载.doc_第5页
第5页 / 共8页
PowerPC栈帧分析Word下载.doc_第6页
第6页 / 共8页
PowerPC栈帧分析Word下载.doc_第7页
第7页 / 共8页
PowerPC栈帧分析Word下载.doc_第8页
第8页 / 共8页
亲,该文档总共8页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

PowerPC栈帧分析Word下载.doc

《PowerPC栈帧分析Word下载.doc》由会员分享,可在线阅读,更多相关《PowerPC栈帧分析Word下载.doc(8页珍藏版)》请在冰点文库上搜索。

PowerPC栈帧分析Word下载.doc

各部分名词解释:

函数参数域(FunctionParameterArea):

这个区域的大小是可选的,即如果如果调用函数传递给被调用函数的参数少于六个时,用GPR4至GPR10这个六个寄存器就可以了,被调用函数的栈帧中就不需要这个区域;

但如果传递的参数多于六个时就需要这个区域。

局部变量域(LocalVariablesArea):

通上所示,如果临时寄存器的数量不足以提供给被调用函数的临时变量使用时,就会使用这个域。

CR寄存器:

即使修改了CR寄存器的某一个段CRx(x=0至7),都有保存这个CR寄存器的内容。

通用寄存器GPR:

当需要保存GPR寄存器中的一个寄存器器GPRn时,就需要把从GPRn到GPR31的值都保存到堆栈帧中。

浮点寄存器FPR:

使用规则共GPR寄存器。

3. 

PowerPC的汇编指令和栈操作

 

PowerPC寄存器没有专用的push和pop指令来执行堆栈操作,所以PowerPC构架使用存储器访问指令stwu、lwzu来代替push和pop指令。

4.函数执行时栈帧的建立与消亡过程

函数栈的建立与消亡过程如下图所示:

4.1函数栈的建立与消亡过程说明

如前所属,PowerPC体系结构中栈的增长方向是从高地址到低地址,故形成过程可以概括为如下几点:

1)调用函数r1指向栈顶(SP),用间接寻址方式分配一定大小栈空间;

2)r31指向栈顶,以r31为基值将参数压入栈内;

3)进入被调函数,跳转到被调函数的SP处;

4)被调函数同样进行栈分配及参数压栈操作;

5)被调函数执行完毕之后,跳转LR,返回到被调用处的下一条指令,继续后续操作(此时的SP即为调用函数的SP)

4.2举例说明栈操作过程

以下以一个简单的函数调用,说明PowerPC栈的操作过程。

函数例子如下:

intcalltest2(inta)

{

intt1=5;

intt2=6;

intresult=0;

char*p=0;

*p=a;

}

intcalltest1(inta)

intt1=3;

intt2=4;

intresult=0;

result=calltest2(t2);

t1=3;

voidcalltest()

intt1=7;

intt2=9;

result=calltest1(t1);

利用反汇编工具,生成汇编代码及分析如下:

Calltest2栈帧建立分析:

stwur1,-48(r1):

分配48字节的栈帧,r1指向栈顶;

(powerpc省略了EBP,所以一上来即进行一次间接寻址)

stwr31,44(r1):

保存r31的原值,以后恢复;

orr31,r1,r1:

让r31指向栈顶r1(r31=r1orr31)

stwr3,8(r31):

第一个形参

0x401d4f0calltest2:

stwur1,-48(r1)

0x401d4f4+0x004:

stwr31,44(r1)

0x401d4f8+0x008:

orr31,r1,r1

0x401d4fc+0x00c:

stwr3,8(r31)

局部变量赋值:

lir05(t1,t2.result)

0x401d500+0x010:

lir0,0x5#5

0x401d504+0x014:

stwr0,12(r31)

0x401d508+0x018:

lir0,0x6#6

0x401d50c+0x01c:

stwr0,16(r31)

0x401d510+0x020:

lir0,0x0#0

0x401d514+0x024:

stwr0,20(r31)

0x401d518+0x028:

0x401d51c+0x02c:

stwr0,24(r31)

加载函数调用参数到r9

0x401d520+0x030:

lwzr9,24(r31)

0x401d524+0x034:

lbzr0,11(r31)

保存r9到r0

0x401d528+0x038:

stbr0,0(r9)

r11=r1,r31=r11-4=r1-4,恢复r31的值

0x401d52c+0x03c:

lwzr11,0(r1)

0x401d530+0x040:

lwzr31,-4(r11)

0x401d534+0x044:

orr1,r11,r11

blr:

跳转到LR地址,返回calltest1中调用calltest2的下一条指令地址0x401d57c的继续指向

0x401d538+0x048:

blr

0x401d53ccalltest1:

将LR内容存入r0(存在函数调用时需要用到LR,用来存放函数调用结束处的返回地址)

0x401d540+0x004:

mfsprr0,LR

0x401d544+0x008:

0x401d548+0x00c:

stwr0,52(r1)

0x401d54c+0x010:

0x401d550+0x014:

局部变量赋值(t1,t2,result)

0x401d554+0x018:

lir0,0x3#3

0x401d558+0x01c:

0x401d55c+0x020:

lir0,0x4#4

0x401d560+0x024:

0x401d564+0x028:

0x401d568+0x02c:

函数调用

0x401d56c+0x030:

lwzr3,16(r31)

0x401d570+0x034:

bl0x401d4f0#calltest2

0x401d574+0x038:

orr0,r3,r3

0x401d578+0x03c:

0x401d57c+0x040:

0x401d580+0x044:

0x401d584+0x048:

0x401d588+0x04c:

lwzr0,4(r11)

0x401d58c+0x050:

mtsprLR,r0

0x401d590+0x054:

0x401d594+0x058:

返回calltest函数的下一条指令地址0x401d5d8的继续指向

0x401d598+0x05c:

blr

0x401d59ccalltest:

stwur1,-48(r1)

0x401d5a0+0x004:

0x401d5a4+0x008:

0x401d5a8+0x00c:

0x401d5ac+0x010:

0x401d5b0+0x014:

lir0,0x7#7

0x401d5b4+0x018:

stwr0,8(r31)

0x401d5b8+0x01c:

lir0,0x9#9

0x401d5bc+0x020:

0x401d5c0+0x024:

0x401d5c4+0x028:

调用函数calltrst1:

将t1(r31+8)加载到r3中,然后跳转到calltest1地址处(0x401d53c)

0x401d5c8+0x02c:

lwzr3,8(r31)

0x401d5cc+0x030:

bl0x401d53c#calltest1

0x401d5d0+0x034:

保存result返回值

0x401d5d4+0x038:

调用完成,开始后续指令操作

0x401d5d8+0x03c:

0x401d5dc+0x040:

0x401d5e0+0x044:

0x401d5e4+0x048:

0x401d5e8+0x04c:

0x401d5ec+0x050:

0x401d5f0+0x054:

0x401d5f4+0x058:

blr

下面利用断点调试跟踪栈内存执行过程

1)在进入calltest但未执行任何指令(参数还未赋值)时,查看寄存器及内存分布如下:

r0=c7cbd8r1/sp=a8ce6c0r2=0r3=0

r4=0r5=0r6=0r7=0

r8=0r9=0r10=0r11=a8ce738

r12=401d59cr13=0r14=0r15=0

r16=0r17=0r18=0r19=0

r20=0r21=0r22=0r23=0

r24=0r25=0r26=0r27=0

r28=0r29=0r30=0r31=a8ce6c0

msr=b030lr=c7cbd8ctr=0pc=401d5b0

cr=0xer=0mq=eeeeeeee

内存空间为

此时sp=0xa8ce6c0,r31指向r1,pc=0x401d5b0

2)执行到result=0(局部变量赋值完成,但没有调用caltest1)

在紧跟SP之后,SP+8即为局部变量存储区,此时此时sp=0xa8ce6c0

3)再执行result=calltest(t1),跳进calltest1之后但未进行任何操作

r0=401d5d0r1/sp=a8ce690r2=0r3=7

r28=0r29=0r30=0r31=a8ce690

msr=b030lr=401d5d0ctr=0pc=401d554

cr=0xer=0mq=0

此时SP=0Xa8ce690,相对于原SP。

正好移动48字节(即calltest栈空间)。

内存分布为

执行函数到result=calltest2

对于有函数参数的函数,SP+8之后存储函数参数,之后紧跟局部变量。

综上所述,函数栈帧是以SP为基准,采用间接寻址方式申请一段栈空间。

对于没有函数参数的函数,SP+8即为局部变量存储区。

对于带有函数参数的函数,SP+8为函数参数存储区,之后为局部变量存储区。

被调函数(calltest1)的SP为调用函数(calltest)SP减去调用函数申请的空间。

上例中SP1=0xa8ce6c0;

SP2=0xa8ce690;

SP3=0xa8ce660

问题:

x86有两个寄存器,esp,ebp,ppc只用一个寄存器是怎么实现定位堆栈位置的?

分析:

栈是单端伸缩的表,因此,从原理上说,只要有一个栈顶指针就可以操作堆栈了。

X86里的EBP起辅助作用,使栈在函数调用中的应用更方便,更高效。

栈底是固定不变的,因此只要存一个数来指示栈底。

对大多数CPU内置的硬件栈来说,栈底都是全1(可寻址空间的最高地址)。

当栈空时,你还要做弹出,状态寄存器的溢出位也可以用来指示栈溢出。

因此,甚至根本不需要保存栈底。

一个栈指针寄存器完全够用了,实际上大部分架构都用一个寄存器。

x86上ebp也不是必须的,你在编译的时候加上-fomit-frame-pointer参数,ebp就不用了,只用esp。

cpu不需要知道,操作系统知道就可以了。

溢出后会产生pagefault,操作系统只需要在这个时候判断访问的时候是非法地址就可以产生segfault终止程序。

汇编指令参考:

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > PPT模板 > 商务科技

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2