• 所谓重定位就是把一块数据从这个地址移动到另外一个地址,怎么确定这块数据的源、目的、长度呢?
    • 想要重定位.data段和.rodata段,就要知道两段源(起始地址)、目的(目的地址:链接地址)、长度(结束地址)
    • 怎么确定源(加载地址)和目的地址(链接地址)?
      • image.png
      • 从链接脚本中可以获取_start、_rodata_start的链接地址
      • 编写代码时可以直接获取_start的加载地址(因为_start是在汇编文件.s中指定的),但无法直接获取_rodata_start的加载地址
        • 所有在链接脚本中定义的参数都是用的链接地址
      • _start链接地址与加载地址的偏差就等于_rodata_start链接地址与加载地址的偏差,从而可以算出_rodata_start的加载地址
    • 怎么获取_start的加载地址和链接地址

      • _start参数是在汇编文件中指定的,当使用ldr伪指令获取时得到的是链接地址,当使用adr伪指令获取时得到的是基于PC+offset的加载地址(adr取出当前标号的地址)
      • 用ADR伪指令获得当前代码(当前代码位于加载地址处)的地址:

        1. .text
        2. .global _start
        3. _start:
        4. ......
        5. adr r0, _start

        adr是伪指令,它最终要转换为真实的指令。它怎么获得_start代码的当前所处地址呢?实际上,adr r0, _start指令的本质是r0 = pc - offset,offset是在链接时就确定了

        • offset链接时确定,因此当程序还位于加载地址处时得到的_start地址就是其加载地址了
      • 用LDR伪指令确定链接地址

        1. .text
        2. .global _start
        3. _start:
        4. ......
        5. ldr r0, =_start

        ldr是伪指令,它最终要转换为真实的指令。它怎么获得_start的链接地址呢?_start的链接地址在链接时,由链接脚本确定。

    • 在链接脚本里可以定义各类符号,在代码里读取这些符号的值

    • 修改链接脚本

      1. SECTIONS {
      2. . = 0xC0200000; /* 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 */
      3. . = ALIGN(4);
      4. .text :
      5. {
      6. *(.text)
      7. }
      8. . = ALIGN(4);
      9. __rodata_start = .;
      10. .rodata : { *(.rodata) }
      11. . = ALIGN(4);
      12. .data : { *(.data) }
      13. . = ALIGN(4);
      14. __bss_start = .;
      15. .bss : { *(.bss) *(.COMMON) }
      16. __bss_end = .;
      17. }
    • 修改start.S

      1. .text
      2. .global _start
      3. _start:
      4. ldr r0, =__data_start /* r0 : 目的: 链接地址 */
      5. /* 计算data段的当前地址:
      6. * _start的链接地址 - _start的当前地址 = __rodata_start的链接地址 - rodata段的当前地址
      7. * data段的当前地址 = __rodata_start的链接地址 - (_start的链接地址 - _start的当前地址)
      8. */
      9. ldr r0, =__rodata_start
      10. ldr r2, =_start /* link addr */
      11. adr r3, _start /* load addr */
      12. sub r2, r2, r3
      13. sub r1, r0, r2 /* r1 : 源 */
      14. ldr r3, =__bss_start
      15. sub r2, r3, r0 /* r2: 长度 */
      16. bl memcpy /* r0: 目的, r1: 源, r2:长度 */