具体参考armlink_user_guide_100070_0611_00_en.pdf
Chapter 8
Scatter File Syntax
今天拿了被同事扔一边的ARM培训资料翻阅,读至scatter一节,发现写得甚是精辟。之前看的很多国人写得文章,未免有简单问题复杂化之嫌。而ARM的RVCT手册又偏冗长,不易让人立刻看到重点。今归纳如下:
scatter基本点:
1. 编译后输出的映像文件中各段是首尾相连的,中间没有空闲的区域,它们的先后关系是根据链接时参数的先后次序决定的 armlinker -file1.o file2.o ……
2. scatter用于将编译后的映像文件中的特定段加载到多个分散的指定内存区域
3. 有2类域region:执行域(execution region,一般是ram区域)和加载域(load region,一般是rom区域)
4. 加载域:就是编译之后得到的二进制文件烧写到rom中的这一段区域,所有的代码RO、预定义变量RW、堆栈之类清不清空无关紧要的大片内存区域ZI,都包括在其中
5. 执行域:就是把加载域进行‘解压缩’后的样子。比如:RO没有变动还是在ROM中,RW被移到了SRAM中,而ZI被放置在SDRAM中
6. scatter本身并不能对映像实现‘解压缩’,编译器读入scatter文件之后会根据其中的各种地址生成启动代码,实现对映像的加载,而这一段代码就是* (InRoot
7. 起始地址与加载域重合的执行域成为root region,* (InRootSections)它是__main()的一部分。这就是在汇编启动代码的最后跳转到__main() 而不是跳向main()的原因之一。 7. 起始地址与加载域重合的执行域成为root region,* (InRoot
Sections)必须放在这个执行域中,否则链接的时候会报错。*(+RO)包含了* (InRoot
Sections),
scatter语法:
ROM_LOAD 0x00000000
{
ROM 0x00000000 0x003FFFFF
{
vectors.o (+RO,+FIRST)
* (InRoot$$Sections) ; All library sections that must be in a root region
*(+RO)
}
SRAM 0x00400000 0x003FFFFF
{
* (+RW,+ZI)
}
SDRAM1 0x41000000 UNINIT
{
stack.o (+ZI) ; stack.s中定义了top_of_stack为长度为1的space,指定栈顶地址
}
SDRAM2 +0 UNINIT
{
heap.o (+ZI)
}
}
注解:
1. ROM_LOAD是加载域。这里只有一个,也可以有多个(rom地址不连续的情况)
2. ROM、SRAM、SDRAM1、SDRAM2是执行域,有多个。第一个执行域必须和加载域地址重合,因为ARM的复位地址就是加载域的起始地址(有bootstrap的话加载域址就是bootstrap执行完后的跳转地址)
3. vectors.o (+RO, +FIRST) 中断向量表放在最开头
4. ROM 0x00000000 0x003FFFFF; 加载域名 起始地址 最大允许长度;‘最大允许长度’也可以省略,但缺点是编译器不会检查段是否溢出和别的段重叠了。‘起始地址’= +0表示紧接着上一段开始的连续地址。
5. * (InRoot$$Sections)是复制代码的代码
6. UNINT关键字表示不进行初始化清零
值得注意的是:在一个scatter文件中,同一个.o文件不能出现2次,即使是在2个不同的加载域中也不可以,否则会报错:Ambiguous selectors found for *.o,错误的例子:
LOAD1 0x00000000
{
EXE1
{
Init.o
}
}
LOAD2 0xFFFF0000
{
EXE2
{
Init.o
}
}
想起了中学里哲学课上老师让解释为什么人不能两次踏入同一条河流,当年稀里糊涂的写的答案,老师批了个大差,回去有没有补上,今天居然在这里遇到了老问题。。。推测是编译器自动生成的scatter载入代码InRoot$$Sections不支持把同一obj搬移2次。
这就带来一个问题:如果希望把同一段代码(如中断跳转表)载入2份拷贝到不同的地址,咋整?一个笨办法是自己写一段代码搬移程序来代替编译器自动生成的搬移代码,但前提是需要搞懂映像文件的组织,增加了工作量。投机一点的方法是在makefile中把一个.o文件复制并重新起一个名字,然后把它们传递给armlink。另外,猜测scatter语法可能包含诸如+duplicate之类的关键字来允许同一段的多个副本,懒得翻ARM手册,请哪位知情者留言告知,谢过
一个映像文件中可以包含多个域(region),在加载和运行映像文件时,每个域可以有不同的地址。每个域可以包括多达3个输出段,每个输出段是由若干个具有相同属性的输入段组成。这样在生成映像文件时,ARM链接器就需要知道下述两个信息。
-
分组信息 决定各域中的输出段是由哪些输入段组织而成;
-
定位信息 决定各域在存储空间中的起始地址。
-
各个加载时域的加载时起始地址、最大尺寸和属性;
-
每个加载时域包含的输出段;
-
各个输出段的运行时起始地址、最大尺寸、存储访问特性和属性;
-
各个输出段中包含的输入段。
-
Load_name 运行时域名称,它除了唯一地标识一个运行时域外,还用来构成链接器生成的链接符号;
-
base_designator 用来表示本加载时域的起始地址,它可以有两种格式表示:起始地址或偏移量;
-
attribute 本加载时域的属性,其可能的取值为下面之一,默认的取值为ABSOLUTE:
-
PI 位置无关属性;
-
RELOC 重定位;
-
ABSOLUTE 绝对地址;
-
max_size 最大尺寸,如果本加载时域的实际尺寸超过了该值,链接器将报告错误。默认的取值为0xFFFFFFFF。
-
output_name 输出段的名称,它用来唯一地标识一个输出段,还用来构成链接器生成的链接符号。
-
base_designator 用来表示本输出段的起始地址,它可以有两种格式:起始地址值或偏移量。
-
attribute 表示本输出段的属性,其可能的取值如下所示:
-
PI 位置无关属性
-
RELOC 重定位
-
ABSOLUTE 绝对地址
-
FIXED 固定地址
-
UNINIT 未初始化的数据
-
max_size 指定本输出段的最大尺寸。