PowerPC构架应用程序二进制接口(ABI)及堆栈帧详解
标签:
abieabi堆栈帧stack-frame |
分类: PowerPC平台 |
第一部分 概述
应用程序二进制接口(ABI-Application Binary Interface)定义了一组在PowerPC系统软件上编译应用程序所需要遵循的一套规则。主要包括基本数据类型,通用寄存器的使用,参数的传递规则,以及堆栈的使用等等。ABI简单明了,但是比较难以理解的是堆栈帧的使用和维护。本文重点介绍PowerPC平台上堆栈帧的使用和维护,关于ABI其它方面的内容简单的介绍一下,O(∩_∩)O~
第二部分 详细内容
2.1 PowerPC构架使用的基本数据类型
PowerPC构架定义的基本数据类型的格式和标准C相同,具体格式如下:
http://s15/middle/70dd1691g792ed9f302ee&690
2.2 PowerPC构架寄存器的使用规则
PowerPC的ABI规定的寄存器的使用规则如下:
GPR0:属于易失性寄存器,ABI规定普通用户不能使用此寄存器。GCC编译器用此寄存器来保存LR寄存器,Linux-PowerPC用此寄存器来传递系统调用号码。
GPR1:属于专用寄存器,ABI规定用次寄存器来保存堆栈的栈顶指针。
备注:PowerPC构架没有独立的栈顶指针,这一点和X86体系结构是不同的。
GPR2:属于专用寄存器,ABI规定普通用户不使用才寄存器,Linux PowerPC用此寄存器来保存当前进程的进程描述符地址。
GPR3至GPR4:属于易失性寄存器,ABI使用这两个寄存器来保存函数的返回值,或者用来传递参数。
GPR5值GPR10:也属于易失性寄存器,加上GPR3和GPR4共8个寄存器用来传递函数的参数。当函数的参数超过八个时使用堆栈来传递。
GPR11至GPR12:属于易失性寄存器,ABI规定普通用户不使用该寄存器,Linux PowerPC有时用这两个寄存器来存放临时变量,但是GCC编译器没有使用这两个寄存器
GPR13:属于专用寄存器,ABI规定该寄存器sdata段的基地址指针。Linux PowerPC在系统初始化时使用该寄存器来存放临时变量。GCC有时会根据某些规则将一些常用的数据放入sdata或者sbss段中。应用程序对sdata或者sbss段数据的访问与对data和bss段数据的访问机制不同,访问sdata段的数据速度更快。
GPR14至GPR31:属于非易失性寄存器。ABI使用这些寄存器来存放一些临时变量,在应用程序中可以自由使用这些变量。
总结成一张表格如下所示:
http://s13/middle/70dd1691gbbd4940f744c&690
第二部分 栈帧的使用规则
PowerPC寄存器没有专用的Pop,Push指令来执行堆栈操作,所以PowerPC构架使用存储器访问指令stwu,lwzu来代替Push和Pop指令。PowerPC处理器使用GPR1来将这个堆栈段构成一个单向链表,这个单链表的每一个数据成员,我们称之为堆栈帧(Stack Frame),每一个函数负责维护自己的堆栈帧。
PowerPC体系结构中栈的增长方向是从高地址到低地址,堆的增长方式是从低地址到搞地址,当两者相遇时就会产生溢出。
堆栈帧的格式如下:
http://s10/middle/70dd1691g792edc1e59d9&690
备注:PowerPC构架规定栈帧的长度是8字节对齐的,栈帧的长度是8的倍数,如果不足8的整数倍,就如上图所示来补字节凑8的倍数。
解释:
函数参数域(Function Parameter Area):这个区域的大小是可选的,即如果如果调用函数传递给被调用函数的参数少于六个时,用GPR4至GPR10这个六个寄存器就可以了,被调用函数的栈帧中就不需要这个区域;但如果传递的参数多于六个时就需要这个区域。
局部变量域(Local Variables Area):通上所示,如果临时寄存器的数量不足以提供给被调用函数的临时变量使用时,就会使用这个域。
CR寄存器:即使修改了CR寄存器的某一个段CRx(x=0至7),都有保存这个CR寄存器的内容。
通用寄存器GPR:当需要保存GPR寄存器中的一个寄存器器GPRn时,就需要把从GPRn到GPR31的值都保存到堆栈帧中。
浮点寄存器FPR:使用规则共GPR寄存器。
下面我们通过几个例子来说明堆栈帧的建立和使用过程:
例子一:调用Funx()函数
FunX下中开始几行汇编会为自己建立堆栈帧:
FunX:
FunX的结尾几行,会移除面建立的堆栈帧,并使得SP(即GPR1)寄存器指向上一个栈帧的栈顶(即栈帧的最低地址处,也就是back chair)
代码如下:
lwz %r0,+92(%r1)
mtlr
%r0
lmw
%r28,+72(%r1)
addi
%r1,%r1,88
blr
例子二:
#include <stdio.h>
void fun()
{
}
int main()
{
}
建立堆栈帧和移除堆栈帧的汇编语言:
100004f4 <fun>:
100004f4:
100004f8:
100004fc:
10000500:
10000504:
10000508:
1000050c:
10000510 <main>:
10000510:
10000514:
10000518:
1000051c:
10000520:
10000524:
10000528:
1000052c:
10000530:
10000534:
10000538:
1000053c:
10000540:
10000544:
10000548:
分析如下图所示:
http://s10/middle/70dd1691g792ee66157d9&690
备注:
当前函数(比如上面的fun)所维护堆栈帧中的backchain存放的是调用它的函数(即main函数)堆栈帧的栈顶地址。
另外,很多C程序中进行的汇编语言函数调用比较简单,例如汇编函数的参数个数一般少于八个,使用临时的通用寄存器GPR14至GPR31就足够了。所以在这种情况下,我们只要保持堆栈中的LR和Back Chain就足够用的了。
但是正如例子二所示:虽然只需要8个字节来保存LR和和Back Chain就行了,但是GCC编译器在建立堆栈帧时还额外分配了8个字节,其中的4个字节来保存r31的值,另外4个字节的空间并没有使用起来。
备注:例子二中所描述的帧结构是PowerPC构架中最小帧结构,这种帧结构的大小为16字节。
参考资料:
Developing PowerPC Embedded Application Binary Interface (EABI) Compliant Programs

加载中…