您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 项目/工程管理 > PowerPC栈帧分析
PowerPC栈帧分析1.PowerPC寄存器的使用规则通用寄存器的用途:r0在函数开始(functionprologs)时使用。r1堆栈指针,相当于ia32架构中的esp寄存器,idapro把这个寄存器反汇编标识为sp。r2内容表(toc)指针,idapro把这个寄存器反汇编标识为rtoc。系统调用时,它包含系统调用号(这个好像跟系统有关吧)。r3作为第一个参数和返回值。r4-r10函数或系统调用开始的参数。r11用在指针的调用和当作一些语言的环境指针。r12它用在异常处理和glink(动态连接器)代码。r13保留作为系统线程ID。r14-r31作为本地变量,非易失性。专用寄存器的用途:lr链接寄存器,它用来存放函数调用结束处的返回地址。ctr计数寄存器,它用来当作循环计数器,会随特定转移操作而递减。xer定点异常寄存器,存放整数运算操作的进位以及溢出信息。msr机器状态寄存器,用来配置微处理器的设定。cr条件寄存器,它分成8个4位字段,cr0-cr7,它反映了某个算法操作的结果并且提供条件分支的机制。2.栈帧的使用规则PowerPC寄存器没有专用的Pop,Push指令来执行堆栈操作,所以PowerPC构架使用存储器访问指令stwu,lwzu来代替Push和Pop指令。PowerPC处理器使用GPR1来将这个堆栈段构成一个单向链表,这个单链表的每一个数据成员,我们称之为堆栈帧(StackFrame),每一个函数负责维护自己的堆栈帧。PowerPC体系结构中栈的增长方向是从高地址到低地址,堆的增长方式是从低地址到搞地址,当两者相遇时就会产生溢出。堆栈帧的格式如下:各部分名词解释:函数参数域(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.函数执行时栈帧的建立与消亡过程函数栈的建立与消亡过程如下图所示:R1(SP)R1(SP)1.申请栈空间,将r1移到栈底R1(SP)LR(函数结束返回地址)R31局部变量2.压参数进栈R1(SP)LR(函数结束返回地址)R31局部变量3.开始调用函数,直接跳转到被调函数地址处R1(SP)R31局部变量R1(SP)LR(函数结束返回地址)R31局部变量R1(SP)被调函数的SP为当前SP4.被调函数申请栈(此处被调函数不再次调用函数)R3-R10(函数参数)调用函数栈图(calltest)被调函数栈图(calltest、calltest2)简化设计,只存在一个调用R1(SP)LR(函数结束返回地址)R31局部变量R1(SP)5.执行调用函数后面的指令被调函数执行完毕,跳转LR,返回到被调处的下一指令地址R1(SP)LR(函数结束返回地址)6.调用函数执行完毕,返回LR由低至高调函数的SP为当前SP4.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;intresult=0;result=calltest1(t1);t1=3;}利用反汇编工具,生成汇编代码及分析如下:intcalltest2(inta){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,r10x401d4fc+0x00c:stwr3,8(r31)局部变量赋值:lir05(t1,t2.result)intt1=5;0x401d500+0x010:lir0,0x5#50x401d504+0x014:stwr0,12(r31)intt2=6;0x401d508+0x018:lir0,0x6#60x401d50c+0x01c:stwr0,16(r31)intresult=0;0x401d510+0x020:lir0,0x0#00x401d514+0x024:stwr0,20(r31)char*p=0;0x401d518+0x028:lir0,0x0#00x401d51c+0x02c:stwr0,24(r31)加载函数调用参数到r9*p=a;0x401d520+0x030:lwzr9,24(r31)0x401d524+0x034:lbzr0,11(r31)保存r9到r00x401d528+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,r11blr:跳转到LR地址,返回calltest1中调用calltest2的下一条指令地址0x401d57c的继续指向0x401d538+0x048:blrintcalltest1(inta){0x401d53ccalltest1:stwur1,-48(r1)将LR内容存入r0(存在函数调用时需要用到LR,用来存放函数调用结束处的返回地址)0x401d540+0x004:mfsprr0,LR0x401d544+0x008:stwr31,44(r1)0x401d548+0x00c:stwr0,52(r1)0x401d54c+0x010:orr31,r1,r10x401d550+0x014:stwr3,8(r31)局部变量赋值(t1,t2,result)intt1=3;0x401d554+0x018:lir0,0x3#30x401d558+0x01c:stwr0,12(r31)intt2=4;0x401d55c+0x020:lir0,0x4#40x401d560+0x024:stwr0,16(r31)intresult=0;0x401d564+0x028:lir0,0x0#00x401d568+0x02c:stwr0,20(r31)函数调用result=calltest2(t2);0x401d56c+0x030:lwzr3,16(r31)0x401d570+0x034:bl0x401d4f0#calltest20x401d574+0x038:orr0,r3,r30x401d578+0x03c:stwr0,20(r31)t1=3;0x401d57c+0x040:lir0,0x3#30x401d580+0x044:stwr0,12(r31)}0x401d584+0x048:lwzr11,0(r1)0x401d588+0x04c:lwzr0,4(r11)0x401d58c+0x050:mtsprLR,r00x401d590+0x054:lwzr31,-4(r11)0x401d594+0x058:orr1,r11,r11返回calltest函数的下一条指令地址0x401d5d8的继续指向0x401d598+0x05c:blrvoidcalltest(){0x401d59ccalltest:stwur1,-48(r1)0x401d5a0+0x004:mfsprr0,LR0x401d5a4+0x008:stwr31,44(r1)0x401d5a8+0x00c:stwr0,52(r1)0x401d5ac+0x010:orr31,r1,r1intt1=7;0x401d5b0+0x014:lir0,0x7#70x401d5b4+0x018:stwr0,8(r31)intt2=9;0x401d5b8+0x01c:lir0,0x9#90x401d5bc+0x020:stwr0,12(r31)intresult=0;0x401d5c0+0x024:lir0,0x0#00x401d5c4+0x028:stwr0,16(r31)调用函数calltrst1:将t1(r31+8)加载到r3中,然后跳转到calltest1地址处(0x401d53c)result=calltest1(t1);0x401d5c8+0x02c:lwzr3,8(r31)0x401d5cc+0x030:bl0x401d53c#calltest10x401d5d0+0x034:orr0,r3,r3保存result返回值0x401d5d4+0x038:stwr0,16(r31)调用完成,开始后续指令操作t1=3;0x401d5d8+0x03c:lir0,0x3#30x401d5dc+0x040:stwr0,8(r31)}0x401d5e0+0x044:lwzr11,0(r1)0x401d5e4+0x048:lwzr0,4(r11)0x401d5e8+0x04c:mtsprLR,r00x401d5ec+0x050:lwzr31,-4(r11)0x401d5f0+0x054:orr1,r11,r110x401d5f4+0x058:blr下面利用断点调试跟踪栈内存执行过程1)在进入calltest但未执行任何指令(参数还未赋值)时,查看寄存器及内存分布如下:r0=c7cbd8r1/sp=a8ce6c0r2=0r3=0r4=0r5=0r6=0r7=0r8=0r9=0r10=0r11=a8ce738r12=401d59cr13=0r14=0r15=0r16=0r17=0r18=0r19=0r20=0r21=0r22=0r23=0r24=0r25=0r26=0r27=0r28=0r29=0r30=0r31=a8ce6c0msr=b030lr=c7cbd8ctr=0pc=401d5b0cr=0xer=0mq=eeeeeeee内存空间为此时sp=0xa8ce6c0,r31指向r1,pc=0x401d5b02)执行到result=0(局部变量赋值完成,但没有调用caltest1)在紧跟SP之后,SP+8即为局部变量存储区,此时此时sp=0xa8ce6c03)再执行result=calltest(t1),跳进calltest1之后但未进行任何操作r0=401d5d0r1
本文标题:PowerPC栈帧分析
链接地址:https://www.777doc.com/doc-2852167 .html