ZHCADC5A June   2013  – June 2020

 

  1.   1
  2. 简介
    1. 1.1  ABI - MSP430
    2. 1.2  范围
    3. 1.3  ABI 变体
    4. 1.4  工具链和互操作性
    5. 1.5 
    6. 1.6  目标文件的类型
    7. 1.7 
    8. 1.8  MSP430 架构概述
    9. 1.9  MSP430 存储器模型
    10. 1.10 参考文档
    11. 1.11 代码片段表示法
  3. 数据表示
    1. 2.1 基本类型
    2. 2.2 寄存器中的数据
    3. 2.3 存储器中的数据
    4. 2.4 指针类型
    5. 2.5 复数类型
    6. 2.6 结构体和联合体
    7. 2.7 数组
    8. 2.8 位字段
      1. 2.8.1 易失性位字段
    9. 2.9 枚举类型
  4. 调用约定
    1. 3.1 调用和返回
      1. 3.1.1 调用指令
        1. 3.1.1.1 间接调用
        2. 3.1.1.2 直接调用
      2. 3.1.2 返回指令
      3. 3.1.3 流水线约定
      4. 3.1.4 弱函数
    2. 3.2 寄存器惯例
      1. 3.2.1 实参寄存器
      2. 3.2.2 被调用者保存的寄存器
    3. 3.3 实参传递
      1. 3.3.1 单个寄存器
      2. 3.3.2 寄存器对
      3. 3.3.3 拆分对
      4. 3.3.4 四倍字(四寄存器实参)
      5. 3.3.5 编译器辅助函数的特殊约定
      6. 3.3.6 C++ 实参传递
      7. 3.3.7 传递结构体和联合体
      8. 3.3.8 未在寄存器中传递的实参的栈布局
      9. 3.3.9 帧指针
    4. 3.4 返回值
    5. 3.5 通过引用传递并返回的结构体和联合体
    6. 3.6 编译器辅助函数的约定
    7. 3.7 已见函数的暂存寄存器
    8. 3.8 _ _mspabi_func_epilog 辅助函数
    9. 3.9 中断函数
  5. 数据分配和寻址
    1. 4.1 数据段和数据区段
    2. 4.2 寻址模式
    3. 4.3 静态数据的分配和寻址
      1. 4.3.1 静态数据的寻址方法
        1. 4.3.1.1 绝对寻址
        2. 4.3.1.2 符号寻址
        3. 4.3.1.3 立即寻址
      2. 4.3.2 静态数据的放置约定
        1. 4.3.2.1 放置的抽象约定
        2. 4.3.2.2 寻址的抽象约定
      3. 4.3.3 静态数据的初始化
    4. 4.4 自动变量
    5. 4.5 帧布局
      1. 4.5.1 栈对齐
      2. 4.5.2 寄存器保存顺序
    6. 4.6 堆分配对象
  6. 代码分配和寻址
    1. 5.1 计算代码标签的地址
      1. 5.1.1 代码的绝对寻址
      2. 5.1.2 符号寻址
      3. 5.1.3 立即寻址
    2. 5.2 分支
    3. 5.3 调用
      1. 5.3.1 直接 调用
      2. 5.3.2 Far Call Trampoline
      3. 5.3.3 间接调用
  7. 辅助函数 API
    1. 6.1 浮点行为
    2. 6.2 C 辅助函数 API
    3. 6.3 辅助函数的特殊寄存器约定
    4. 6.4 C99 的浮点辅助函数
  8. 标准 C 库 API
    1. 7.1  保留符号
    2. 7.2  <assert.h> 实现
    3. 7.3  <complex.h> 实现
    4. 7.4  <ctype.h> 实现
    5. 7.5  <errno.h> 实现
    6. 7.6  <float.h> 实现
    7. 7.7  <inttypes.h> 实现
    8. 7.8  <iso646.h> 实现
    9. 7.9  <limits.h> 实现
    10. 7.10 <locale.h> 实现
    11. 7.11 <math.h> 实现
    12. 7.12 <setjmp.h> 实现
    13. 7.13 <signal.h> 实现
    14. 7.14 <stdarg.h> 实现
    15. 7.15 <stdbool.h> 实现
    16. 7.16 <stddef.h> 实现
    17. 7.17 <stdint.h> 实现
    18. 7.18 <stdio.h> 实现
    19. 7.19 <stdlib.h> 实现
    20. 7.20 <string.h> 实现
    21. 7.21 <tgmath.h> 实现
    22. 7.22 <time.h> 实现
    23. 7.23 <wchar.h> 实现
    24. 7.24 <wctype.h> 实现
  9. C++ ABI
    1. 8.1  限制 (GC++ABI 1.2)
    2. 8.2  导出模板 (GC++ABI 1.4.2)
    3. 8.3  数据布局(GC++ABI 第 2 章)
    4. 8.4  初始化保护变量 (GC++ABI 2.8)
    5. 8.5  构造函数返回值 (GC++ABI 3.1.5)
    6. 8.6  一次性构建 API (GC++ABI 3.3.2)
    7. 8.7  控制对象构造顺序 (GC++ ABI 3.3.4)
    8. 8.8  还原器 API (GC++ABI 3.4)
    9. 8.9  静态数据 (GC++ ABI 5.2.2)
    10. 8.10 虚拟表和键函数 (GC++ABI 5.2.3)
    11. 8.11 回溯表位置 (GC++ABI 5.3)
  10. 异常处理
    1. 9.1  概述
    2. 9.2  PREL31 编码
    3. 9.3  异常索引表 (EXIDX)
      1. 9.3.1 指向行外 EXTAB 条目的指针
      2. 9.3.2 EXIDX_CANTUNWIND
      3. 9.3.3 内联 EXTAB 条目
    4. 9.4  异常处理指令表 (EXTAB)
      1. 9.4.1 EXTAB 通用模型
      2. 9.4.2 EXTAB 紧凑模型
      3. 9.4.3 个性化例程
    5. 9.5  回溯指令
      1. 9.5.1 通用序列
      2. 9.5.2 字节编码展开指令
    6. 9.6  描述符
      1. 9.6.1 类型标识符编码
      2. 9.6.2 作用域
      3. 9.6.3 Cleanup 描述符
      4. 9.6.4 catch 描述符
      5. 9.6.5 函数异常规范 (FESPEC) 描述符
    7. 9.7  特殊段
    8. 9.8  与非 C++ 代码交互
      1. 9.8.1 EXIDX 条目自动生成
      2. 9.8.2 手工编码的汇编函数
    9. 9.9  与系统功能交互
      1. 9.9.1 共享库
      2. 9.9.2 覆盖块
      3. 9.9.3 中断
    10. 9.10 TI 工具链中的汇编语言运算符
  11. 10DWARF
    1. 10.1 DWARF 寄存器名称
    2. 10.2 调用帧信息
    3. 10.3 供应商名称
    4. 10.4 供应商扩展
  12. 11ELF 目标文件(处理器补充)
    1. 11.1 注册供应商名称
    2. 11.2 ELF 标头
    3. 11.3
      1. 11.3.1 段索引
      2. 11.3.2 段类型
      3. 11.3.3 扩展段标头属性
      4. 11.3.4 子段
      5. 11.3.5 特殊段
      6. 11.3.6 段对齐
    4. 11.4 符号表
      1. 11.4.1 符号类型
      2. 11.4.2 通用块符号
      3. 11.4.3 符号名称
      4. 11.4.4 保留符号名称
      5. 11.4.5 映射符号
    5. 11.5 重定位
      1. 11.5.1 重定位类型
        1. 11.5.1.1 绝对重定位
        2. 11.5.1.2 PC 相对重定位
        3. 11.5.1.3 数据段中的重定位
        4. 11.5.1.4 MSP430 指令的重定位
        5. 11.5.1.5 MSP430X 指令的重定位
        6. 11.5.1.6 其他重定位类型
      2. 11.5.2 重定位操作
      3. 11.5.3 未解析的弱引用的重定位
  13. 12ELF 程序加载和链接(处理器补充)
    1. 12.1 程序标头
      1. 12.1.1 基址
      2. 12.1.2 段内容
      3. 12.1.3 线程局部存储
    2. 12.2 程序加载
  14. 13构建属性
    1. 13.1 MSP430 ABI 构建属性子段
    2. 13.2 MSP430 构建属性标签
  15. 14复制表和变量初始化
    1. 14.1 复制表格式
    2. 14.2 压缩的数据格式
      1. 14.2.1 RLE
      2. 14.2.2 LZSS 格式
    3. 14.3 变量初始化
  16. 15修订历史记录

变量初始化

节 4.1中所述,初始化后的读写变量会被收集到目标文件的专门段中,例如 .data。该段包含程序启动时该段初始状态的映像。

TI 工具链支持两种加载此类段的模型。在所谓的 RAM 模型 中,一些未指定的外部代理(如加载器)负责将数据从可执行文件获取到它在读写存储器中的位置。这是基于 OS 的系统(在某些情况下为引导加载系统)中使用的典型直接初始化模型。

另一种模型称为 ROM 模型,适用于裸机嵌入式系统,它们必须能够在没有操作系统或其他加载器支持的情况下冷启动。初始化程序所需的任何数据必须驻留在持久性离线存储 (ROM) 中,并在启动时复制到其 RAM 位置。TI 工具链通过利用Chapter14中所述的复制表功能实现此目的。初始化机制与复制表在概念上相似,但细节稍有不同。

图 14-3 展示了 ROM 模型变量初始化的概念工作原理。在此模型中,链接器将数据从包含初始化变量的段中移除。这些段变为未初始化段,分配到它们在 RAM 中的运行时地址(比如 .bss)。链接器将初始化数据编码为一个特殊段,被称为 .cinit(代表 C 初始化),来自运行时库的启动代码在这里解码并复制到其运行地址。

GUID-0BA59A60-13BA-4334-B0AB-58EF34C385F0-low.gif图 14-3 通过 cinit 进行基于 ROM 的变量初始化

.cinit 表中的源数据可以压缩,也可以不压缩,与复制表类似。如果数据经过压缩,编码和解码方案与复制表相同,可以共享处理程序表和解压缩处理程序。

.cinit 段包含以下项目中的部分或全部:

  • cinit 表包含 cinit 记录,与复制记录类似。
  • 处理程序表包含指向解压缩例程的指针(如节 14.1中所述)。处理程序表和处理程序可通过初始化和复制表分享。
  • 源数据包含压缩或未压缩的数据,用于初始化变量。

这些项目顺序不限。

图 14-4 是展示 .cinit 段的原理图。

GUID-4D35E64F-7F44-416F-83A9-191292DD559F-low.gif图 14-4 .cinit 段

.cinit 段的段类型为 SHT_TI_INITINFO,将其标识为此格式。工具应识别段类型,而不是名称 .cinit。

定义了两种特殊符号,来分隔 cinit 表:__TI_CINIT_Base 指向 cinit 表,而 __TI_CINIT_Limit 指向表末尾往后一个字节。启动代码使用这些符号来引用该表。

CINT_RECORD 结构体的格式取决于所使用的代码和数据模型。

对于小数据模型和小型代码模型:

        typedef struct  {
           void  * source_data; /* 16-bit pointer */
           void  * dest;        /* 16-bit pointer */
        } CINIT_RECORD;

对于小数据模型和大型代码模型:

        typedef struct  {
           uint32  source_data; /* 32-bit storage for data or code pointer */
           uint32  dest;        /* 32-bit storage for data or code pointer */
        } CINIT_RECORD;

对于大(或受限)数据模型和大型代码模型:

        typedef struct  {
           void  * source_data; /* 20-bit pointer */
           void  * dest;        /* 20-bit pointer */
        } CINIT_RECORD;
  • source_data 字段指向 cinit 段中的源数据。
  • dest 字段指向目标地址。与复制表记录不同,cinit 记录不包含 size 字段;大小会始终编码到源数据中。

源数据与复制表压缩源数据格式相同(参阅节 14.1),并且处理程序的接口相同。除了 RLE 和 LZSS 格式,cinit 记录还定义了另外两种格式:未压缩和零初始化。

  • 显式未压缩格式是必需的,因为与复制表记录不同,cinit 记录没有过载的大小字段。大小字段会始终编码到源数据中,即使未使用压缩也是如此。编码如下:
    GUID-3A235658-F83B-4E34-926C-857CB5E676B0-low.png

    编码数据包含一个大小字段,它在处理程序索引之后下一个 2 字节边界上对齐。“大小”字段的大小取决于存储器模型。对于小代码和小数据模型,大小为 2 个字节。在所有其他存储器模式组合中,大小为 4 个字节。大小字段指定数据有效载荷中有多少字节,它紧跟在大小字段之后开始算起。初始化操作会将大小字节从数据字段复制到目标地址。TI 运行时库包含一个处理程序,被称为 _ _TI_decompress_none,用于未压缩格式。

  • 零初始化格式是一种紧凑格式,用于变量初始化值为零的常见情况。编码如下:
    GUID-1CD24896-7391-42EA-9415-3F03050D6680-low.png

    大小字段在处理程序索引之后下一个 2 字节边界上对齐。“大小”字段的大小取决于存储器模型。对于小代码和小数据模型,大小为 2 个字节。在所有其他存储器模式组合中,大小为 4 个字节。初始化操作会在目标地址将大小连续字节填充为零。TI 运行时库包含一个处理程序,被称为 _ _TI_zero_init,用于此格式。

    如果可以使用相同格式便利地进行编码,链接器就可以自由地将相邻对象的初始化合并到单一 cinit 记录中,作为一项优化。对于零初始化对象而言,这通常很重要。