Chinaunix首页 | 论坛 | 博客
  • 博客访问: 972993
  • 博文数量: 403
  • 博客积分: 27
  • 博客等级: 民兵
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-25 22:20
文章分类

全部博文(403)

文章存档

2016年(3)

2015年(16)

2014年(163)

2013年(222)

分类: LINUX

2013-06-10 17:18:15

原文地址:也谈栈和栈帧(四) 作者:r_luo

 
    这次来看看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

阅读(653) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~