1.ARM映像文件
ARM中的各种源文件(包括汇编文件,C语言程序及C++程序等)经过ARM编译器编译后生成ELF格式的目标文件。这些目标文件和相应的C/C++运行时用到的库经过ARM连接器处理后,生成ELF格式的映像文件(image),这种ELF格式的映像文件是一种可执行文件,可被写入嵌入式设备的ROM中。
ARM映像文件的组成:ARM映像文件是一个层次性结构的文件,包括了域(region),输出段(output section)和输入段(input section)。
一个映像文件由一个或者多个域组成;每个域最多由三个输出段组成组成;每个输出段又包含一个或者输入段;各输入端包含了目标文件中的代码和数据。
所谓域,指的就是整个bin映像文件所处在的区域,它又分为加载域和运行域。加载域就是映像文件被静态存放的工作区域,一般来说flash里的整个bin文件所在的地址空间就是加载域,当然程序一般都不会放在flash里执行,一般都会搬到sdram里运行工作,它们在被搬到sdram里工作所处的地址空间就是运行域。
我们输入的代码,一般有代码部分和数据部分,这就是所谓的输入段,每个输入段都有相应的属性,可以为只读(ro),可读写的(rw)以及初始化成0的(zi)。ARM连接器根据各输入段的属性将这些输入段分组,再组成对应属性的输出段。对于加载域中的输出段,一般来说ro段后面紧跟着rw段,rw段后面紧跟着zi段。在运行域中这些输出段并不连续,但rw和zi一定是连着的。zi段和rw段中的数据其实可以是rw属性。
通常一个映像文件中包含若干个域,各个域又包含若干的输出段。ARM连接器就需要知道如下信息以决定生成相应的映像文件。
*分组信息 :决定如何各将输入段组织成相应的输出段和域。
*定位信息 :决定各个域在存储器空间中的起始地址。
根据映像文件中地址映射的复杂程度有两种方法告诉ARM连接器这些相关的信息。
(1)当映像文件中最多包含两个域,每个域最多有三个输出段时,可以使用连接器选项告诉连接器相关的地址映射关系。选项有-ropi,-rwpi,-ro_base,-rw_base,-split等。
(2)当映像文件地址映射关系更复杂时,可以使用一个配置文件(分散加载文件)告诉连接器相关的地址映射关系。
2.简单的初始化用户程序的执行环境
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
|Image$$RW$$Base| :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段在运行域中的搬运过程:
IMPORT |Image$$RO$$Limit| /*表示RO区末地址后面的地址,即RW数据源的起始地址*/
IMPORT |Image$$RW$$Base| /*RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址*/
IMPORT |Image$$ZI$$Base| /*ZI区在RAM里面的起始地址*/
IMPORT |Image$$ZI$$Limit| /*ZI区在RAM里面的结束地址后面的一个地址*/
IMPORT Main ; 声明C程序中的Main()函数
AREA Start,CODE,READONLY ; 声明代码段Start
ENTRY ; 标识程序入口
CODE32 ; 声明32位ARM指令
Reset LDR SP,=0x40003F00
; 初始化C程序的运行环境
LDR R0,=|Image$$RO$$Limit| /* 取ROM区中数据段的首地址 */
LDR R1,=|Image$$RW$$Base| /* 取RAM区中RW段的目标首地址*/
LDR R3,=|Image$$ZI$$Base| /*取RAM区中ZI段的首地址 */
CMP R0,R1 /* 比较ROM区中数据段首地址和RAM区中RW段目标首地址*/
BEQ LOOP1 /*相等代表当前是在RAM中运行*/
LOOP0 CMP R1,R3 /*不相等则和RAM区中ZI段的目标地址比较*/
LDRCC R2,[R0],#4 /*如果r1STRCC R2,[R1],#4 /*如果r1BCC LOOP0 /*如果r1LOOP1 LDR R1,=|Image$$ZI$$Limit| /* 取ZI段的结束地址 */
MOV R2,#0
LOOP2 CMP R3,R1
STRCC R2,[R3],#4 /*如果r3BCC LOOP2 /*如果r3B Main ; 跳转到C程序代码Main()函数