本资源的原文使用英文撰写。 为方便起见,TI 提供了译文;由于翻译过程中可能使用了自动化工具,TI 不保证译文的准确性。 为确认准确性,请务必访问 ti.com 参考最新的英文版本(控制文档)。
本文档是德州仪器 (TI) 的 C28x 系列处理器的基于 ELF 的嵌入式应用二进制接口 (EABI) 规范。EABI 是宽泛的标准,定义了程序、程序组件和执行环境(如果存在操作系统,还包括操作系统)之间的低级别接口。EABI 的组件包括调用约定、数据布局和寻址约定、和目标文件格式。
本规范旨在使 C28x 的工具提供商、软件提供商和用户能够构建彼此可互操作的工具和程序。
在 TI 的 C28x 编译器工具 18.12.0.LTS 版本发布之前,C28x 的唯一 ABI 是基于 COFF 的原始 ABI。它严格上来说是一个裸机 ABI;没有执行级别的组件。
TI 编译器工具的 18.12.0.LTS 版本引入了一种名为 C28x EABI 的新 ABI。它基于 ELF 目标文件格式。它源自业界标准模型,包括 IA-64 C++ ABI 和用于 ELF 和动态链接的 System V ABI。ABI 的处理器特定方面(例如数据布局和调用约定)与 COFF ABI 相比基本没有变化,尽管存在一些差异。毋庸置疑,COFF ABI 和 EABI 是不兼容的;也就是说,给定系统中的所有代码都必须遵循相同的 ABI。TI 的编译器工具支持新的 EABI 和旧的 COFF ABI,但我们鼓励迁移到新的 ABI,因为未来可能会停止支持 COFF ABI。
平台 是程序运行所在的软件环境。ABI 具有特定于平台的方面,尤其是在与执行环境相关的约定领域,例如程序段的数量和使用、寻址约定、可见性约定、抢占、程序加载和初始化。目前裸机是唯一受支持的平台。裸机 一词表示不存在任何特定环境。这并不是说不能有操作系统,而是说没有特定于操作系统的 ABI 规范。换句话说,裸机 ABI 未涵盖程序的加载和运行方式以及它如何与系统的其他部分进行交互。
裸机 ABI 允许在许多具体方面存在很大的可变性。例如,实现可能提供位置独立性 (PIC),但如果给定系统不要求位置独立性,则这些约定不适用。由于这种可变性,程序可能仍然符合 ABI,但不兼容;例如,如果一个程序使用 PIC,但另一个程序不使用,则它们无法互操作。工具链应努力强制执行此类不兼容性。
图 1-1 显示了 ABI 的组成部分及其关系。我们将从下到上简要描述图中的这些组成部分,并提供在此 ABI 规范中参考的相应章节。
底部区域的组成部分与目标级互操作性有关。
C 语言 ABI(Chapter2、Chapter3、Chapter4、Chapter5、Chapter6和Chapter7)规定了函数调用约定、数据类型表示、寻址约定和 C 运行时库的接口。
C++ ABI(Chapter8)规定了如何实现 C++ 语言;这包括有关虚拟函数表、名称改编、如何调用构造函数以及异常处理机制(Chapter9)的详细信息。C28x C++ ABI 基于流行的 IA-64 (Itanium) C++ ABI。
DWARF 组成部分(Chapter10)规定了目标级调试信息的表示。基本标准是 DWARF3 标准。此规范详细说明了处理器特定的扩展。
ELF 组成部分(Chapter11)规定了目标文件的表示。该规范为系统 V ABI 规范扩充了处理器特定的信息。
构建属性(Chapter13)是指一种将影响对象间兼容性的各种形参(如目标设备假设、内存模型或 ABI 变体)编码到目标文件中的方法。工具链可以使用构建属性来防止组合或加载不兼容的目标文件。
图中间区域的组成部分与执行时互操作性有关。
图 1-1 顶部的组成部分为 ABI 扩充了平台特定的约定,后者可以定义使可执行文件与执行环境兼容的要求,如程序段的数量和使用、寻址约定、可见性约定、抢占、程序加载和初始化。裸机是指缺失任何具体环境。
最后,有一组规范不是 ABI 的正式组成部分,但本文档进行了介绍以供参考,同时供其他工具链选择实现。
初始化(Chapter14)是指初始化变量赖以获取其初始值的机制。名义上,这些变量驻留在 .data 段中,在加载 .data 段时会直接将它们初始化,不需要工具额外参与。然而,TI 工具链支持一种机制,通过该机制,.data 段能够以压缩形式编码到目标文件中,并在启动时解压缩。这是一种通用机制的特殊用法,该机制以编程方式将压缩后的代码或数据从离线存储(例如 ROM)复制到其执行地址。我们将该过程称为复制表。虽然不是 ABI 的一部分,但本文档介绍了初始化和复制表机制,以便在需要时通过其他工具链使用。
如前所述,ABI 并未定义所有情况下的具体行为,而是一套允许平台或系统特定变化的原则规范。ABI 中存在可以使用或不使用的模型变体。ABI 在使用此类变体的情况下对实现进行了标准化。有些变体彼此不兼容。如果任何对象使用特定的模型,则所有对象都必须使用。在这种情况下,工具链应使用构建属性来防止组合不兼容的对象。
此 ABI 不特定于任何特定供应商的工具链。实际上,它的目的是使替代工具链能够存在并可互操作。ABI 描述了如何实现机制;而不是工具链如何在用户层面支持它们。有时会提到 TI 工具,它们仅供说明之用。不过,TI 的 C28x 编译器工具本质上具有独特的地位,因为它们源自器件供应商,并根据 ABI 规范共同开发,在某些情况下构成了后者的基础。
如果 TI 工具的行为与本 ABI 相冲突,则应将其视为工具中的缺陷;如果您发现此类情况,请将缺陷报告提交至 support@tools.ti.com。然而,若本规范不完整或不明确,TI 工具的行为应视为具有决定性。ABI 标准的主要目标是与 TI 工具实现互操作;工具链供应商应努力实现该目标,而无论标准本身是否有遗漏或岐义。在这种情况下请通知我们,我们将努力澄清规范。
通常情况下,工具链包括链接器以及标准运行时库,这些库实现工具链提供的部分语言支持。
C28x 所用库格式是通用 GNU/SVR4 ar 格式。
链接器和库通常具有 ABI 范围之外的相互依赖性。例如,许多链接器使用特殊符号来控制各种库组件的包含或排除;或者,某些库会引用特殊的链接器定义符号。因此,链接器和库应来自同一工具链。如果所用链接器来自一个工具链,而库却来自另一个工具链,则该 ABI 不会支持。这仅适用于属于工具链一部分的内置库;可以链接使用不同工具链构建的应用库。
ELF 定义了以下不同类别的目标文件:
此规范使用可互换的术语静态链接单元 和加载模块 来指代可执行文件。
ELF 加载模块(可执行文件)以段 形式表示程序的存储器映像。在这种语境下,段是指具有共同属性的连续的、不可分割的存储器范围。当段的地址确定后,它便成为联编段,这在链接时静态发生。
C28x 器件具有 16 位和 32 位 CPU 寄存器。
C28x 器件具有 32 位地址寄存器,但几乎所有 C28x 器件都只有一个 22 位地址空间。
C28x 仅在小端字节序模式下编译。
C28x 可按字寻址,字为 16 位。
C28x 器件上没有 8 位对象。这给在 C28x 器件上实现 ELF 目标文件格式带来了独特的挑战。有关 ELF 文件的更多信息,请参阅Chapter11。
C28x EABI 只支持一种内存模型:统一大内存模型。在此模型中,指针和 ptrdiff_t 类型均为 32 位。
有关指针的信息,请参见节 2.4。
文档标题 | 链接或 URL |
---|---|
TMS320C28x 优化 C/C++ 编译器用户指南 | SPRU514 |
TMS320C28x 汇编语言工具用户指南 | SPRU513 |
TMS320C28x DSP CPU 和指令集参考指南 | SPRU430 |
ELF 规范 - GABI 第 4/5 章 | http://www.caldera.com/developers/gabi/2003-12-17/contents.html |
IA64 (Itanium) C++ ABI | http://refspecs.linux-foundation.org/cxxabi-1.83.html |
IA64 (Itanium) 异常处理 ABI | http://www.codesourcery.com/public/cxx-abi/abi-eh.html |
ARM 架构的应用二进制接口 | http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html |
适用于 ARM 架构的 C 库 ABI | http://infocenter.arm.com/help/topic/com.arm.doc.ihi0039b/IHI0039B_clibabi.pdf |
DWARF 调试格式版本 3 | http://dwarfstd.org/Dwarf3.pdf |
C 语言标准 | http://www.open-std.org/jtc1/sc22/wg14,ISO/IEC 9899:1990 |
C99 语言标准 | http://www.open-std.org/jtc1/sc22/wg14,ISO/IEC 9899 |
C++ 语言标准 | http://www.open-std.org/jtc1/sc22/wg21,ISO/IEC 14882:1998 |
在本文档中,我们使用代码片段来说明寻址、调用序列等。在片段中,通常使用以下符号约定:
sym | 要引用的符号 |
标签 | 表示代码地址的符号 |
func | 表示函数的符号 |
tmp | 临时寄存器(还有 tmp1、tmp2 等) |
reg, reg1, reg2 | 任意寄存器 |
dest | 产生的值或地址的目标寄存器 |
引入了汇编器内置的几个运算符。它们用于为各种寻址结构生成适当的重定位,并且通常不言而喻。
本节介绍标准 C 数据类型在存储器和寄存器中的表示。可支持其他语言;这些语言使用的类型将定义其自身至这些表示的映射。
在本节的描述和图表中,位 0 始终指最低有效位。
整数值使用二进制补码表示法。浮点值使用 IEEE 754.1 表示法表示。浮点运算在硬件支持的程度上遵循 IEEE 754.1。
表 2-1 提供 C 数据类型的大小和对齐方式(以位为单位)。
类型 | 通用名称 | 大小 | 对齐 |
---|---|---|---|
signed char | schar | 16 | 16 |
unsigned char | uchar | 16 | 16 |
char | 普通字符 | 16 | 16 |
bool (C99) | uchar | 16 | 16 |
_Bool (C99) | uchar | 16 | 16 |
bool (C++) | uchar | 16 | 16 |
short、signed short | int16 | 16 | 16 |
unsigned short | uint16 | 16 | 16 |
int、signed int | int16 | 16 | 16 |
unsigned int | uint16 | 16 | 16 |
long ,signed long | int32 | 32 | 32 |
unsigned long | uint32 | 32 | 32 |
long long、signed long long | int64 | 64 | 32 |
unsigned long long | uint64 | 64 | 32 |
enum | -- | 不尽相同(请参阅节 2.9) | 32 |
float | float32 | 32 | 32 |
double | float64 | 64 | 32 |
long double | float64 | 64 | 32 |
指针 | -- | 32 | 16 |
此规范中使用的表中的通用名称以与语言无关的方式标识类型。
默认情况下,char 类型是无符号型。这与“signed char”和“unsigned char”类型不同,后者指定了它们的符号行为。
整数类型具有互补无符号变体。通用名称以“u”为前缀(例如 uint32)。
bool 类型使用值 0 表示 false,1 表示 true。其他值未定义。
C、C99 和 C++ 中的其他类型被定义为标准类型的同义词:
typedef unsigned long wchar_t;
typedef unsigned long wint_t;
typedef char * va_list;
一般来说,实现可自由使用其认为合适的寄存器。本节中指定的标准寄存器表示仅适用于传递给函数或从函数返回的值。
一些结构体对象可以驻留在寄存器中。有关更多 信息,请参阅节 2.6。
寄存器中的数值始终右对齐;也就是说,寄存器的位 0 包含该值的最低有效位。小于 16 位 的有符号整数值将符号扩展到寄存器的高位。小于 16 位 的无符号值将加零扩展。
C28x 具有不同大小的寄存器。最常用的 CPU 寄存器是 16 位或 32 位的,寄存器选择取决于数据大小。
ACC、P 和 XT 寄存器可保存 32 位数据。
以下寄存器对可保存 64 位数据或指针:ACC:P、XAR1:XAR0、XAR3:XAR2、AR5:XAR4 和 XAR7:XAR6。在这些对中,最低有效位包含在对的第二个寄存器中。
XAR0-XAR7 寄存器可保存 32 位指针。
AH、AL、T、PH、PL 和 AR0-AR7 寄存器可保存 16 位数据。
对于支持 FPU 的器件,R0-R7 寄存器可保存 32 位浮点值。
有关 C28x 寄存器的更多信息,请参阅 TMS320C28x DSP CPU 和指令集参考指南 (SPRU430)。
C28x 仅使用小端字节序模式。字节序是指多字节值的存储器布局。在小端字节序模式下,最低有效字节存储在最小地址中。字节序仅影响对象的存储器表示;无论字节序如何,寄存器中的标量值始终具有相同表示形式。字节序确实会影响结构和位字段的布局,并且会延续影响它们的寄存器表示。
需对齐标量变量,以便可使用适合其类型的本机指令来加载和存储:MOV 表示字、MOVL 表示双字。 没有用于加载或存储 64 位类型的本机指令。这些指令正确地考虑了进出存储器时的字节序。
指针具有以下数据大小。
类型 | 大小 | 存储 | 对齐 |
---|---|---|---|
函数指针 | 32 | 32 | 32 |
数据指针 | 32 | 32 | 32 |
size_t | 32 | 32 | 32 |
ptrdiff_t | 32 | 32 | 32 |
即使指针存储为 32 位,编译器也应假定全局变量和函数的地址在 22 位以内。
支持 C99 标准中定义的 _Complex 类型。内部表示法如下所示:
struct _Complex
{ float_type real;
float_type imag; };
结构体成员会被分配从 0 开始的偏移量。每个成员会被分配满足其对齐要求的最低可用偏移量。成员之间可能需要进行填充,以便满足此对齐约束。
联合体成员全部被分配 0 偏移量。
C++ 类的底层表示是一个结构体。在本文档的其他地方,术语结构体 也适用于类。
结构体或联合体的对齐要求等同于其成员中最严格的对齐要求,包括下一节中所述的位字段容器。通过在最后一个成员之后插入填充,存储器中的结构体或联合体大小将向上舍入为其对齐的倍数。如节 3.3中的规定,在栈上按值传递的结构体和联合体具有特殊的对齐规则。
通常,大小为 32 位或更小的结构体在传递到函数或从函数返回时,可能驻留在寄存器或寄存器对中。此类结构体先在 R0H-R3H 寄存器中按值传递,然后在栈上按值传递。单字段结构体按对应于底层标量类型的值进行传递和返回。请参阅节 3.5,了解按引用传递和返回的更大结构体和联合体的信息。
对于支持 FPU32 或 FPU64 的器件,大小小于 128 位的同构浮点结构将按值传递。此外,对于支持 FPU64 的器件,64 位双精度值 (R0-R3) 按值传递。
在小端模式下,寄存器中的结构体始终右对齐;也就是说,第一个字节占用寄存器(如果是寄存器对,则为偶数寄存器)的 LSB,结构体的后续字节将填充到寄存器中递增的有效字节中。C28x 仅使用小端模式。