• 一个程序中含有那些段?
      • 代码段:如果它不在链接地址上,就需要重定位
      • 只读数据段:如果它不在链接地址上,就需要重定位
      • 可读可写的数据段:如果它不在链接地址上,就需要重定位
      • BSS段:不需要重定位,因为程序里根本不保存BSS段,使用前把BSS段对应的空间清零即可
    • 谁来做重定位?
      • 对于6ULL/157这些功能强大的芯片,可以让片内ROM程序/片内固件来进行重定位
      • 大部分时候,是程序本身做重定位:它把自己复制到链接地址去
      • image.png
    • 程序运行时,其前面的一小段代码需要将整个程序拷贝链接地址去;但是起始运行的一小段拷贝代码并不位于链接地址,为什么也可以正常的把自身复制到链接地址上呢?为什么也可以正常的执行重定位工作呢?
      • 关键在于这一小段代码使用的是位置无关的指令码(位置无关码)编写的
      • 什么叫位置无关? 就是说这一段代码在任何地方都可以运行,跟所处的位置没有关系
    • 怎么写出位置无关码:
      • 跳转:使用相对跳转指令,不能使用绝对跳转指令
        • 只能使用branch指令(比如bl main),不能给PC直接复制,比如ldr pc, =main
        • 直接给PC复制就相当于将函数的链接地址赋给PC,这样就不是位置无关了
      • 不要访问全局变量、静态变量
      • 不使用字符串
    • 对于F103/157/6ULL,实现一个memcpy函数来进行重定位即可
    • 数据传输三要素:源、目的、长度
    • 怎么做重定位和清除BSS段?
      • 核心是: 复制
      • 复制需要知道源、目的、长度
        • 怎么知道代码段/数据段保存在哪?(加载地址)
        • 怎么知道代码段/数据段要被复制到哪?(链接地址)
        • 怎么知道代码段/数据段的长度?
      • 怎么知道BSS段的地址范围:起始地址、长度?
      • 这一切
        • 在keil中使用散列文件(Scatter File)来描述
        • 在GCC中使用链接脚本(Link Script)来描述
    • 加载地址和链接地址的区别

    程序运行时,应该位于它的链接地址处,因为:

    • 使用函数地址时用的是”函数的链接地址”,所以代码段应该位于链接地址处
    • 去访问全局变量、静态变量时,用的是”变量的链接地址”,所以数据段应该位于链接地址处

    但是: 程序一开始时可能并没有位于它的”链接地址”:

    • 比如对于STM32F103,程序被烧录器烧写在Flash上,这个地址称为”加载地址”
    • 比如对于IMX6ULL/STM32MP157,片内ROM根据头部信息把程序读入内存,这个地址称为“加载地址”

    加载地址 != 链接地址时,就需要重定位。