首先,了解为什么需要这些寄存器的一些背景知识。PPC 是一种类似于 RISC 的 ISA,因为所有指令的大小都相同(32 位),并且用于地址等内容的立即值(通常最多 16 位)的空间有限。那么你如何解决超过 16 位的问题呢?
一种选择是按 16 位切片构建地址,例如:
lis r4,msg@ha # load top 16 bits of &msg
addi r4,r4,msg@l # add bottom 16 bits
另一种是使用 PIC(位置无关代码)来寻址与当前地址相关的事物:
bcl 20, 4*cr7+so, loc_picbase #branch and link to next location
loc_picbase:
mflr r31 # load link register (lr) into r31. It will be equal to loc_picbase
addis r2, r31, (_NXArgc - loc_picbase)@ha
stw r28, (_NXArgc - loc_picbase)@l(r2)
<and so on>
然而,使用其中任何一个都需要每次数据访问多条指令,并且并不总是可行(例如,如果二进制文件需要重新定位,则第一个变体将需要修补指令,而这在运行只读代码时无法完成)。因此,在某些平台上,他们提出了 TOC(目录)的概念,它是一个固定寄存器,对整个模块具有相同的值,并且所有数据都使用偏移量寻址。该寄存器r2
是为此目的保留的,AFAIK 应该由 OS 加载程序设置。由于 PPC 指令中的偏移量限制为 16 位(-32768 到 +32767),因此您可以寻址大约 64K 的数据r2
(通常放置在数据区域的中间以获得最大的覆盖范围)。如果模块需要更多的数据,大于 16 位的偏移量会分几个步骤构造,或者可以使用多个 TOC(在这种情况下,不同的功能可能使用不同的 TOC)。
在 PPC64 ABI 中,后者是通过所谓的函数描述符实现的,它是一对指针,其中一个是函数代码的地址,另一个是函数期望的 TOC 值。任何时候调用函数时,都会在跳转到函数代码之前从描述符加载 TOC(除非编译器知道目标函数使用相同的 TOC 值,在这种情况下可以跳过 TOC 重新加载)。
在嵌入式环境中,通常没有操作系统,只有一个单片固件,这个概念通过使用寄存器r13
SDA(小数据区)和r2
额外的数据区(SDA2 -小数据区 2)来应用到整个固件)。所谓的零数据区 (ZDA) 也可用于放置在地址 0 附近的数据(使用寄存器中的特殊寻址模式r0
)。
一些编译器(特别是青山)支持额外的数据指针(r14
,r15
等等)。
在嵌入式环境中,数据寄存器(r2
,r13
等)应在启动代码初始化,复位后不久,随着固件的其余部分通常希望他们拥有正确的价值观。
PS 的使用r2
取决于环境。您正在编译的目标环境很可能不使用 TOC,因此您没有看到它被使用(PowerPC 上的堆栈指针是r1
,而不是r2
)。
一些参考: