本节主要内容介绍ARM平台的分散加载知识。
默认存储器映射
在介绍分散加载之前先简单介绍一下与内存映射相关的知识。在没有使用分散加载文件的情况下,编译器按照m默认存储器对映像文件进行映射。如下图是默认的存储器地址映射。
链接程序放置规则
链接程序遵守一组规则,以决定代码和数据位于存储器中的什么位置,如下图所示。
链接程序放置遵循以下规则:
① 映像首先按属性组织:RO 段在最低的存储器地址,其次是RW段,然后是ZI段。
每一种属性中,代码在数据之前。
② 链接程序按名称的字母顺序放置输入段(Section)。输入段名称即汇编程序AREA伪操作定义的名称。
在输入段中,独立对象的代码和数据,按照对象文件在链接程序命令行中被指定的顺序放置。
要精确放置代码和数据,ARM 公司建议不要过分依靠这些规则。相反,必须使用分散加载机制来完全控制代码和数据的放置。请参阅下一章的调整映像存储器映射以适应目标系统硬件存储器的实际要求。
应用程序启动
多数嵌入式系统中,执行主任务前,执行初始化序列来设置系统。默认的初始化序列如下图所示。
化序列可分成三个功能块:__main 直接跳转到__scatterload;__scatterload 负责建立运行时的映像存储器映射,而__rt_entry(运行时的入口)则负责初始化 C 库。
__scatterload 执行代码和数据复制以及 ZI 数据的清零。对于 ZI 数据的清零和未改变的
RW 数据来说,这一步总是要做的。
__scatterload 跳转到__rt_entry。它设置应用程序的栈和堆,初始化库函数及其静态数据,并调用任何全局声明的对象的构造函数(仅 C++)。
然后__rt_entry 跳转到应用程序入口 main()。主应用程序结束执行时,__rt_entry 将库关闭,然后把控制权交还给调试器。
函数main()有一个特殊含意。main()函数的存在强制链接程序链接到__main和__rt_entry中的初始化代码。没有main()函数,就不会链接到初始化进程,那么一些标准C库功能就不会得到支持。
映像文件存储器映射调整
1、关于分散加载
映像由域(Regions)和输出段(Output Sections)组成。每个域可以有不同的加载地址和执行地址。
分散加载可以更加方便准确的指定映像存储器映射,为映像组件分组和布局提供了全面控制。它能够描述由载入时和执行时分散在存储器映射中的多个区组成的复杂映像映射。虽然,分散加载可以用于简单映像,但它通常仅用于具有复杂存储器映射的映像。
要构建映像的存储器映射,必须向 armlink 提供以下信息:
• 分组信息:决定如何将各输入段组织成相应的输出段和域;
• 定位信息:决定各域在存储空间的起始地址。
有两种方法可以配置指定映像文件的分组和定位信息:如果映像文件中地址映射关系比较简单,可以使用命令行选项;如果映像文件中地址映射关系比较复杂的情况,可以使用一个配置文件。使用该配置文件可以告诉链接器相关的地址映射关系。配置文件又叫Scatter文件,是一个文本文件,通过下面的链接选项来实现。
-scatter filename
1).为分散加载定义的符号
armlink使用Scatter文件创建映像时,它创建一些区相关符号。下表概括了这些符号的意义。
2).使用 Scatter 文件的优势
链接程序的命令行选项提供了一些对数据和代码布局的控制,但要实现对布局的全面控制命令行输入的指令是远远不够的。在下面一些情况下,就需要使用 Scatter 文件对映像布局进行控制。
① 需要实现复杂存储器映射
系统中的代码和数据必须放在多个不同存储器区域中,这样连接器必须知道哪个段放在哪个储存器空间的详细信息。这种情况下,最好用 Scatter 文件实现代码映像的分散加载。
② 系统中存在多种不同类型存储器
许多系统包含多种不同类型存储器,如flash存储器、ROM、SDRAM和快速SRAM。分散载入描述可以将代码和数据放置在最适合的存储器类型中。例如,中断代码可能放在快速SRAM中,以加快中断响应时间,而不频繁使用的配置信息可能放在较慢的flash存储器中。
③ 存储器映射 I/O
分散载入描述可以将数据精确定位在内存地址中,而避免数据和内存映射外围地址相
冲突。
④ 位于固定位置函数
可以将特定函数放在存储器中的同一个位置,这样即使周围的应用程序已经被修改并重新编译,也可以使具有特定功能的函数地址保持不变。
⑤ 使用符号识别堆和栈
可以为堆和栈的位置定义符号,链接应用程序时可以指定该封闭模块的位置。随着目前嵌入式系统越来越复杂,系统中可能同时使用flash、ROM和RAM,所以建议在生产系统映像时使用 Scatter文件。
3).分散加载命令行选项
可以使用下面的命令行选项使用分散加载文件。
-scatter description_file_name
使用该命令可以使链接器使用命令中给出的 description_file_name 文件生成最终的映像
文件。
4).简单存储器映像举例
例如,一个实际系统的存储器映射如下图所示。
为了实现上图的存储器映射,使用下图所现实的Scatter文件。
5).复杂存储器映像实现举例
一个复杂存储器映射如下图所示。
为了实现上图的存储器映射,使用以下程序所现实的 Scatter 文件。
LOAD_ROM_1 0x0000 ;第一个加载时域的起始地址
{
EXEC_ROM_1 0x0000 ;第一个运行时域的起始地址
{
programl.o(+RO) ;放置 program.o 中所以的 RO 段
}
SRAM 0x9000 ;运行时域的起始地址
{
programl.o(+RW,+ZI) ;放置 program.o 中所有的 RW 和 ZI 段
}
}
LOAD_ROM_2 0x4000 ;第二个加载时域的起始地址
{
EXEC_ROM_2 0x4000 ;运行时域的起始地址
{
program2.o(+RO)
}
DRAM 0x18000 ;运行时域的起始地址
{
program2.o(+RW,+ZI)
}
}
上面两个例子中,简单存储器映射可以使用命令行选项实现,但第二个复杂存储器映射的例子却只能使用Scatter文件实现。
Scatter 文件语法
分散载入描述文件是一个文本文件,它向armlink描述目标系统的存储器映射。如果从命令行加载Scatter文件,可以使用任意类型的文件扩展名。
在Scatter文件中,用户可以指定以下存储器映像内容:
• 每个载入区的载入地址和最大尺寸;
• 每个载入区的属性;
• 从每个载入区派生的执行区;
• 每个执行区的执行地址和最大尺寸;
• 每个执行区的输入节。
描述文件的格式反映出载入区、执行区和输入节的层次结构。
1).BNF 的表示法和语法
所谓BNF(Backus Naur Format)即Scatter文件所用的形式语言。下表概括了其所用的符号和语法规则。
2).Scatter 文件语法概述
分散加载描述scatter_description被定义为一个或多个load_region_description模式:
Scatter_description ::=
load_region_description+
加载域描述load_region_description被定义为载入区名称,可以选择性地在其后跟随属性、尺寸说明符以及一个或多个执行区描述:
load_region_description ::=
load_region_name (base_address | ("+" offset)) [attributes] [max_size]
"{"
execution_region_description+
"}"
执行域描述execution_region_description被定义为执行区名称,是一种基址规范,可以
选择性地在其后跟随属性、尺寸说明符以及一个或多个输入段描述:
execution_region_description ::=
exec_region_name (base_address | "+" offset) [attribute_list] [max_size | "–" length]
"{"
input_section_description*
"}"
输入段描述input_section_description被定义为源模块选择程序模式,可以在其后选择性地跟随输入节选择程序:
input_section_description ::=
module_select_pattern
[ "("
("+" input_section_attr | input_section_pattern)
([","] "+" input_section_attr | "," input_section_pattern))*
")" ]
下图显示一个典型的分散载入描述文件的内容和组织结构。
3).加载域描述
一个加载域具有以下属性:
• 名称:链接程序使用它识别不同的加载域;
• 基址:载入视图中的代码和数据的起始地址;
• 属性:可选;
• 最大尺寸:可选;
• 执行区列表:这些执行区标识执行视图中模块的类型和位置。
下图显示了加载域的描述。
BNF语法为:
load_region_description ::=
load_region_name (base_address | ("+" offset)) [attribute_list] [ max_size ]
"{"
execution_region_description+
"}"
语法说明如下。
① load_region_name为加载域的名称。只有前31个字符有效。该名称仅用于识别每个域。
② base_address是区中对象的链接地址。base_address必须是一个字对齐数值。
③ +offset描述基址,它从前一个加载域的末尾偏移offset个字节。offset的值必须能被4整除。如果是第一个加载域,则+offset表示该域的基地址是从0之后的offset字节开始。
④ attribute_list指定加载域内容的属性:
• PI:位置独立;
• RELOC:可重定位;
• OVERLAY:重叠;
• ABSOLUTE:绝对地址;
• NOCOMPRESS:代码不被压缩。
可以指定这些属性中的一项(除NOCOMPRESS外,其他4项属性为互斥关系)。默认的加载域属性是ABSOLUTE。具有PI、RELOC或OVERLAY属性之一的加载域可以有重叠的地址范围。对于 ABSOLUTE加载域,armlink不允许重叠的地址范围。OVERLAY关键字允许在同一个地址有多个执行区。
⑤ max_size:它指定加载域的最大尺寸。(如果指定了可选的max_size值,但分配给该区的字节超过max_size字节,armlink将生成错误。)
⑥ execution_region_description:它指定执行区名称、地址和内容。
4).执行域描述符
执行域具有以下一些属性:
• 执行域基地址(支持绝对地址的或相对地址的);
• 执行域的最大尺寸(可选);
• 指定执行域属性;
• 一个或多个输入段描述(放在本执行区中的模块)。
下图显示了一个典型的执行域描述。
执行域描述符中的BNF语法为:
execution_region_description ::=
exec_region_name (base_address | "+" offset) [attribute_list] [max_size | "–" length]
"{"
input_section_description+
"}"
其语法说明如下。
① exec_region_name为执行域命名。(只有前31个字符有效。)
② base_address是域中对象的链接地址。base_address必须是字对齐的。
③ +offset是描述基址,它从前一个执行区的末尾偏移offset个字节。offset的值必须能被4整除。如果前面没有执行区(即,这是载入区中的第一个执行区),则+offset表示基址从它所在的载入区的基址之后offset个字节开始。如果使用+offset格式并且所在的加载域具有RELOC属性,则执行区继承该RELOC属性。但是,如果使用固定的base_address,则随后出现的offset不继承RELOC属性。
④ attribute_list指定执行区内容的属性:
• PI:位置独立。
• OVERLAY:重叠。
• ABSOLUTE:绝对地址。域的执行地址由 base_designator 指定。
• FIXED:固定地址。执行域的加载地址和执行地址都由 base_designator 指定。
base_designator 必须是绝对基址,或者偏移量为+0。
• EMPTY:它在执行区中保留一个已知长度的空白存储器块,通常用作堆或栈。
• PADVALUE:指定填充字的默认值,如果在域定义中指定了该属性,则必须为该属
性赋值。使用该属性的例子如下。
EXEC 0x10000 PADVALUE 0xffffffff EMPTY ZEROPAD 0x2000
通过该Scatter文件描述符,创建了一个长度为0x2000的域,该域中的所有内容用0xffffffff
填充。
• ZEROPAD 0:初始化一块内容全为0的内存区域,并将其作为一个输入段填充到ELF映像文件中。这样减少了在运行时将某段内存初始化为0的操作。
• UNINIT:指示该段为不能被初始化为0。
⑤ max_size为可选的参数,如果分配给域的存储器超过max_size字节,则它指示armlink生成错误。
⑥ -length如果指定的长度为负值,则base_address是域的结束地址。它通常与EMPTY一起使用,以表示在存储器中变小的栈。
当确定执行域属性时,注意以下几点。
① PI、OVERPLAY、FIXED和ABSOLUTE为并列关系属性,某一个执行域只能为这4种属性之一。如果没有指定,ABSOLUTE为其默认属性。
② 使用+offset格式的base_designator的执行区继承前一个执行区的属性(如果它是加
载域中的第一个执行区,则继承所在加载域的属性,),或者具有ABSOLUTE属性。
③ 不能为执行域显式指定RELOC属性。该属性只能从前面的执行域或父区继承才能具有RELOC属性。
④ 被指定了PI或OVERLAP属性的执行域,不能有重叠的地址范围。但对于ABLOUTE和FIXED属性的执行域,ARM编译器不允许有重叠的地址范围。
⑤ RW段默认使用压缩属性。如果不想链接器对该段进行压缩,必须在Scatter文件中使用NOCOMPRESS显示声明。
⑥ UNINIT指定执行区中的ZI输出节(如果有)不被初始化为0。使用它可以创建包含未初始化数据或存储器映射I/O的执行区。
5).输入段描述符
输入段由以下部分组成。
• 模块名称,如目标文件名称、库成员名称或库文件名称。模块名称可以使用通配符。
• 输入段名称,或输入节属性,如READ-ONLY或CODE。
下图显示了输入段描述符的基本组成。
BNF语法为:
input_section_description ::=
module_select_pattern
["("
("+" input_section_attr | input_section_pattern)
([","] "+" input_section_attr | "," input_section_pattern))*
")"]
其语法说明如下。
① module_select_pattern
这是由文字文本构成的模式。“”通配符匹配 0 个或多个字符,而“?”匹配任何单个字符。匹配不区分大小写。使用.o 可以匹配所有对象。使用*可以匹配所有目标文件和库。
当满足下列条件之一时,链接器认为 module_selector_pattern 与输入段匹配。
• 包含输入段的目标文件与 module_selector_pattern 匹配。
• 包含输入段的库成员名称(不带路径名)与 module_selector_pattern 匹配。
• 从其中提取段的库全名(包含路径名)。如果名称包含空格,使用通配符可以简化搜索。例如,使用*libname.lib 匹配 C:\lib dir\libname.lib。
另外,ARM 链接器支持特殊的模块选择程序模式“.ANY”,允许将输入节分配给执行区,而无需考虑其父模块。使用.ANY 以任意分配方式填充执行区。
② input_section_attr
输入段属性符定义了一个用逗号隔开的模式类别。该类表中的每个模式定义了输入段名称或输入段属性匹配方式。当匹配模式使用输入段名称时,它前面必须使用符号“+”,而符号“+”前面紧接的逗号可以省略。
输入段属性不区分大小写。可以是下列属性之一:
• RO-CODE;
• RO-DATA;
• RO,同时选择 RO-CODE 和 RO-DATA;
• RW-DATA;
• RW-CODE;
• RW,同时选择 RW-CODE 和 RW-DATA;
• ZI;
• ENTRY,包含 ENTRY 点的节。
可以识别以下同义词:
• CODE 代表 RO-CODE;
• CONST 代表 RO-DATA;
• TEXT 代表 RO;
• DATA 代表 RW;
• BSS 代表 ZI。
可以识别以下伪属性:
• FIRST;
• LAST。
如果对输入段的排列顺序有特殊的要求,如特定的输入段必须是域中的第一个输入节,而包含校验和的输入段必须是最后一个输入段,可以使用FIRST和LAST标记执行区中的第一个和最后一个段。FIRST或LAST伪属性必须放在属性列表的最后。
特殊的模块选择程序模式“.ANY”允许在不考虑其父模块的情况下,将输入段分配给执行域。使用一个或多个“.ANY”模式以任意分配方式填充执行域。在大多数情况下,使用单个“.ANY”相当于使用“*”模块选择属性。
在分散载入描述文件中不能使用两个“*”选择属性。但是,可以使用两个变形的选择程序,例如,A和B,也可以将.ANY选择属性与模块选择属性一起使用。模块选择属性的优先级比.ANY高。如果删除了文件中包含选择属性的部分,.ANY选择属性才能在链接时起作用。
在解析所有其他(非.ANY)输入段描述并且将输入段分配给最匹配的执行区之后,才解析使用.ANY 模块选择程序模式的input_section_descriptions。如果有一个以上.ANY模式,则链接程序尽可能多地填充第一个.ANY,然后开始填充下一个.ANY。
每个未被分配的剩余输入段将被分配给具有以下特性的执行区:
• 最大的剩余空间(由max_size的值和已分配给该区的输入段的尺寸确定);
• 匹配.ANY的input_section_description;
• 与输入段的存储器属性相匹配的存储器访问属性(如果有);
• input_section_pattern。
Scatter 文件典型用法
1).创建启动域
所谓启动域就是加载地址和执行地址相同的域。系统执行的初始入口点必须要在启动域
中,否则链接器将报告以下错误。
Entry point (0x00000000) lies within non-root region ER_ROM
Scatter文件中确定启动域可以使用下面两种方法。
① 使用ABSOLUTE设置执行区属性,并且对第一个执行区及其所在的加载区使用相同的地址。为确保执行域地址和加载域地址相同,可以将加载域的起始地址和执行域的起始地址设为相同的值或者将第一个执行域的地址偏移量设为0。
下面的例子,指定了一个启动域。
BOOT 0x0000 ;加载域的起始地址在0x0
{
EXER 0x0000 ;指定加载域和执行域的地址相同
{
* (+RO) ;必须将启动域包含在内
}
;其他执行域
}
② 使用FIXED执行域属性,确保指定域的载入地址和执行地址相同。
下面的例子显示了使用FIXED属性,将执行域的起始地址固定在ROM中。
BOOT 0x0000 ;加载域的起始地址在0x0
{
EXER 0x0000 ;指定加载域和执行域的地址相同
{
* (+RO) ;必须将启动域包含在内
}
EXER_INIT 0x8000 FIXED
{
init.o(+RO)
}
}
③ 如果使用分散加载,负责创建执行域的代码和数据不能将其自身复制到另一位置,因此启动域必须包含以下内容。
• _main.o 和_scatter*.o:包含复制代码和数据的代码。
• Region$$Table和ZISection$$Table段:包含要复制代码和数据的地址。
• _dc*.o:执行代码压缩。
可以使用armlinker产生的InRoot S e c t i o n s 符号放置启动代码。因为这些代码被定义为只读属性,所有如果 S c a t t e r 文件中包含了“ ∗ ( + R O ) ”,则表示启动域中包含了这些代码。或者显式的使用 I n R o o t Sections符号放置启动代码。因为这些代码被定义为 只读属性,所有如果Scatter文件中包含了“*(+RO)”,则表示启动域中包含了这些代码。或 者显式的使用InRoot Sections符号放置启动代码。因为这些代码被定义为只读属性,所有如果Scatter文件中包含了“∗(+RO)”,则表示启动域中包含了这些代码。或者显式的使用InRootSections符号在Scatter文件中对以上代码进行配置。
下面的例子显示了如何在Scatter文件中使用InRoot$$Sections链接符号,放置启动域。
LOADREG 0x8000
{
ROOT 0x8000
{
* (InRoot$$Sections) ;放置启动域
}
OTHER 0x100000
{
* (RO,+RW,+ZI)
}
;其他 Scatter 文件描述
}
2).为执行域确定固定地址
可以在执行区分散加载描述中使用FIXED属性来创建根区,该根区在固定地址载入和执行。
FIXED可以用于在单一加载域内(因此通常用于单个ROM设备)创建多个根区。
例如,使用FIXED属性将函数或数据块(如常数表或校验和)放在ROM中的固定地址,这样就可以使用指针很方便的对其进行访问。
下面的例子显示了如何放置单个目标内容。
LOADREG1 0x0 0x10000
{
EXECREG1 0x0 0x1000 ;启动域,包含初始化代码
{ ;将初始化代码放在 0x0 地址
init.o (Init, +FIRST)
* (+RO) ;随后排放余下的只读数据
}
RAM 0x400000 0x2000 ;将可读可写数据放在 0x400000 地址
{
* (+RW +ZI)
}
DATABLOCK 0x4FF00 FIXED 0xFF ;执行域放在 0x4FF00 地址
{ ;限制该域的最大长度为 0xFF
data.o(+RO-DATA) ;将只读数据放在 0x1FF00 和 0x1FFFF 之间
}
}
通过上面的Scatter文件,可以将初始化代码放在0x0处,其后是其他RO代码和除了data.o
对象中的RO数据之外的所有RO数据;所有全局的RW变量放在RAM中0x400000处;最好将data.o的RO-DATA只读数据表放在地址0x4FF00处,并指定其最大长度为0xFF。
等效的简单映像分散载入描述
前面介绍了分散加载的命令行选项,如-ro-base、-rw-base、-reloc、-split、-ropi和-rwpi。
但在实际编程时,因为使用Scatter文件可以产生更清晰的内存映像视图,所以最好使用Scatter文件对映像进行加载。
本节详细介绍如何将各分散加载的命令行选项,替换为Scatter文件。
1).-ro-base address 选项的替换
使用-ro-base address命令行链接产生的内存映像由一个加载域和三个执行域组成。执行
域放在存储器映像中的相邻位置。选项中的address指定了加载域和第一个执行域的起始地址(加载域和第一个执行域的起始地址相同)。
下面的例子显示了与“-ro-base 0x8000”命令行选项等价的Scatter文件。
LOADREG 0x8000 ;定义加载域的起始地址 0x8000
{
ROM +0 ;定义第一个执行域的起始地址,该地址与加载域的起始地址相同,为 0x8000
{
*(+RO) ;该域放置所有的 RO 段
}
RAM_RW +0 ;定义第二个执行域,起始地址为 0x8000+ROM 段大小
{
*(+RW) ;将所有的 RW 代码放置在该段
}
RAM_ZI +0 ;定义 ZI 段
;ZI 段的起始地址为 0x8000+ROM 段的大小+RAM_RW 段的大小
{
*(+ZI) ;放置所有的 ZI 段
}
}
上例中的Scatter文件创建的映像由一个加载域和三个执行域组成。加载域的起始地址为0x8000。三个执行域分别为ROM、RAM_RW和RAM_ZI,它们分别包含RO、RW和ZI输出段。RO和RAM_RW为启动域,RAM_ZI在执行时动态创建。ROM的执行地址是0x8000,通过对执行区描述使用+offset格式的基址指定程序,所有三个执行域在存储器映射中相邻放置,即前一个执行域的末尾放置后一个执行域。
如果链接程序时,将-ro-base选项和-ropi混合使用,则可以生成位置无关代码。
下面的例子显示了与-ro-base 0x8000 -ropi 等效的Scatter文件。
LOADREG 0x8000 PI ;加载域的地址为 0x8000,并指定该加载域的属性为 PI
{
ROM +0 ;第一执行域的地址为 0x8000,而且该执行域继承了加载域的 PI 属性
;所有该域的执行地址是可变的
{
*(+RO) ;放置所有的 RO 段
}
RAM_RW +0 ABSOLUTE ;使用 ABSOLUTE 属性代替 PI 属性
{
*(+RW) ;放置 RW 段
}
RAM_ZI +0
{
*(+ZI)
}
}
执行域ROM从LOADREG加载域继承PI属性。下一个执行域RAM_RW被标记为ABSOLUTE所以其不再具有PI属性。另外,因为RAM_ZI域使用了+0的偏移量,所以它从RAM_RW域继承ABSOLUTE属性。
2).-ro-base 和-rw-base 选项的替换
使用-ro-base和-rw-base选项链接的映像也由一个加载域和三个执行域组成,它与类型1生成的映像十分相似,只是此类映像的RW执行区与RO执行区不相邻。在-ro-base选项中指定加载域的起始地址,在-rw-base选项中指定执行域的地址。
下面的例子显示与使用-ro-base 0x8000 -rw-base 0x040000等效的分散载入描述。
LOADREG 0x8000 ;定义加载域的起始地址为 0x8000
{
ROM_RO +0 ;定义第一个执行域的起始地址为 0x8000
{
* (+RO) ;在该域中放置所有的 RO 段
}
RAM_RW 0x040000 ;第二个执行域名为 RAM_RW,起始地址为 0x40000
{
* (+RW) ;放置所有的 RW 段
}
RAM_ZI +0
{
* (+ZI) ;放置所有的 ZI 段
}
}
该Scatter文件创建的映像有一个名为LOADREG的加载域,载入地址是0x8000。该映像有3个执行区,分别为ROM、RAM_RW和RAM_ZI,它们分别包含RO、RW和ZI输出段。其中,RO域是启动域,执行地址是 0x8000,RAM_RW执行域与第一个执行域RAM_RW不相邻。其执行地址是 0x040000。紧随其后的执行区RAM_ZI放置所有的ZI数据。
另外,也可以将-rw-base和位置无关选项-rwpi配合使用,将RW输出节的执行区标记为位置独立。
下面的例子显示了使用-ro-base 0x8000 -rw-base 0x40000 -rwpi等效的Scatter文件。
LOADREG 0x0x8000 ;定义加载域的起始地址为 0x8000
{
ROM +0 ;定义第一执行域,其起始地址为 0x8000
{
*(+RO) ;放置所有 RO 段
}
RAM_RW 0x40000 PI ;设置第二执行域的属性为 PI 属性
{
*(+RW)
}
ER_ZI +0 ;继承了 PI 属性
{
*(+ZI)
}
}
第一个执行域ROM从加载域LOADREG继承ABSOLUTE属性。第二个执行区RAM_RW标记为PI属性。另外,因为ER_ZI区的偏移为+0,所以它从RAM_RW区继承PI属性。
3).-reloc -split 选项的替换
使用-split 选项生成的映像由两个加载域和三个执行域组成。
使用以下的链接选项重新分割并定位加载域。
• -reloc
组合使用-reloc -split生成具有两个加载域的映像,并且使加载域具有RELOC属性。
• -ro-base address1
指定包含 RO 输出段的域的载入地址和执行地址。
• -ro-base address2
指定包含 RW 输出段的域的载入地址和执行地址。
• -split
将默认的单一加载域(包含RO和RW输出段的加载域)分成两个加载域。一个载入域包含RO输出段,另一个包含RW输出段。
下面的例子显示了与使用-ro-base 0x8000 -rw-base 0x040000 -split等效的Scatter文件。
LOADREG1 0x8000 ;指定第一个加载域的起始地址为 0x8000
{
ROM +0
{
*(+RO)
}
}
LOADREG2 0x040000 ;第二个加载域的起始地址为 0x40000
{
RAM_RW +0
{
*(+RW) ;放置所有的 RW 段
}
RAM_ZI +0
{
*(+ZI)
}
}
使用上例中的Scatter文件创建的内存映像有两个加载域,分别为LOADREG1和LOADREG2,它们的起始地址分别为0x8000和0x040000。
该映像文件有三个执行域,分别为ROM、RAM_RW和RAM_ZI,它们分别包含RO、RW和ZI输出段。ROM的执行地址是0x8000。
RAM_RW执行域与ROM不相邻。其执行地址是0x040000。执行域 RAM_ZI紧随RAM_RW域放置。
可以使用-reloc选项和-split选项配合使用,指定两个加载域具有RELOC属性。
下面的例子显示与使用-ro-base 0x8000 -rw-base 0x040000 -reloc -split等效的Scatter文件。
LOADREG 0x010000 RELOC
{
ROM + 0
{
* (+RO)
}
}
LOADREG 0x040000 RELOC
{
RAM_RW + 0
{
* (+RW)
}
RAM_ZI +0
{
* (+ZI)
}
}