stm32wle5xx_flash.sct
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00040000 { ; load region size_region
; FLASH
ER_IROM1 0x08000000 0x00040000 { ; load address = execution address
*.o (RESET, +First) ;从所有.o文件中抽取RESET段放在最开始的地方
*(InRoot$$Sections)
.ANY (+RO) ;所有.o文件的只读数据段
}
; Non-backup SRAM1
RW_IRAM1 0x20000000 0x00008000 { ; RW data
.ANY (+RW +ZI)
}
; Backup SRAM2
RW_IRAM2 0x20008000 EMPTY 0x00008000 { ; to be modified accordingly to user project. Can be NoInit data for backup usage, RW, ZI region, etc...
}
}
分散加载文件(scatter file)是一个文本文件,它的作用是可以描述ARM链接器生成映像文件所需要的信息。
如果不使用 scatter file 文件来指定,那么 ARM 链接器会按照默认的方式来生成映像文件,但是对于某些应用场景来说,我们希望能够将一些数据放在指定的位置,这个时候,分散加载文件就发挥其作用了。
ARM Cortex-M4系列芯片的启动流程:
MCU运行到main函数之前所做的操作,简而言之,主要做了如下三个工作:
- 堆栈以及堆的初始化
- 定位中断向量表
- 调用Reset_Handler
分散加载操作是在__main()函数内部完成的,紧接着,就运行C语言运行环境初始化&C Library的初始化。
Code: 为程序代码部分
RO-Data: 表示程序定义的常量及const型数据
RW-Data:表示已经初始化的静态变量,变量有初值
ZI-Data: 表示未初始化的静态变量,变量无初值
因为分散加载的机制是将不同代码放在不同的存储空间,因此还需要了解代码的映像文件的基本概念。
ARM映像文件其实就是源文件经过编译器生成的目标文件.obj和相应的C/C++运行时库经过连接器的处理后,生成的.axf格式的映像文件,它可以直接烧录到目标设备的ROM中直接运行或加载后运行。
段的概念_重定位的引入
可读可写的全局变量保存在RAM中
可读不可写常量保存在ROM中
段的概念
- 代码段
- 只读数据段
- 可读可写的数据段
- BSS段
char g_Char = 'A'; //可读可写,不能放在ROM上,应该放在RAM里
const char g_Char2 = 'B'; //只读变量,可以放在ROM上
int g_A = 0; //初始值为0,不需要放在ROM上
int g_B; //没有初始化,初始值为0,不需要保存在ROM上
所以,程序分为这几个段:
- 代码段(RO-CODE):就是程序本身,不会被修改
- 可读可写的数据段(RW-DATA):有初始值的全局变量、静态变量,需要从ROM上复制到内存
- 只读数据段(RO-DATA):可以放在ROM上,不需要复制到内存
- BSS段或ZI段:初始值为0的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以;未初始化的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以
- 局部变量:保存在栈中,运行时生成
- 堆:一块空闲空间,使用malloc函数来管理它,malloc函数可以自己写
重定位
保存在ROM上的全局变量的值,在使用前要复制到内存,这就是数据段重定位。
要把代码移动到其它为止,这就是代码重定位。
如果不在链接地址上,就需要重定位,除了BSS段不需要重定位,使用前把BSS段对应的空间清零即可。
谁来做重定位?
程序本身:把自己复制到链接地址去
一开始,程序可能并不位于它的链接地址上,为什么它可以执行重定位的操作?
因为重定位的代码是使用位置无关码写的
位置无关码:这段代码在任何位置都可以运行,跟它所在的位置无关
怎么写出位置无关码:
- 跳转:使用相对跳转指令,不能使用绝对跳转指令
只能使用branch指令(比如bl main) - 不要访问全局变量、静态变量
- 不使用字符串
怎么做重定位和清除BSS段
核心:复制
复制的三要素:源、目的、长度
怎么知道代码段/数据段保存在哪里?(加载地址)
怎么知道代码段/数据段被复制到哪里?(链接地址)
怎么知道代码段/数据段的长度?
在Keil中使用散列文件(Scatter file)描述
在GCC中使用链接脚本(Link Script)描述
加载地址和链接地址
程序运行时,应该位于它的链接地址处,因为:
- 使用函数地址时使用的是函数的链接地址
- 访问全局变量、静态变量时,用的是变量的链接地址
一个加载域里有三个可执行域
源 | 目的 | 长度 |
---|---|---|
0x08000000 | 0x08000000 | 0x00040000 |
紧随上一个之后 | 0x20000000 | 0x00008000 |
紧随上一个之后 | 0x20008000 | 0x00008000 |