也谈栈和栈帧(四)

tech2024-11-03  22

这次来看看PowerPC体系架构CPU的栈帧布局和操作方法。PowerPC用得不多,有不对的地方大家拍砖啊~~ 1.  PowerPC的栈帧     先来看看PowerPC的栈帧布局图:          上图描述的是PowerPC的栈帧布局方式,PowerPC的栈生长方向也是由高到低,caller是调用者,current是被调用者。压栈的顺序依次是FPR、GPR、CR、Local Variable、Function Parameters、Padding、LR和Back Chain Word。具体涵义如下:   (1)函数参数域FPR(Function Parameter Register):这个区域的大小是变化的,当调用者传递给被调用者的参数少于8个时,用GPR3-GPR10这8个寄存器就行,被调用者的栈帧中就可不要这个区域;但如果传递的参数多于8个时就需要这个区域。   (2)通用寄存器GPR(General Parameter Register):当需要保存GPR寄存器中的一个寄存器GPRx时,就需要把从GPRx-GPR31的值都保存到堆栈帧中。   (3)CR寄存器:即使修改了CR寄存器的某一个段CRx(x=0至7),都要保存这个CR寄存器的内容。   (4)局部变量域(Local Variables Area):同上FPR所示,如果临时寄存器的数量不足以提供给被调用者的临时变量使用时,就会使用这个区域。   (5)Function Parameters:跟第一个FPR重复了?暂时不知。   (6)Padding:是补齐字节数,让当前栈帧的长度保持8Bytes的倍数。   (7)LR:也就是ra寄存器,是指返回时的函数指针。   (8)Back Chain Word:是调用者函数帧的栈顶esp,即上一个栈帧的低地址,当前函数栈帧的基址ebp。     跟x86和ARM一样,压栈的顺序有一定的规律,一个栈空间内的地址前面,必然有一个代码地址明确标示着调用函数位置内的某个地址。而且很容易发现,跟x86一样(如果x86中ebp算是调用者栈帧的话),栈帧的最后两个位置存储的也是ra和ebp。所以可以考虑向x86学习,根据当前ebp的值回溯出整个任务的调用栈,如图中蓝箭头所示,具体操作后面再专门讲述。   2.  PowerPC的寄存器     PowerPC的ABI规定的寄存器的使用规则如下:   (1)GPR0:属于易失性寄存器,ABI规定普通用户不能使用此寄存器。GCC编译器用此寄存器来保存LR寄存器,Linux PowerPC用此寄存器来传递系统调用号码。   (2)GPR1:属于专用寄存器,ABI规定用次寄存器来保存堆栈的栈顶指针。注:PowerPC构架没有独立的栈顶指针,这一点和X86体系结构是不同的!   (3)GPR2:属于专用寄存器,ABI规定普通用户不使用才寄存器,Linux PowerPC用此寄存器来保存当前进程的进程描述符地址。   (4)GPR3-GPR4:属于易失性寄存器,ABI使用这两个寄存器来保存函数的返回值,或者用来传递参数。   (5)GPR5-GPR10:也属于易失性寄存器,加上GPR3和GPR4共8个寄存器用来传递函数的参数。当函数的参数超过八个时使用堆栈来传递。   (6)GPR11-GPR12:属于易失性寄存器,ABI规定普通用户不使用该寄存器,Linux PowerPC有时用这两个寄存器来存放临时变量,但是GCC编译器没有使用这两个寄存器。   (7)GPR13:属于专用寄存器,ABI规定该寄存器sdata段的基地址指针。Linux PowerPC在系统初始化时使用该寄存器来存放临时变量。GCC有时会根据某些规则将一些常用的数据放入sdata或者sbss段中。应用程序对sdata或者sbss段数据的访问与对data和bss段数据的访问机制不同,访问sdata段的数据速度更快。   (8)GPR14-GPR31:属于非易失性寄存器。ABI使用这些寄存器来存放一些临时变量,在应用程序中可以自由使用这些变量。   3.  PowerPC的汇编指令和栈操作     PowerPC寄存器没有专用的push和pop指令来执行堆栈操作,所以PowerPC构架使用存储器访问指令stwu、lwzu来代替push和pop指令。     下面我们通过一个例子来说明堆栈帧的建立、使用和移除过程:     func1中开始几行汇编会为自己建立栈帧:func1:    mflr %r0                ;Get link register           stwu %r1,-88(%r1)       ;Save back chain then move sp           stw %r0,+92(%r1)        ;Save link register           stmw %r28,+72(%r1)      ;Save 4 non-volatiles r28-r31     func1的结尾几行,会移除前面建立的栈帧,并使得SP(即GPR1)寄存器指向上一个栈帧的栈顶(即栈帧的最低地址处,也就是back chair)     如下所示:          lwz %r0,+92(%r1)       ;Get saved link register           mtlr %r0               ;Restore link register           lmw %r28,+72(%r1)      ;Restore non-volatiles           addi %r1,%r1,88        ;Remove frame from stack           blr                    ;Return to caller function
最新回复(0)