Scatter File的用法 === 对于嵌入式开发来说,scatter file显得异常重要,尤其是想把某段内容链接到指定的地址区域的时候,这些内容可以是code、const常量和变量。 如果是ARM平台的话,在ARM的linker guide里有详细介绍scatter file的用法。其实DSP程序的开发也会有类似scatter file的东西,记得当时用TI DSP的时候有个叫做cmd文件的东西,里面会要求指定各个段的链接地址,包括起始地址和size。从理论上来说,可以指定每一个变量,每一段代码链接的 位置,当然,其实许多时候并没有必要这样做。但是scatter file确实提供了这样一种精确控制的方法。记得有看过一点tms3202812的例子程序,好像在cmd文件中进行了精细的控制,为每个寄存器定义了一 个名字(一个变量),然后在cmd文件中指定了这个变量的链接链接地址,这样就可以实现操作这个变量即是操作指定的寄存器,用起来很方便。(只是记得是这 样,具体没有仔细看) 在基于ARM的嵌入式开发中,scatter file是一个文本文件,其为linker所用,linker会按照指定的原则来进行链接。为了讲解如何将某个变量或者某段代 码链接到指定位置,我们先来看几个概念。 段(Section):段分为输入段(input section)和输出段(output section),段是连接器操作的基本单位。 输入段(Input Section):输入段作为linker的输入,分布在多个目标文件或者库中。 输出段(Output Section):Linker的输出是一个可执行的映像,在这个映像中各个变量等被链接到指定的地址区域,这些地址区域就是 scatter file中指定的执行区。 段的属性:段的属性有三种,包括Read-Only(简称RO,只读,包括只读的数据和代码)、Read- Write(简称RW,可读写)、Zero-Initialized(简称ZI,初始化为0的可读写数据,通常未初始化的数据也包含在内)。另外段还可以 有一个名字,通过这个名字来区分同一个段的不同部分。 讲完段(section)以后,我们看一下以下几个概念,Image(映像)、Load region(加载区)、Execution region(执行区)。 Image(映像):Linker将目标文件(object)和库(lib)链接之后的输出即是 Image,Image通常是可执行的二进制文件(当然也可能是不可执行的资源文件等)。在Image中通常包含了只读的code和data、初始化的数 据。(可以想一下为什么不包含ZI数据?) Load Region(加载区):在系统上电以后,Image被加载到目标系统中(通常是bootloader将Image搬到RAM中),这 个时候Image还没有开始执行(即内核文件还未解压,可以想一下为什么需要解压),此时各个段在RAM中的区域就是Load Region。 Execution Region(执行区):系统上电加载到RAM中以后开始执行,这个时候内核就需要解压。为什么需要解压?之前有讲到Image并没有 包含ZI数据,因为ZI数据都是0,只要在解压的时候将这些变量初始化为0就行了,没有必要把这些0值都放在Image里面来占用空间;而RW数据都是初 始化的数据,这些变量如果不保存一个初始值linker就不知道该初始化为多少。内核解压就是要将RW数据和ZI数据在指定的区域进行初始化,初始化为初 始值或者0。这些指定的区域就是Execution Region(执行区)。另外RO(只读的数据和代码)也有可能会搬动。 对于scatter file的用法这里不会做过多介绍,具体内容可以参考ADS Linker Guide,在ARM网站上就有。下面主要讲如何将指定的段链接到指定的地址区域上去。 这里假设我有一个文件叫做example.c,编译结束后会生成目标文件example.obj,我打算将RW数据链接到0×80000000开始的 4KB(0×1000)区域内,将ZI数据链接到0×40000000开始的8KB(0×2000)区域内,RO data和code保留在原有位置不动。这样的设置只要设置scatter file即可达到目的,scatter file如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ROM 0x0 0x1000000 ;该行指定映像加载区的起始位置为0x0,最大32MB { RAM_A 0x0 0x100000 ;该行指定执行区的起始位置在0x0,最大1MB { Example.obj (+RO) * (+RO) ;使用通配符*将其余文件的RO段也放在RAM_A区域内 } RAM_B 0x80000000 0x1000 ;RW数据放在2GB开始的4KB区域 { Example.obj (+RW) } RAM_C 0x40000000 0x2000 ;ZI数据放在1GB开始的8KB区域 { Example.obj (+ZI) } …… } 这里面需要注意的一点是ROM和RAM_A的起始位置必须要相同,RAM_A是系统上电后要执行的第一段代码,中断向量表和内核解压的代码也就在这里面。 如果这个区域链接到了其他的位置,那么系统从ROM的起始地址开始执行,第一条指令就不是RAM_A的第一条指令了。 上面的例子是一种简单的情况,如果我有这样一种需求,我想把example.c文件中定义的某个数组和部分的RO Data和Code链接到内部RAM(假设从0×20000000开始的32KB区域),而把其他的RW和ZI变量链接到到外部RAM(假设从 0×40000000开始的32MB区域)。对于这种需求就需要将同一个段中不同的部分进行区分,这就需要为段命名。可按照如下步骤进行操作: 1、在将要链接到INTSRAM_CODE区域的code用下面一对预编译指令包起来 1 2 3 #pragma arm section code = "INTSRAMCODE" #pragma arm section code 2、在要链接到INTSRAM_CONST区域的常量用下面一对预编译指令包起来 1 2 3 #pragma arm section rodata = "INTSRAMCONST" #pragma arm section rodata 3、在要链接到INTSRAM_DATA区域的变量用下面一对预编译指令包起来 1 2 3 #pragma arm section rwdata = "INTERNRW" #pragma arm section rwdata 4、设置scatter file如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ROM 0x0 0x1000000 { RAM_A 0x0 0x100000 { * (+RO) ;默认情况下所有的code都放在RAM_A中 } INTSRAM_CODE 0x20000000 0x1000 ;内部RAM的前4KB区域放指定的code { Example.obj (INTSRAMCODE) ;名为INTSRAMCODE段中code都会放在这里 } INTSRAM_CONST +0x0 0x4000 ;前一个区域之后接着就是16KB的INTSRAM_CONST区域 { Example.obj (INTSRAMCONST) ;名为INTSRAMCONST的rodata都会放在这里 } INTSRAM_DATA +0x0 0x2000 ;前一个区域之后紧接着就是8KB的INTSRAM_DATA区域 { Example.obj (INTERNRW) ;名为INTERNRW的rwdata都会放在这里 } EXTRAM 0x40000000 0x1000000 ;起始于0x40000000的32MB外部RAM { * (+RW +ZI) ;其他的RW和ZI数据都会默认放在这里 } } 5、这样设定后重新编译链接工程即可。 需要注意的几点: 1、上面声明的一些section name,可以任意起名字,只要保持与scatter file中设置相同即可,当然起一些有意义的名字更容易理解。 2、可以将code、rodata、rwdata、zidata组合在一起写,只要将要搬的内容包起来即可,如下所示 1 2 3 #pragma arm section code = "INTSRAMCODE", rodata = "INTSRAMCONST", rwdata = "INTSRAMRW" , zidata = "INTSRAMZI" #pragma arm section code, rodata, rwdata , zidata
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.