散列文件的使用与分析
1、重定位的实质: 移动数据
(1)把代码段、只读数据段、数据段,移动到它的链接地址处。 也就是
- 数据保存在哪里?加载地址
- 数据要复制到哪里?链接地址
长度
(2)这3要素怎么得到? 在keil中,使用散列文件来描述。 散列?分散排列? 是的,在STM32F103这类资源紧缺的单片机芯片中:
- 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
数据段保存在Flash上,使用前被复制到内存里
(3)在STM32F10系列开发中,散列文件(Scatter File,.sct 文件)的作用非常关键,它主要用于控制程序在Flash和RAM中的布局,即链接阶段的内存分配策略。
2、散列文件示例
2.1、示例文件
(1)KeilMDK配置,勾选如下选项:

(2)散列文件 ...\Object\*.sct ,打开如下:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08000000 0x00100000 { ; load region size_regionER_IROM1 0x08000000 0x00100000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00030000 { ; RW data.ANY (+RW +ZI)}
}(3)要看懂如下代码,查看帮助文档《compiler_reference_guide.pdf》。
- compiler_reference_guide:编译器参考指南

2.2、散列文件语法
(1)参考资料:如下文档中的4.7节。
(2)散列文件的结构
一个散列文件包含一个或多个加载区域(Load region)。每个加载区域可以包含一个或多个执行区域(Execution region)。散列结构的组成部分如下图:

(3)一个散列文件由一个或多个加载区域(Load region)组成:
load_region_description ::=
load_region_name (base_address | ("+" offset)) [attribute_list] [max_size]
"{"
execution_region_description+
"}(3)加载区域中含有一个或多个执行区域(Execution region), 执行区域语法如下:
<execution_region_description> ::=<exec_region_name> (<base_address> | "+" <offset>) [<attribute_list>] [<max_size>| <length>]"{"<input_section_description>*"}"(4)执行区域中含有一个或多个输入段(Input section), Input section语法如下:
<input_section_description> ::=<module_select_pattern> [ "(" <input_section_selector> ( ","<input_section_selector> )* ")" ]
<input_section_selector> ::= "+" <input_section_attr>| <input_section_pattern>| <input_section_type>| <input_symbol_pattern>| <section_properties>3、散列文件解析
(1)总体结构
LR_IROM1:定义了一个加载区域(Load Region),表示程序代码和只读数据的存储区域。0x08000000:加载区域的起始地址。0x00100000:加载区域的大小(1MB)。ER_IROM1:定义了一个执行区域(Execution Region),表示程序代码和只读数据的执行地址。RW_IRAM1:定义了一个读写区域(Read-Write Region),表示程序的读写数据存储区域。
(2)加载区域和执行区域
- LR_IROM2:加载区域Load region
- ER_IROM1:执行区域(
Execution region) - RW_IRAM1:执行区域(
Execution region)
- ER_IROM1:执行区域(
- *.o (RESET, +First):表示所有目标文件(.o 文件)中的
RESET符号将被放置在执行区域的最开始位置。RESET通常是程序的复位向量,即程序启动时的入口点。

- *.o :所有objects文件
- *:所有objects文件和库,在一个散列文件中只能使用一个*
- .ANY:等同于*,优先级比*低;在一个散列文件的多个可执行域中可以有多个
.ANY
(3)RW_IRAM1执行区域和加载区域不在一个地方,所以需要在汇编中把这部分内容从Flash 中复制到RAM中。
- 复制内容需要确定源地址、目标地址和长度。
- 这些信息参考一级标题:4、怎么获得region的信息。
(4)AI解析

4、怎么获得region的信息
《compiler_reference_guide.pdf》文档的4.5 Accessing and Managing Symbols with armlink

4.1、可执行域的信息
(1) Image$$<region_name>$$Base:该区域的执行地址
- region_name替换为RW_IRAM1,即目标地址。
(2)Image$$<region_name>$$Length:该区域的长度
- region_name替换为RW_IRAM1,即长度。

4.2、加载域的信息
(1)Load$$<region_name>$$Base:该区域的加载地址。

4.3、汇编代码里怎么使用这些信息
示例代码如下:
IMPORT |Image$$RW_IRAM1$$Base|
IMPORT |Image$$RW_IRAM1$$Length|
IMPORT |Load$$RW_IRAM1$$Base|LDR R0, = |Image$$RW_IRAM1$$Base| ; DEST
LDR R1, = |Load$$RW_IRAM1$$Base| ; SORUCE
LDR R2, = |Image$$RW_IRAM1$$Length| ; LENGTH
4.4、C语言里怎么使用这些信息
4.4.1、方法1
声明为外部变量。 注意:使用时需要使用取址符:
extern int Image$$RW_IRAM1$$Base;
extern int Load$$RW_IRAM1$$Base;
extern int Image$$RW_IRAM1$$Length;memcpy(&Image$$RW_IRAM1$$Base, &Image$$RW_IRAM1$$Length, &Load$$RW_IRAM1$$Base);4.4.2、方法2
声明为外部数组。 注意:使用时不需要使用取址符:
extern char Image$$RW_IRAM1$$Base[];
extern char Load$$RW_IRAM1$$Base[];
extern int Image$$RW_IRAM1$$Length;memcpy(Image$$RW_IRAM1$$Base, Image$$RW_IRAM1$$Length, &Load$$RW_IRAM1$$Base);