• 在汇编中,我们很容易使用链接脚本lds内的值;因为对于汇编来说,这些都是符号标志而已,直接使用ldr rx, =xxx获取即可
  • 那么在C语言中,我们如何得到链接脚本里的值呢?

    • 可以在C语言中将链接脚本的值声明为外部变量(声明类型随意,可以声明为unsigned int/char甚至是指针变量),使用时需要使用取址符

      1. extern unsigned int __bss_start;
      2. extern unsigned int __bss_end;
      3. unsigned int len;
      4. len = (unsigned int)&__bss_end - (unsigned int)&__bss_start;
      5. memset(&__bss_start, 0, len);
    • 还可以将链接脚本的值声明为外部数组,使用时无需取址符,直接使用

      1. extern char __bss_start[];
      2. extern char __bss_end[];
      3. unsigned int len;
      4. len = __bss_end - __bss_start;
      5. memset(__bss_start, 0, len);
    • 总结: 声明为一般变量,使用需要加取址符;声明为数组,使用无需加取址符

  • 纯C代码的重定位函数 ```c

    if 0

    / 声明为外部变量 / / 源地址(加载地址)在汇编中通过传参方式得到: adr r0, _start / void SystemInit(void loadaddr) { extern char __text_start; / C中没法像汇编一样使用_start的链接地址,所以直接在链接脚本定义一个链接的起始地址 */ extern char bss_start; extern char bss_end;

    unsigned int len;

    / text/rodata/data重定位 / len = &bss_start - &text_start; / 如果bss_start和text_start声明为int型, 则此处相减时需要强制类型转换成int来相减 /

    1. /* 主要是因为char指针相减单位是1byte,int指针相减单位是4byte */

    memcpy(&__text_start, loadaddr, len); / 目的, 源, 长度 /

    / clear bss / len = &bss_end - &bss_start; memset(&__bss_start, 0, len); / dest, val(0), len / }

else

/ 声明为外部数组 / / 源地址(加载地址)在汇编中通过传参方式得到: adr r0, _start / void SystemInit(void *loadaddr) { extern char text_start[]; extern char bss_start[]; extern char __bss_end[];

  1. unsigned int len;
  2. /* text/rodata/data重定位 */
  3. len = __bss_start - __text_start;
  4. memcpy(__text_start, loadaddr, len); /* 目的, 源, 长度 */
  5. /* clear bss */
  6. len = __bss_end - __bss_start;
  7. memset(__bss_start, 0, len); /* dest, val(0), len */

}

endif

  1. - 在汇编中调用重定位代码,并在重定位后跳转到链接地址上执行程序
  2. ```c
  3. .text
  4. .global _start
  5. _start:
  6. /* 设置sp */
  7. ldr sp, =(0x80000000+0x100000)
  8. adr r0, _start
  9. bl SystemInit
  10. /* 调用main函数 */
  11. //bl main
  12. ldr pc, =main
  • 对于把链接脚本的值声明为外部变量/数组来使用的理解

    • 在C语言中,定义一个全局变量,编译时会在符号表中出现;符号表中会出现该全局变量的地址 | Name | Address | | —- | —- | | g_a | xxxxxxxx |

    • 对于链接脚本的符号,被声明为外部变量,则该变量在符号表中也会有一项,其地址就是其在链接脚本中表示的地址 | Name | Address | | —- | —- | | g_a | xxxxxxxx | | __bss_start | yyyyyyyy |

    • 与全局变量相比,如何得到(链接脚本符号的)外部变量在符号表中对应的地址值呢?

      • 全局变量可以用取址符&得到地址,当然我们外部变量也可以用取址符&得到地址
      • 对于外部数组,当然也可以参照正常数组来,数组名=地址
        • 对于int g_a变量
          • 使用&g_a得到符号表里的地址。
  • 对于extern unsigned int __bss_start变量

    • 要得到符号表中的地址,也是使用&__bss_start
  • 对于extern char __bss_start[]变量

    • 要得到符号表中的地址,直接使用__bss_start[],不需要加&
    • 为什么?`__bss_start本身就表示地址啊