PowerPC 在处理器级别上是没有实现堆栈操作的,也就是说,在 PowerPC 架构中,没有专门的堆栈操作汇编命令。但事实上 PowerPC 又使用到了堆栈,比如在函数调用、传参、返回错误码等操作。
在讲 PowerPC 堆栈处理之前,先提一下要用到的某些寄存器。首先是32个通用寄存器 GPR0 ~ GPR31。据说这32个寄存器是64位的,但是高32位的访问方式不同,通常只用低32位,所以我们暂且把他们当作32位的寄存器来用好了。
按照 PowerPC EABI (Embedded Application Binary Interface) 的说法:GPR0 由于语言的不同而用处不同,gcc 使用 GPR0 来保存 LR 寄存器的值;GPR1 用于存放堆栈指针,通常是栈顶指针;GPR3 ~ GPR4 用于传参和保存返回值;GPR5 ~ GPR10 也用于传参;GPR13 保存 sdata 段的段基址;GPR14 ~ GPR31 是可供用户自由使用的。LR 寄存器保存返回地址。
操作堆栈,通常使用到两个类型的指令:{stw, stwu} 和 lwz。
stw
stw rA, off(rB)stw 相当于 push,表示32位的 rA 寄存器的值,保存到 (rB 的值 + off) 所指的位置上。stwu 表示在 stw 的基础上,同时更新 rA 的值为 (rB 的值 + off)。
lwz
lwz rA, off(rB)lwz 相当于 pop,表示把地址为 (rB 的值 + off) 所指位置的值,保存到32位寄存器 rA 中。
来看一简单的程序是怎样进行堆栈操作的:
//hello.c
#include <stdio.h>
int plus(int a, int b)
{
return (a + b);
}
int main()
{
int a = 11;
int b = 22;
plus(a, b);
return 0;
}$ ppc_4xxFP-gcc -S hello.c汇编代码主要部分,从 main 开始执行:
plus:
stwu 1,-32(1) ;初始化新的堆栈,用于 plus() 函数,并把 main 中的 gpr1 压入栈顶,并得到新的栈顶 new_gpr1
stw 31,28(1) ;把 gpr31 压入栈中偏移为28这个位置
mr 31,1
stw 3,8(31) ;把 gpr3 中的参数 a 压入堆栈偏移为8的位置
stw 4,12(31) ;把 gpr4 中的参数 b 压入堆栈偏移为12的位置
lwz 9,8(31) ;从堆栈中取出参数 a,保存在 gpr9 中
lwz 0,12(31) ;从堆栈中取出参数 b,保存在 gpr0 中
add 0,9,0 ;执行 a + b 语句
mr 3,0 ;把 a+b 的结果保存到 gpr3 中,这是 plus() 函数的返回值
lwz 11,0(1) ;取出 gpr1 保存在 gpr11 中
lwz 31,-4(11) ;XXX
mr 1,11 ;恢复 new_gpr1 为 main 中的 gpr1
blr ;返回 main
main:
stwu 1,-32(1) ;初始化堆栈,大小为32字节,现在 gpr1 更新为 (orig_gpr1-32),并把 orig_gpr1 压入栈顶
mflr 0 ;把 LR 保存到 gpr0 中
stw 31,28(1) ;把 orig_gpr31 压入栈中偏移为28这个位置
stw 0,36(1) ;把 LR 压入栈中偏移为36这个位置(超出了栈的空间)
mr 31,1 ;把 gpr1 保存到 gpr31
li 0,11 ;赋值语句:a=11;
stw 0,12(31) ;把 a 压入栈中偏移为12的位置
li 0,22 ;赋值语句:b=22;
stw 0,8(31) ;把 a 压入栈中偏移为8的位置
lwz 3,12(31) ;把 a 出栈,存到 gpr3 中
lwz 4,8(31) ;把 b 出栈,存到 gpr4 中
bl plus ;调用 plus() 函数,CPU 自动设置 LR 寄存器
li 0,0 ;把立即数0加载到 gpr0 寄存器中
mr 3,0 ;把 gpr0 的值(0)存入到 gpr3 寄存器中,这是 main 函数的返回值
lwz 11,0(1) ;把 orig_gpr1 的值存入 grp11 中
lwz 0,4(11) ;把 36(1) 的值存入 gpr0 中
mtlr 0 ;恢复 LR 寄存器的值
lwz 31,-4(11) ;XXX
mr 1,11 ;恢复 gpr1 的值为 orig_gpr1
blr ;main 函数返回
可以看出,两个函数都有自己的栈空间,通过栈顶组成一个链表的形式,也就是说可以通过某个栈中栈顶的值,找到前一个栈的栈顶位置。
那两条 lwz 31,-4(11) 不知道有什么作用。只是需要注意 stw 和 lwz 并不更新堆栈指针的值,而 stwu 会更新堆栈指针的值。
因为 stw 需要指定数据存放在栈中的偏移,这就造成编译器在编译的过程中,某些变量的存储位置在栈中可能是随机的,所以想要在内联汇编中,像 push 和 pop 那样简单的使用 stw 和 lwz 来操作栈,可能会比较麻烦。
参考:
- 《PowerPC Linux详解——核心篇》
- AMCC PPC440 Processor User’s Manual
- Developing PowerPC Embedded Application Binary Interface (EABI) Compliant Programs

PowerPC处理器没有专门的堆栈指令,但在函数调用等场景中使用堆栈。根据PowerPC EABI,GPR1作为堆栈指针,stw和lwz用于堆栈数据存取,但不更新指针,stwu则会。内联汇编使用这些指令操作栈可能复杂。
1万+

被折叠的 条评论
为什么被折叠?



