ZHCADC4A September   2011  – March 2014

 

  1.   1
  2. 简介
    1. 1.1  ABI - C6000
    2. 1.2  范围
    3. 1.3  ABI 变体
    4. 1.4  工具链和互操作性
    5. 1.5 
    6. 1.6  目标文件的类型
    7. 1.7 
    8. 1.8  C6000 架构概述
    9. 1.9  参考文档
    10. 1.10 代码片段表示法
  3. 数据表示
    1. 2.1 基本类型
    2. 2.2 寄存器中的数据
    3. 2.3 存储器中的数据
    4. 2.4 复数类型
    5. 2.5 结构体和联合体
    6. 2.6 数组
    7. 2.7 位字段
      1. 2.7.1 易失性位字段
    8. 2.8 枚举类型
  4. 调用约定
    1. 3.1 调用和返回
      1. 3.1.1 返回地址计算
      2. 3.1.2 调用指令
      3. 3.1.3 返回指令
      4. 3.1.4 流水线约定
      5. 3.1.5 弱函数
    2. 3.2 寄存器惯例
    3. 3.3 实参传递
    4. 3.4 返回值
    5. 3.5 通过引用传递并返回的结构体和联合体
    6. 3.6 编译器辅助函数的约定
    7. 3.7 段间调用的暂存寄存器
    8. 3.8 设置 DP
  5. 数据分配和寻址
    1. 4.1 数据段和数据区段
    2. 4.2 静态数据的分配和寻址
      1. 4.2.1 静态数据的寻址方法
        1. 4.2.1.1 near DP 相对寻址
        2. 4.2.1.2 Far DP 相对寻址
        3. 4.2.1.3 绝对寻址
        4. 4.2.1.4 GOT 间接寻址
        5. 4.2.1.5 PC 相对寻址
      2. 4.2.2 静态数据的放置约定
        1. 4.2.2.1 放置的抽象约定
        2. 4.2.2.2 寻址的抽象约定
        3. 4.2.2.3 链接器要求
      3. 4.2.3 静态数据的初始化
    3. 4.3 自动变量
    4. 4.4 帧布局
      1. 4.4.1 栈对齐
      2. 4.4.2 寄存器保存顺序
        1. 4.4.2.1 大端字节序对交换
        2. 4.4.2.2 示例
      3. 4.4.3 DATA_MEM_BANK
      4. 4.4.4 C64x+ 特定的堆栈布局
        1. 4.4.4.1 _ _C6000_push_rts 布局
        2. 4.4.4.2 紧凑帧布局
    5. 4.5 堆分配对象
  6. 代码分配和寻址
    1. 5.1 计算代码标签的地址
      1. 5.1.1 代码的绝对寻址
      2. 5.1.2 PC 相对寻址
      3. 5.1.3 同一段内的 PC 相对寻址
      4. 5.1.4 短偏移 PC 相对寻址 (C64x)
      5. 5.1.5 基于 GOT 的代码寻址
    2. 5.2 分支
    3. 5.3 调用
      1. 5.3.1 直接 PC 相对调用
      2. 5.3.2 Far Call Trampoline
      3. 5.3.3 间接调用
    4. 5.4 寻址紧凑指令
  7. 动态链接的寻址模型
    1. 6.1 术语和概念
    2. 6.2 动态链接机制概述
    3. 6.3 DSO 和 DLL
    4. 6.4 抢占
    5. 6.5 PLT 条目
      1. 6.5.1 直接调用导入函数
      2. 6.5.2 通过绝对地址寻址的 PLT 条目
      3. 6.5.3 通过 GOT 寻址的 PLT 条目
    6. 6.6 全局偏移表
      1. 6.6.1 使用 Near DP 相对寻址的基于 GOT 的引用
      2. 6.6.2 使用 Far DP 相对寻址的基于 GOT 的引用
    7. 6.7 DSBT 模型
      1. 6.7.1 导出函数的进入/退出序列
      2. 6.7.2 避免在内部函数中使用 DP 负载
      3. 6.7.3 函数指针
      4. 6.7.4 中断
      5. 6.7.5 与非 DSBT 代码的兼容性
    8. 6.8 动态链接的性能影响
  8. 线程局部存储分配和寻址
    1. 7.1 关于多线程和线程局部存储
    2. 7.2 术语和概念
    3. 7.3 用户界面
    4. 7.4 ELF 目标文件表示
    5. 7.5 TLS 访问模型
      1. 7.5.1 C6x Linux TLS 模型
        1. 7.5.1.1 通用动态 TLS 访问模型
        2. 7.5.1.2 局部动态 TLS 访问模型
        3. 7.5.1.3 初始可执行文件 TLS 访问模型
          1. 7.5.1.3.1 线程指针
          2. 7.5.1.3.2 初始可执行文件 TLS 寻址
        4. 7.5.1.4 局部可执行文件 TLS 访问模型
      2. 7.5.2 静态可执行文件 TLS 模型
        1. 7.5.2.1 静态可执行文件寻址
        2. 7.5.2.2 静态可执行文件 TLS 运行时架构
        3. 7.5.2.3 静态可执行文件 TLS 分配
          1. 7.5.2.3.1 TLS 初始化映像分配
          2. 7.5.2.3.2 主线程的 TLS 分配
          3. 7.5.2.3.3 线程库的 TLS 区域分配
        4. 7.5.2.4 静态可执行文件 TLS 初始化
          1. 7.5.2.4.1 主线程的 TLS 初始化
          2. 7.5.2.4.2 线程库进行 TLS 初始化
        5. 7.5.2.5 线程指针
      3. 7.5.3 裸机动态链接 TLS 模型
        1. 7.5.3.1 用于裸机动态链接的默认 TLS 寻址
        2. 7.5.3.2 TLS 块创建
    6. 7.6 线程局部符号解析和弱引用
      1. 7.6.1 通用和局部动态 TLS 弱引用寻址
      2. 7.6.2 初始和局部可执行文件 TLS 弱引用寻址
      3. 7.6.3 静态可执行文件和裸机动态 TLS 模型弱引用
  9. 辅助函数 API
    1. 8.1 浮点行为
    2. 8.2 C 辅助函数 API
    3. 8.3 辅助函数的特殊寄存器约定
    4. 8.4 复数类型的辅助函数
    5. 8.5 C99 的浮点辅助函数
  10. 标准 C 库 API
    1. 9.1  保留符号
    2. 9.2  <assert.h> 实现
    3. 9.3  <complex.h> 实现
    4. 9.4  <ctype.h> 实现
    5. 9.5  <errno.h> 实现
    6. 9.6  <float.h> 实现
    7. 9.7  <inttypes.h> 实现
    8. 9.8  <iso646.h> 实现
    9. 9.9  <limits.h> 实现
    10. 9.10 <locale.h> 实现
    11. 9.11 <math.h> 实现
    12. 9.12 <setjmp.h> 实现
    13. 9.13 <signal.h> 实现
    14. 9.14 <stdarg.h> 实现
    15. 9.15 <stdbool.h> 实现
    16. 9.16 <stddef.h> 实现
    17. 9.17 <stdint.h> 实现
    18. 9.18 <stdio.h> 实现
    19. 9.19 <stdlib.h> 实现
    20. 9.20 <string.h> 实现
    21. 9.21 <tgmath.h> 实现
    22. 9.22 <time.h> 实现
    23. 9.23 <wchar.h> 实现
    24. 9.24 <wctype.h> 实现
  11. 10C++ ABI
    1. 10.1  限制 (GC++ABI 1.2)
    2. 10.2  导出模板 (GC++ABI 1.4.2)
    3. 10.3  数据布局(GC++ABI 第 2 章)
    4. 10.4  初始化保护变量 (GC++ABI 2.8)
    5. 10.5  构造函数返回值 (GC++ABI 3.1.5)
    6. 10.6  一次性构建 API (GC++ABI 3.3.2)
    7. 10.7  控制对象构造顺序 (GC++ ABI 3.3.4)
    8. 10.8  还原器 API (GC++ABI 3.4)
    9. 10.9  静态数据 (GC++ ABI 5.2.2)
    10. 10.10 虚拟表和键函数 (GC++ABI 5.2.3)
    11. 10.11 回溯表位置 (GC++ABI 5.3)
  12. 11异常处理
    1. 11.1  概述
    2. 11.2  PREL31 编码
    3. 11.3  异常索引表 (EXIDX)
      1. 11.3.1 指向行外 EXTAB 条目的指针
      2. 11.3.2 EXIDX_CANTUNWIND
      3. 11.3.3 内联 EXTAB 条目
    4. 11.4  异常处理指令表 (EXTAB)
      1. 11.4.1 EXTAB 通用模型
      2. 11.4.2 EXTAB 紧凑模型
      3. 11.4.3 个性化例程
    5. 11.5  回溯指令
      1. 11.5.1 通用序列
      2. 11.5.2 字节编码展开指令
      3. 11.5.3 24 位展开编码
    6. 11.6  描述符
      1. 11.6.1 类型标识符编码
      2. 11.6.2 作用域
      3. 11.6.3 Cleanup 描述符
      4. 11.6.4 catch 描述符
      5. 11.6.5 函数异常规范 (FESPEC) 描述符
    7. 11.7  特殊段
    8. 11.8  与非 C++ 代码交互
      1. 11.8.1 EXIDX 条目自动生成
      2. 11.8.2 手工编码的汇编函数
    9. 11.9  与系统功能交互
      1. 11.9.1 共享库
      2. 11.9.2 覆盖块
      3. 11.9.3 中断
    10. 11.10 TI 工具链中的汇编语言运算符
  13. 12DWARF
    1. 12.1 DWARF 寄存器名称
    2. 12.2 调用帧信息
    3. 12.3 供应商名称
    4. 12.4 供应商扩展
  14. 13ELF 目标文件(处理器补充)
    1. 13.1 注册供应商名称
    2. 13.2 ELF 标头
    3. 13.3
      1. 13.3.1 段索引
      2. 13.3.2 段类型
      3. 13.3.3 扩展段标头属性
      4. 13.3.4 子段
      5. 13.3.5 特殊段
      6. 13.3.6 段对齐
    4. 13.4 符号表
      1. 13.4.1 符号类型
      2. 13.4.2 通用块符号
      3. 13.4.3 符号名称
      4. 13.4.4 保留符号名称
      5. 13.4.5 映射符号
    5. 13.5 重定位
      1. 13.5.1 重定位类型
      2. 13.5.2 重定位操作
      3. 13.5.3 未解析的弱引用的重定位
  15. 14ELF 程序加载和动态链接(处理器补充)
    1. 14.1 程序标头
      1. 14.1.1 基址
      2. 14.1.2 段内容
      3. 14.1.3 绑定段和只读段
      4. 14.1.4 线程局部存储
    2. 14.2 程序加载
    3. 14.3 动态链接
      1. 14.3.1 程序解释器
      2. 14.3.2 动态段
      3. 14.3.3 共享对象依赖关系
      4. 14.3.4 全局偏移量表
      5. 14.3.5 过程链接表
      6. 14.3.6 抢占式
      7. 14.3.7 初始化和终止
    4. 14.4 裸机动态链接模型
      1. 14.4.1 文件类型
      2. 14.4.2 ELF 标识
      3. 14.4.3 可见性和绑定
      4. 14.4.4 数据寻址
      5. 14.4.5 代码寻址
      6. 14.4.6 动态信息
  16. 15Linux ABI
    1. 15.1  文件类型
    2. 15.2  ELF 标识
    3. 15.3  程序标头和段
    4. 15.4  数据寻址
      1. 15.4.1 数据区段基表 (DSBT)
      2. 15.4.2 全局偏移量表 (GOT)
    5. 15.5  代码寻址
    6. 15.6  延迟绑定
    7. 15.7  可见性
    8. 15.8  抢占式
    9. 15.9  “作为自有导入”占先
    10. 15.10 程序加载
    11. 15.11 动态信息
    12. 15.12 初始化和终止函数
    13. 15.13 Linux 模型摘要
  17. 16符号版本控制
    1. 16.1 ELF 符号版本控制概述
    2. 16.2 版本段标识
  18. 17构建属性
    1. 17.1 C6000 ABI 构建属性子段
    2. 17.2 C6000 构建属性标签
  19. 18复制表和变量初始化
    1. 18.1 复制表格式
    2. 18.2 压缩的数据格式
      1. 18.2.1 RLE
      2. 18.2.2 LZSS 格式
    3. 18.3 变量初始化
  20. 19扩展程序标头属性
    1. 19.1 编码
    2. 19.2 属性标签定义
    3. 19.3 扩展程序标头属性段格式
  21. 20修订历史记录

通用动态 TLS 访问模型

这是最通用的 TLS 访问模型。使用该访问模型的对象可用于构建任何 Linux 模块:可执行文件、初始加载模块和 dlopened 模块。在静态链接期间,为该模型生成的代码不能假设模块 id 或偏移是已知的。

采用这种访问模型,可在运行时加载动态模块。为实现这种可能性,线程库的线程管理架构必须提供一种方法,以便在运行时加载和卸载动态模块时,添加和移除 TLS 块。

编译器生成对 __tls_get_addr() 的调用来获取线程局部变量的地址。模块 TLS 块中的模块 id 和线程局部变量的偏移量都是作为形参来传递的。该代码从全局偏移量表 (GOT) 条目中获得模块 id 和偏移量,以确保位置独立性 (PIC) 和符号占先。

__tls_get_addr() 函数传递模块 id 和偏移量的最简单方法如下所示:

    void * __tls_get_addr(unsigned int module_id, ptrdiff_t offset); 

请注意,两者都是 32 位实参,GOT 条目也是 32 位条目。作为优化方案,如果 ISA 能够支持,我们可将这两个 GOT 条目加载为 64 位双字。这两个 GOT 条目必须连续分配并与 64 位边界对齐。可将该 GOT 实体视为如下结构:

    struct TLS_descriptor 
    { 
        unsigned int module_id; 
        ptrditt_t offset; 
    } __attribute__ ((aligned (8)));

那么,__tls_get_addr() 接口变为:

    void * __tls_get_addr(struct TLS_descriptor); 

在该 EABI 中,大小为 64 位或更小的结构由值传递,从而在 A5:A4 寄存器对中传递 TLS 描述符。在小端字节序模式下,模块 id 在 A4 中传递,偏移量在 A5 中传递。在大端字节序模式下,按照 C6x EABI 调用约定交换寄存器。本节中的示例使用小端字节序模式。

使用该接口,线程局部访问变为以下内容(适用于 C64 及更高版本):

        LDDW   *+DP($GOT_TLS(X)), A5:A4  ;reloc R_C6000_SBR_GOT_U15_D_TLS
     || CALLP  __tls_get_addr,B3          ; A4 has the address of X at return
        LDW    *A4, A4                   ; A4 has the value of X

重定位 R_C6000_SBR_GOT_U15_D_TLS 使得链接器为 x 的模块 id 和偏移量创建 GOT 条目,如下所示:

64-bit aligned address:
        GOT[n]            ;reloc R_C6000_TLSMOD  (symbol X)
        GOT[n+1]          ;reloc R_C6000_TBR_U32 (symbol X)

然后,链接器使用 GOT 实体的 DP 相对偏移量来解析 R_C6000_SBR_GOT_U15_D_TLS 重定位。动态加载器将 R_C6000_TLSMOD 解析为在其中定义 x 的模块的模块 id。它将 R_C6000_TBR_U32 解析为模块 TLS 块中 x 的偏移量。

C6x ISA 当前无直接加载 64 位 TLS 描述符的指令。但是,我们使用 64 位描述符来定义 __tls_get_addr() 接口,以期未来 ISA 具有此类支持功能。

    void * __tls_get_addr(struct TLS_descriptor);

链接器须连续分配线程局部变量的模块 id 和偏移量的 GOT 条目,并在发现 R_C6000_SBR_GOT_U15_D_TLS 重定位时将第一个条目与 64 位边界对齐。

由于不支持 DP 相对 64 位加载,因此可在当前 ISA 上使用以下序列:

        LDW    *+DP($GOT_TLSMOD(X)), A5   ;reloc R_C6000_SBR_GOT_U15_W_TLSMOD
        LDW    *+DP($GOT_TBR(X)), A4      ;reloc R_C6000_SBR_GOT_U15_W_TBR
     || CALLP  __tls_get_addr,B3          ; A4 has the address of X at return
        LDW    *A4, A4                    ; A4 has the value of X

重定位 R_C6000_SBR_GOT_U15_W_TLSMOD 和 R_C6000_SBR_GOT_U15_W_TBR 使得链接器为 x 的模块 id 和偏移量分别创建 GOT 条目。该访问模式不要求这些 GOT 条目是连续且 64 位对齐的。如果链接器也未发现同一符号的 DW_TLS 重定位,则可自由分别定义模块 id 和偏移量 GOT 条目,无需 64 位对齐。但是,如果除同一符号的 TLSMOD/TBR 重定位之外还发现了 DW_TLS,则必须定义 64 位对齐的连续 GOT 条目,并将其重新用于 TLSMOD/TBR 重定位。

如果必须使用 far DP 寻址来寻址 GOT,则通用动态寻址变为:

        MVKL $DPR_GOT_TLSMOD(X),  A5      ;reloc R_C6000_SBR_GOT_L16_W_TLSMOD
        MVKH $DPR_GOT_TLSMOD(X),  A5      ;reloc R_C6000_SBR_GOT_H16_W_TLSMOD
        ADD  DP, A5, A5
        LDW  *A5, A5
        MVKL $DPR_GOT_TPR(X),  A4         ;reloc R_C6000_SBR_GOT_L16_W_TBR
        MVKH $DPR_GOT_TPR(X),  A4         ;reloc R_C6000_SBR_GOT_H16_W_TBR
        ADD  DP, A4, A4
        LDW  *A4, A4
     || CALLP  __tls_get_addr,B3          ; A4 has the address of X at return
        LDW    *A4, A4                    ; A4 has the value of X

__tls_get_addr() 可计算线程局部地址,如下所示:

    void * __tls_get_addr(struct TLS_descriptor desc)
    {
        void *TP  = __c6xabi_get_tp();
        int  *dtv = (int*)(((int*) TP)[0]);
        char *tls = (char *)dtv[desc.module_id];
        return tls + desc.offset;
    }