1.1 Realview MDK链接程序的两种方式
使用RealviewMDK时不可避免的要涉及到链接脚本文件,特别是编译链接那些大的工程文件时更是如此。在链接脚本中可以指定代码的存储布局,可以将代码段、只读数据段、可读写的数据段分别存放,甚至可以精确地指定代码放置的位置,这一点是很关键的,譬如说启动代码就必须放在可执行文件的开始位置。由于链接脚本重要性,开发者必须掌握其编写的方法。
Realview MDK链接程序使用了两种方式控制程序的链接:
1.1.1链接控制命令选项
当使用链接控制命令选项时,链接器定义了12个段地址描述符, 这12个描述符可以直接在程序中引用.
Image$$ 执行区符号符号说明
Image$$region_name$$Base 区的执行地址。
Image$$region_name$$Length 执行区长度(以字节为单位),不包括 ZI 长度。
Image$$region_name$$Limit 执行区中非 ZI 部分末尾后面的字节的地址。
Image$$region_name$$RO$$Base 此区中的 RO 输出节的执行地址。
Image$$region_name$$RO$$Length RO输出节的长度(以字节为单位)。
Image$$region_name$$RO$$Limit 执行区中 RO 输出节末尾后面的字节的地址。
Image$$region_name$$RW$$Base 此区中的 RW 输出节的执行地址。
Image$$region_name$$RW$$Length RW 输出节的长度(以字节为单位)。
Image$$region_name$$RW$$Limit 执行区中 RW 输出节末尾后面的字节的地址。
Image$$region_name$$ZI$$Base 此区中的 ZI 输出节的执行地址。
Image$$region_name$$ZI$$Length ZI 输出节的长度(以字节为单位)。
Image$$region_name$$ZI$$Limit 执行区中 ZI 输出节末尾后面的字节的地址。
它的RW长度计算:Length = (Image$$RW$$Limit-Image$$RW$$Base)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0206ic/Chdcgbjd.html
1.1.1链接脚本文件*.sct
使用链接脚本文件后,上面提到的6个描述符号没有了,取而代之的是链接脚本文件中的段描述符.
在编译连接时如果指定了分散加载文件(.sct文件),在连接后会自动生成如下变量:
下面将结合3个具体的例子说明链接脚本文件的使用,下面三个例子中,载入区域和执行区域的名字是可以任意命名的,对这些段地址的引用可以使用如Image$$ LR_IROM1$$Base
Image$$ LR_IROM1$$Limit
Image$$ RW_RAM1 $$Base
Image$$ ZI_RAM1$$Limit
长度计算:Length = (Image$$段名$$Limit-Image$$段名$$Base)
代码段长度的计算方法为:Image$$ER_ROM1$$Limit-Image$$ER_ROM1$$Base
数据段长度的计算方法为:Image$$RW_RAM1$$Limit-Image$$RW_RAM1$$Base
引用链接脚本文件中的变量时,首先应该声明,下面是在汇编文件中和C/C++文件中的声明和引用方法:
A:汇编文件中的声明和引用方法:
声明:IMPORT ||Image$$region_name$$ZI$$Limit||
引用:LDR r0, =||Image$$region_name$$ZI$$Limit||
B: C/C++文件中的声明和应用方法:
声明:extern unsigned int Image$$region_name$$Limit
引用:config.heap_base = (unsigned int) &Image$$region_name$$Limit
例1 一个加载区域,多个连续的执行区域
;**************************************************************
; *** ******Scatter-Loading Description File***************
; **************************************************************
LR_IROM1 0x00000000 ; 定义载入区域LR_IROM1的起始地址为0x00000000
{
ER_IROM1 +0 ;执行区域ER_IROM1的起始地址紧接载于区域LR_IROM1的起始地址,即为
;0x00000000.
{
*(+R0) ;所有的只读代码段都连续地放在这个区域
}
RW_RAM1 +0 ;可读写数据段RW_RAM1紧接ER_IROM1段的尾地址存放,即0x00000000 +
;LR_IROM1的容量
{
* (+RW) ;所有的可读写的数据段(可读写的程序)都连续地放在这个区域
}
ZI_RAM1 +0 ;清零数据段ZI_RAM1(初始化为零的变量或者是未初始化的变量)紧接;
;RW_RAM1段的尾地址存放
{
*(+ZI) ;所有的清零数据段都连续地放在这个区域
}
}
例2 一个加载区域,多个非连续的执行区域
; **************************************************************
; *** ******Scatter-Loading Description File***************
; **************************************************************
LR_IROM1 0x00010000 ;定义载入区域LR_IROM1的起始地址为0x00010000
{
ER_IROM1 +0 ;执行区域ER_IROM1的起始地址紧接载于区域LR_IROM1的起始
;地址,即为0x010000.
{
*(+R0) ;所有的只读代码段都连续地放在这个区域
}
RW_RAM1 0x00040000 ;可读写数据段RW_RAM1的起始地址0x00040000
{
*(+RW) ;所有的可读写的数据段(可读写的程序)都连续地放在这个区域
}
ZI_RAM1 +0 ;清零数据段ZI_RAM1(初始化为零的变量或者是未初始化的变量)
;紧接RW_RAM1段尾地址存放,即为0x040000 +RW_RAM1的容量
{
*(+ZI) ; 所有的清零数据段都连续地放在这个区域
}
}
例3 两个加载区域,多个非连续的执行区域
; *************************************************************
; *** ******Scatter-Loading Description File***************
; *************************************************************
LR_IROM10x00010000 ;定义载入区域LR_IROM1的起始地址为0x00010000
{
ER_IROM1 +0 ;执行区域ER_IROM1的起始地址紧接载于区域LR_IROM1的起始地址
;即为0x010000.
{
*(+R0) ;所有的只读代码段都连续地放在这个区域
}
}
LR_IROM20x00040000 ;定义载入区域LR_IROM2的起始地址为0x00040000
{
RW_RAM1 +0 ;可读写数据段RW_RAM1的起始地址0x00040000
{
*(+RW) ;所有的可读写的数据段(可读写的程序)都连续地放在这个区域
}
ZI_RAM1 +0 ;清零数据段ZI_RAM1(初始化为零的变量或者是未初始化的变量)
;紧接RW_RAM1段的尾地址存放,即为0x040000 +RW_RAM1的容量
{
*(+ZI) ;所有的清零数据段都连续地放在这个区域
}
}
实际代码中应用说明:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
; Run in flash
LR_ROM1 0x00000000
{
ER_ROM1 0x00000000 0x00200000
{
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_RAM1 0x30000000 0x04000000
{
.ANY (+RW +ZI)
}
RW_IRAM1 0x40000000 0x00001000 {
.ANY (+RW +ZI)
}
}
-------------------------------------------------------------------------------------------------
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
; Run in RAM
LR_ROM1 0x30000000 { ; load region
ER_ROM1 0x30000000 0x0040000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_RAM1 +0 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM1 0x40000000 0x00001000 {
.ANY (+RW +ZI)
}
}
----------------------------------------------------------------------------------------
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
; Two Section
LR_ROM1 0x00000000
{
ER_ROM1 0x00000000 0x00200000
{
S3C2440A.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_RAM1 0x30050000 0x03000000
{
.ANY (+RW +ZI)
}
RW_IRAM1 0x40000000 0x00001000 {
.ANY (+RW +ZI)
}
}
LR_ROM2 0x00001000
{
ER_ROM2 0x30000100 0x00004000
{
keyled.o (+RO)
}
}
使用上述两段分散加载的,必须先把0x1000的代码copy到0x30000100处,程序才能正常运行。
------------------------------------------------------------------
Notes:
* (InRoot$$Sections)
起始地址与加载域重合的执行域成为root region,有许多 ARM库节必须放置在根区(rootregion)中,例如 __main.o、__scatter*.o、__dc*.o 和*Region$$Table。 此列表在不同版本中可能有差异。 链接器可以使用InRoot$$Sections 自动放置所有这些节,而不会影响将来的使用。
*(+RO)包含了* (InRoot$$Sections),所以如果在rootregion中用到了*(+RO)可以不再指定* (InRoot$$Sections)
.ANY (+RO)
.ANY(+RO) 是指其下面的执行域对于指定的模块,RO数据指定了存放地址,用.ANY就可以把已经指定的具有RO属性的Code/data排除。