ABI或EABI(Extend ABI)通常是处理器体系结构的一部分,它与平台是紧密相连的。我们可以把ABI理解为一套规则,这套规则一般包括定义了以下内容:
1:应用程序如何发出系统调用来trap到内核态。
2:如何使用机器的寄存器。比如,RISC处理器的ABI就要规定用那个通用寄存器来作stack pointer和frame pointer。
3:规定如何进行procedure call。
2, 3是最重要的。而且特定于那个平台的编译器和链接器实现都要遵循这些约定。
首先是寄存器的使用规则:
GPR0:普通用户不能使用此寄存器。GCC编译器使用此寄存器保存LR寄存器。Linux使用该寄存器传递系统调用号。
GPR1:该寄存器保存堆栈的栈顶指针,也就是SP指针。
GPR2:普通用户不能使用此寄存器。Linux用该寄存器保存当前进程的进程描述符指针。
GPR3-GPR4:使用这两个寄存器保存程序的返回值。
GPR3-GPR10:用于传递函数参数。当参数多于8个时,使用存储器传送。
GPR11-GPR12:一般用户不能使用此寄存器。Linux有时使用这两个寄存器存放临时变量。
GPR13:该寄存器用于保存sdata段的基地址指针。类似于MIPS中的$28(GP)寄存器。
GPR14-GPR31:程序可以自由使用这几个寄存器。
其次是运行的堆栈结构:以E500为例:
============================================
| Previous BacK Chain | <---
============================================ |
| 32-bit General Register Save Area | |
============================================ |
| CR Save Area | |
============================================ |
| Padding to 8-byte Boundry | |
============================================ |
| 64-bit General Register Save Area | |
============================================ |
|Local Variable Space(and required padding) | |
============================================ |
| Parameter Save Area | |
============================================ |
| LR Save Word | |
============================================ |
| SP -> Back Chain | ---
============================================
Back Chain:当前栈顶指针寄存器SP保存上一个栈桢的Back Chain的地址。当函数返回时,SP指回上一个栈桢。
LR Save Word:保存LR寄存器的值,用于函数返回。(MIPS也会保存RA寄存器)
Parameter Save Area:用于存放函数参数。当参数多于8个时才用此区域。
Local Variable Area:用于存放临时变量。E500会首先选择GPR14-GPR31保存临时变量,只有当寄存器不够用时才用此区域。
64-bit General Register Save Area:保存函数用到的64位寄存器。
CR Save Area:保存CR寄存器。
32-bit General Register Save Area:保存函数用到的32位寄存器。
注意,根据具体的函数不一样,栈桢的内容是不一样的,最小的栈桢只有Back Chain,LR Save Word和2个字的padding区域,共16字节。
最后PowerPC中的函数调用:
由于PowerPC中没有真正的堆栈指针SP以及堆栈指令push,pop等,所以这些都需要模拟。AB
I(Application Binary Interface)定义了相当的关于这方面的规范。查看汇编指令可以看
到,直接使用偏移地址存储数据来模拟这样的堆栈。进入一个函数,堆栈根据所需要的大小
向下增长,当前堆栈指针sp存入0位置,将LR存入原先堆栈+4位置,然后将非易失性寄存器
存入堆栈中,局部变量,包括参数都放在堆栈中。
以下使用一VxWorks的入口函数简单分析下,中间插入部分汇编。
void usrInit (int startType)
{
stwu r1,-0x20(r1); /* 保存原先sp,同时更新sp */
mflr r0; /* LR 获取 */
stw r31, 0x1C(r1); /* 非易失性reg保存,这只保存r31 */
stw r0, 0x24(r1); /* LR存储在上个堆栈sp+4处 */
mr r31, r1 /* r31为当前sp值 */
stw r3, 0x8(r31) /* 局部变量,也就是startType */
sysStart (startType);
lwz r3, 0x8(r31)
crclr 0x6
bl 0x1E11F8BC /* 函数跳转 */
cacheLibInit (USER_I_CACHE_MODE, USER_D_CACHE_MODE);
li r3, 01
li r4, 01
bl 0x1E80EE0
excVecInit ();
sysHwInit ();
usrCacheEnable ();
usrKernelInit ();
}
lwz r11, 0x0(r1) /* SP恢复 */
lwz r0, 0x4(r1)
mtlr r0 /* LR 恢复 */
lwz r31, -0x4(r11) /* R31恢复 */
mr r1, r11
blr /* usrInit函数返回 */