• 前面重定位了数据段、清除了BSS段,但是并没有重定位代码段,为什么程序也可以正常运行?
      • 前面是说了,当程序使用位置无关码编写时可以放在任意地址运行
      • 所以当我们使用位置无关码编写程序时,就可以不在链接地址上运行
    • 如果程序使用了位置相关的指令码来编写时,就必须进行重定位,否则无法再对应的地址找到程序内容

      • 如果代码段没有重定位,则不能使用链接地址来调用函数
      • 汇编中

        1. ldr pc, =main ; 这样调用函数时,用到main函数的链接地址,如果代码段没有重定位,则跳转失败
      • C语言中

        1. void (*funcptr)(const char *s, unsigned int val);
        2. funcptr = put_s_hex; /* 函数指针用的是函数的链接地址 */
        3. funcptr("hello, test function ptr", 123);
    • 使用函数指针时,实际上等于函数的链接地址

    • 链接脚本中,将代码段text/只读数据段rodata/数据段data放在了一起,所以我们可以一次性重定位这三个段

      1. . = ALIGN(4);
      2. .text :
      3. {
      4. *(.text)
      5. }
      6. . = ALIGN(4);
      7. __rodata_start = .;
      8. .rodata : { *(.rodata) }
      9. . = ALIGN(4);
      10. .data : { *(.data) }
      11. . = ALIGN(4);
      12. __bss_start = .;
      13. .bss : { *(.bss) *(.COMMON) }
      14. __bss_end = .;
      • 代码段放在最前面,所以其起始地址就等于_start地址
      • 因而可以确定重定位的源、目的:
        • ldr r0, =_start //确定目的
        • adr r1, _start //确定长度
      • data段的结束位置就是bss段的起始位置,所以可以算出重定位的数据长度
        • ldr r3, =__bss_start
        • sub r2, r3, r0 //确定长度
    • 使用位置无关码需要注意什么?
      • 只使用相对跳转指令:b、bl
      • 不只用绝对跳转指令

      ldr pc, =main

      • 不访问全局变量、静态变量、字符串、数组

    **

    • 重定位完后,使用绝对跳转指令跳转到XXX函数的链接地址去 ``` bl main // bl相对跳转,程序仍在原来的区域运行

    ldr pc, =main // 绝对跳转,跳到链接地址去运行

    ldr r0, =main // 更规范的写法,支持指令集切换 blx r0

    1. - 重定位代码

    / text/rodata/data重定位 / ldr r0, =_start

    1. adr r1, _start /* 源 */
    2. ldr r3, =__bss_start
    3. sub r2, r3, r0
    4. bl memcpy /* r0: 目的, r1: 源, r2:长度 */
    5. /* clear bss */
    6. ldr r0, =__bss_start
    7. mov r1, #0
    8. ldr r2, =__bss_end
    9. sub r2, r2, r0
    10. bl memset /* r0: dest, r1: val(0), r2: len */
    11. /* 调用main函数, 要使用绝对跳转指令,否则无法跳转到链接地址去执行 */
    12. //bl main
    13. ldr pc, =main

    ```

    • image.png