ARM中的各种源文件(包括汇编文件,C语言程序及C++程序等)经过ARM编译器编译后生成ELF(Executable and linking format)格式的目标文件。这些目标文件和相应的C/C++运行时用到的库经过ARM连接器处理后,生成ELF格式的映像文件(image),这种ELF格式的映像文件是一种可执行文件,可被写入嵌入式设备的ROM 中。
bin文件是真正的可执行文件,axf文件是ARM的调试文件,除了包含bin的内容之外,还附加了其他的调试信息,这些调试信息加在可执行的二进制数据的前面,所以把axf文件写到ARM的指令执行地址(一般是0x0)将不能运行,因为在此地址前几十个字节的数据不是可执行的二进制数据,而是头部的调试信息;而bin文件正是去掉了调试信息的可以执行的“精华”部分。
ARM映像文件的组成
ARM映像文件是一个层次性结构的文件,包括了域(region),输出段(output section)和输入段(input section)。一个映像文件由一个或者多个域组成;每个域最多由三个输出段组成组成;每个输出段又包含一个或者多个输入段;各输入端包含了目标文件中的代码和数据。
相关术语:
1.域 (region):一个映象文件由一个或多个域组成。反过来说域是组成映象文件的最大的结构。所谓域,指的就是整个bin映像文件所处在的区域,它又分为加载域和运行域。加载域就是映像文件被静态存放的工作区域,一般来说flash里的整个bin文件所在的地址空间就是加载域,当然程序一般都不会放在flash里执行,一般都会搬到sdram里运行工作,它们在被搬到sdram里工作所处的地址空间就是运行域。一个域通常映射到一个物理存储器上,如ROM和RAM等。
2.段(Section):一个域包含一个或多个输出段,一个输出段包含一个或多个输入段。我们输入的代码,一般有代码部分和数据部分,这就是所谓的输入段,每个输入段都有相应的属性,可以为只读(ro),可读写的(rw)以及初始化成0的(zi)。
3.RO,RW,ZI:输入段中包含4类内容:代码、已经初始化的数据、未经初始化的存储区域、内容初始化为0的存储区域。每个输入段有相应的属性,可以为只读(RO)、可读写(RW)以及初始化为0的(ZI)。ARM连接器根据各输入段的属性将这些输入段分组,再组成对应属性的输出段。对于加载域中的输出段,一般来说ro段后面紧跟着rw段,rw段后面紧跟着zi段。在运行域中这些输出段并不连续,但rw和zi一定是连着的。zi段和rw段中的数据其实可以是rw属性。
注:
(1)C中的指令以及常量被编译后是RO类型数据。
(2)C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
(3)C中的已被初始化成非0值的变量编译后市RW类型数据。
4.加载时地址:是映象文件位于存储器(还没有运行,一般在ROM中)时的地址
5.运行时地址:是映象文件运行时的地址。
通常一个映像文件中包含若干个域,各个域又包含若干的输出段。ARM连接器就需要知道如下信息以决定生成相应的映像文件。烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。
*分组信息 :决定如何各将输入段组织成相应的输出段和域。
*定位信息 :决定各个域在存储器空间中的起始地址。
根据映像文件中地址映射的复杂程度有两种方法告诉ARM连接器这些相关的信息。
(1)当映像文件中最多包含两个域,每个域最多有三个输出段时,可以使用连接器选项告诉连接器相关的地址映射关系。选项有-ropi,-rwpi,-ro_base,-rw_base,-split等。
(2)当映像文件地址映射关系更复杂时,可以使用一个配置文件(分散加载文件)告诉连接器相关的地址映射关系。
ARM映像文件数据移动:
上面已经提到了RW段加载地址一般在ROM中,运行时需要被搬运到RAM中。加载时状态的映象文件中的RO、RW和ZI的地址都是临时的,他们在运行时要被BootLoader程序搬运到真正的运行时地址。这个地址是连接时设置的地址。这个问题很重要,如果在编译前没有正确的设置运行时地址,那么程序就不能被搬运到正确的RAM地址中运行。
=======================================================================================================
了解了以上内容,那么就可以打开ADS1.2来看一下具体的设置。
打开ADS的一个工程后,点击如下图所示的Debug Settings按钮打开对话框。打开Target Settings对话框后,在左边列表中选择Linker选项。点击它下面的的ARM Linker。然后在右面的选项卡选择Output选项卡。如下图所示。
看看ADS 开发文档ARM Developer suite 1.2 的ADS_CodeWarriorIDEGuide.pdf 怎么说的
RO Base This text field sets both the load address and execution address of the region containing the RO
section. If you do not enter a value, the value defaults to 0x8000.
意思是,这个文本框设置加载时地址和运行时地址。如果没有设置值,默认时0x8000。这个值将会对应ADS的预定义变量Image$$RO$$Base,指定了RO的base。这个变量可以被初始化程序IMPORT进去。这个参数有两个意思:
1.如果生成可执行bin文件烧写到flash中去,那么这个地址就是要烧到flash中的地址(一般是0x0)。这里又出现一个问题,如果使用的ARM芯片是支持memory remap的(如三星的4510芯片),那么可以在bootloader程序中将RO段搬运到RAM中,再把RAM remap 到0x0,这样系统读取RO段的时候就可以在RAM中读了。如果ARM芯片不支持remap(如三星的44b0x),那么RO段不能搬运到RAM中,而在FLASH中读取。
2.如果生成afx调试文件,那么这个地址是调试时加载到RAM中的地址。
根据上面的1,2可知,如果要烧写FLASH 那么RO Base 应该设置成ARM片选的FLASH 的首地址;如果要调试那么RO Base要设置成RAM地址。
RW Base 这个文本框设定包含RW和ZI输出段的运行时域地址。如果你在这里输入一个值,连接器创建一个包含两个运行时域的映象,这两个域是:
包含RO输出段的运行时域
包含RW和ZI输出段的运行时域
如果你输入了RW Base值并且选择了Split image选项,连接器创建的映象文件分别包含RW输出段和ZI输出段的装载时地址和运行时地址,并都由你输入的RW Base值指定。
对于简单连接方式,当没有输入RW Base值时,映象文件包含一个加载时域和一个运行时域。这时,RO输出段、RW输出段、ZI输出段都包含在一个域中。当输入RW Base值时,映象文件包含两个运行时域,一个包含RO输出段,一个包含RW输出段和ZI输出段。当指定了-split选项时,映象文件又多包含两个加载时域,一个包含RO输出段,一个包含RW输出段和ZI输出段。
简单的初始化用户程序的执行环境
ARM映像文件一开始总是存储在ROM/Flash里面的,其RO部分既可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去,其实RW包括ZI区域,ZI区域放的是未赋值的全局变量,RW 区域放的是已赋值(赋0除外)的全局变量。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。
先介绍几个必要的符号,编译器使用下列符号来记录各段的起始和结束地址:
|Image$$RO$$Base| :RO段起始地址
|Image$$RO$$Limit| :RO段结束地址加1 (在加载域中,是RW的起始地址)
|Image$$RW$$Base| :RW段起始地址 (在运行域中即运行的时候,是RW的起始地址)
|Image$$RW$$Limit| :ZI段结束地址加1
|Image$$ZI$$Base| :ZI段起始地址
|Image$$ZI$$Limit| :ZI段结束地址加1
这些符号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的。 由于rw和zi相连,|Image$$ZI$$Base|就等于|Image$$RW$$Limit| .其它的值都是编译器自动计算出来的。我们还可以通过scatter文件更详细得指定各个输出段的工作地址。
初始化用户执行环境主要是把ro、rw、zi三段拷贝到指定的位置。
下面的程序是rw、zi段在运行域中的搬运过程:
;Copy and paste RW data/zero initialized data
ldr r0, =|Image$$RO$$Limit| /*取RO区末地址后面的地址,即RW数据源的起始地址*/
ldr r1, =|Image$$RW$$Base| /*取RW区在RAM里的执行区起始地址,即编译器选项RW_Base指定的地址*/
ldr r3, =|Image$$ZI$$Base| /*取ZI区在RAM里面的起始地址*/
;Zero init base => top of initialised data
cmp r0, r1 /* 比较ROM区中数据段首地址和RAM区中RW段目标首地址*/
beq %F2 /*相等代表当前已经是在RAM中运行*/(F表示after,B表示before,r0与r1相等则转跳)
/*B %F2表向前跳到标号为2的Lable处*/
1
cmp r1, r3 /*不相等则和RAM区中ZI段的目标地址比较*/
ldrcc r2, [r0], #4 /*如果r1<r3,则把r0地址上的数据读出到r2中,然后r0=r0+4*/
strcc r2, [r1], #4 /*如果r1<r3,则把r1地址上的数据读出到r2中,然后r0=r0+4*/
bcc %B1 /*如果r1<r3,则跳转到Lable为1处继续执行*/
2
ldr r1, =|Image$$ZI$$Limit| /* 取ZI段的结束地址 */
mov r2, #0 /*将r2赋值为0*/
3
cmp r3, r1 ; Zero init
strcc r2, [r3], #4 /*如果r3<r1,将r2内容写入到r3地址单元中,然后r3=r3+4*/
bcc %B3 /*如果r3<r1,则跳转到Lable为3处继续执行*/