• 重定位的实质就是:移动数据(数据拷贝)
      • 把代码段、只读数据段、数据段,移动到它的链接地址处。也就是复制
    • 拷贝数据怎么知道源、目的、长度?
      • 在GCC中,使用链接脚本来描述。
      • 在keil中,跟链接脚本对应的是散列文件,散列的意思就是”分散排列“(代码段和数据段分开存储)
    • 在STM32F103这类资源紧缺的单片机芯片中:
      • 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
      • 数据段保存在Flash上,使用前被复制到内存里
      • image.png
    • 在资源丰富的MPU板子上:
      • 内存很大,几十M、几百M,甚至几G
      • 可能没有XIP设备(XIP: eXecute In Place,原地执行)
        • 没有类似STM32F103上的Flash,代码无法在存储芯片上直接运行
      • 基于这些特点,在MPU板子上
        • 代码段、数据段、BSS段等等,运行时没有必要分开存放
        • 重定位时,把整个程序(包括代码段、数据段等),一起复制到它的链接地址去
    • 链接脚本示例

      1. /* 一个链接脚本可以由多个SECTION来组成, 示例中有4个section: .text .rodata .data .bss */
      2. SECTIONS {
      3. /* . : 表示当前地址; 指定当前地址为0xc0200000 */
      4. /* 程序运行时应该位于这个链接地址上 */
      5. . = 0xC0200000; /* 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 */
      6. . = ALIGN(4); // 当前地址向4取整
      7. .text : // .text : 段名; 存放所有文件的代码段
      8. {
      9. *(.text) // * : 表示所有文件; 放所有文件的代码段(从所有的.o文件中抽出代码段放在一起)
      10. }
      11. . = ALIGN(4);
      12. .rodata : { *(.rodata) }
      13. . = ALIGN(4);
      14. .data : { *(.data) }
      15. . = ALIGN(4);
      16. // 链接脚本中想得到更多的信息(地址信息), 可以添加一些符号使其等于当前地址
      17. // . 表示当前地址, 当前地址可读可写
      18. __bss_start = .;
      19. .bss : { *(.bss) *(.COMMON) } // .bss段将所有文件的.bss段和.COMMON段存放在一起
      20. __bss_end = .;
      21. }
    • 链接脚本语法

      • 一个链接脚本由一个SECTIONS组成。一个SECTIONS里面,含有一个或多个section

        1. SECTIONS {
        2. ...
        3. secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
        4. { contents } >region :phdr =fill
        5. ...
        6. }
      • section是链接脚本的核心

      • secname:段名
      • start:段的起始地址,不写时为当前地址
      • BLOCK(align): 指定如何对齐
      • AT ( ldadr ): 加载地址,一般不需要写,不写时加载地址就等于该段的起始地址(链接地址)
      • contents: 段的内容
      • (NOLOAD) 和 >region :phdr =fill暂时用不到,可以先不管