(和《ARM嵌入式应用技术基础》186-190页一模一样)
Scatter文件编写
一个映像文件中可以包含多个域(region),在加载和运行映像文件时,每个域可以有不同的地址。每个域可以包括多达3个输出段,每个输出段是由若干个具有相同属性的输入段组成。这样在生成映像文件时,ARM链接器就需要知道下述两个信息。
- 分组信息 决定各域中的输出段是由哪些输入段组织而成;
- 定位信息 决定各域在存储空间中的起始地址。
根据映像文件中地址映射的复杂程度,有两种方法来告诉ARM链接器这些相关的信息。对于映像文件中地址映射关系比较简单的情况,可以使用命令行选项;对于映像文件中地址映射关系比较复杂的情况,可以使用一个scatter配置文件。Scatter文件又称为分散加载文件,将重点讲解如何编写scatter文件。
1、Scatter文件结构
Scatter文件是一个文本文件,使用BNF语法来描述ARM链接器生成映像文件时所需要的信息。具体来说,在scatter文件中可以指定下列信息:
- 各个加载时域的加载时起始地址、最大尺寸和属性;
- 每个加载时域包含的输出段;
- 各个输出段的运行时起始地址、最大尺寸、存储访问特性和属性;
- 各个输出段中包含的输入段。
一个Scatter文件包含若干个加载域,一个加载域包含若干个输出段,一个输出段由若干个具有相同属性的输入段组成,其结构如图1所示
图1 Scatter文件结构示意图
① 加载时域的描述
加载时域包括名称、起始地址、属性、最大尺寸和一个运行时域的列表。使用BNF语法描述,加载时域的格式如下所示:
Load_name base_designator attribute max_size { …… } |
- Load_name 运行时域名称,它除了唯一地标识一个运行时域外,还用来构成链接器生成的链接符号;
- base_designator 用来表示本加载时域的起始地址,它可以有两种格式表示:起始地址或偏移量;
- attribute 本加载时域的属性,其可能的取值为下面之一,默认的取值为ABSOLUTE:
- PI 位置无关属性;
- RELOC 重定位;
- ABSOLUTE 绝对地址;
- max_size 最大尺寸,如果本加载时域的实际尺寸超过了该值,链接器将报告错误。默认的取值为0xFFFFFFFF。
② 输出段的描述
输出段包括名称、起始地址、属性、最大尺寸和一个输入段的集合。使用BNF语法描述,输出段的格式如下所示:
output_name base_designator attribute max_size { …… } |
- output_name 输出段的名称,它用来唯一地标识一个输出段,还用来构成链接器生成的链接符号。
- base_designator 用来表示本输出段的起始地址,它可以有两种格式:起始地址值或偏移量。
- attribute 表示本输出段的属性,其可能的取值如下所示:
- PI 位置无关属性
- RELOC 重定位
- ABSOLUTE 绝对地址
- FIXED 固定地址
- UNINIT 未初始化的数据
- max_size 指定本输出段的最大尺寸。
③ 输入段的描述
输入段里描述了一个文本字符串的模式,匹配该模式的输入段都将被包含在当前域中。模式中可以使用匹配符,符号"*"代表零个或者多个字符,符号"?"代表单个字符。进行匹配时,所有字符是大小写无关的。
下面介绍一些使用scatter文件配置映像文件地址映射模式的例子。在本例中,映像文件包括一个加载时域和3个连续的输出段,这种模式适合于那些将其他程序加载到RAM中的程序,如操作系统的引导程序和Angel等。
例子 一个简单的scatter文件
Load_1 0x4000 ;定义加载时域的名称为Load_1,起始地址为0x4000 { ER_RO + 0 ;输出段名ER_RO,地址偏移量0,所以起始地址为0x4000 { *( + RO) } ;通配符*,包含了所有的RO属性的输入段,它们被连续放置 ER_RW + 0 ;输出段名称ER_RW,起始地址为前一个输出段的结束地址加偏移量0 { *( + RW) } ;本输出段包含所有的RW属性的输入段,它们被连续放置 ER_ZI 0x5000 ;输出段名称ER_ZI,起始地址为0x5000 { *( + ZI) } ;本输出段包含了所有的ZI属性的输入段,它们被连续放置 } |
按照例 scatter文件的描述,ARM链接器会生成相应的映像文件地址映射关系,如图2所示。
图2 程序运行时地址映射关系
2、固定时域
任何一个映像文件都需要指定一个初始入口点(initial entry point),它是影响文件运行时的入口点。初始入口点必须位于一个固定域中,所谓固定域是指该域的加载时地址和运行时地址是相同的。如果初始入口点不是位于一个固定域中,ARM链接器在链接时会产生下面的错误信息。
L6203E:Entry point (0x0000 0000) lies within non-root region 32 bit RAM
使用scatter文件时,可以有下面两种方法来设置固定域。
① 设置输出段地址
第1种方法是设定一个加载域中第1个输出段的运行地址,使其和该加载域的加载地址相同。这样该输出段就是一个固定域。
例1就使用这种方法确定固定域。其中,加载域LR_1的起始地址为0x8000,输出段ER_RO的起始地址指定为0x8000,与加载域LR_1的起始地址相同,因此,输出段ER_RO是一个固定域,并且是映像文件的初始入口点。
例1 指定固定域
LR_1 0x08000 ;加载域LR_1的起始地址为0x8000 { ER_RO 0x08000 ;输出段ER_RO的起始地址为0x8000 { *( + RO) ;包含了所有的RO数据,包含初始入口点 } ;其他部分内容 } |
② 设置输出段属性
第2种方法通过将某个输出段的属性设置成FIXED。
例2指定固定域
LR_1 0x8000 ;加载时域LR_1的起始地址为0x8000 { ER_RO 0x8000 { *( + RO) ;除了init.o之外的其他RO数据 } ER_INIT 0x9000 FIXED ;设置输出段属性为FIXED,确定固定域 { init.o( + RO) ;本输出段包含了init.o,包含映像文件的初始入口点 } ;其他部分内容 } |
3、一个实际系统的例子
在一个嵌入式设备中,为了保持好的性价比,通常在系统中存在多种存储器。在一个实际的ARM开发板中,可能包括片内Flash、RAM和片外Flash、RAM。在本例中,我们假设用ARM芯片构造了一个嵌入式系统,包含了8KB片内Flash存储器、16KB片内RAM存储器、起始地址为0x80000000的片外Flash和起始地址为0x81000000的片外RAM,其地址空间分配关系如图3所示。
在这样的ARM系统中,我们编写了程序,并且按照例3中的分散加载文件对映象文件的地址进行分配。分配后的地址映像关系如图4所示。
图3 ARM系统中的地址空间 图4 地址映像关系
从图4中可以看出:可执行代码都放在片外Flash中,并且Vectors向量表放在片外Flash的起始地址上;Startup目标文件的数据放置在片内RAM中,堆栈放在片内RAM的顶端;其他数据放置在片外RAM中,堆空间紧跟其后。
例3 片外Flash启动程序的scatter文件
ROM_LOAD 0x80000000 ;定义加载区名称ROM_LOAD,起始地址0x80000000 { ROM_EXE 0x80000000 ;定义执行代码空间,起始地址与加载域地址相同 { Startup.o (vectors, +First) ;首先放置Startup.o文件的向量表vectors * ( +RO) ;后面地址空间放置其他RO属性代码 } IRAM 0x40000000 ;定义数据空间 { Startup.o ( +RW, +ZI) } STACKS 0x40004000 UNINIT ;定义堆栈空间 { stack.o ( +ZI) } ERAM 0x81000000 ;定义数据空间 { * ( +RW, +ZI) } ;剩下未指定空间的所有数据 HEAP + 0 UNINIT ;定义堆空间 { heap.o ( +ZI) } |