|
LD链接脚本浅析
链接脚本主要描述如何将输入文件中的各个部分映射到输出文件中,并控制输出文件的内存布局。
对于复杂的工程我们一般都采用链接脚本来告知Linker输出文件的布局,对于简单功能的工程,也可以通过链接选项设置。
以E907SDK内的链接脚本gcc_csky.ld为例,对链接脚本的基本构成及相关语法做简要介绍
1- 链接脚本的基本构成
一般分成两部分,MEMORY命令定义了当前平台的内存分布;SECTION命令定义了输出文件在内存的布局。
2- 链接脚本具体构成
2.1 MEMORY
MEMORY命令主要定义了当前平台上的内存,比如在E907 SDK内,smartL平台内对地址空间的分配如下,
在示例工程内,定义了4个不同的内存区域,分别用来存放代码段和不同的数据段。
MEMORY
{
I-SRAM : ORIGIN = 0x0 , LENGTH = 0x40000 /* I-SRAM 256KB /
D-SRAM : ORIGIN = 0x20000000 , LENGTH = 0xc0000 / D-SRAM 768KB /
O-SRAM : ORIGIN = 0x50000000 , LENGTH = 0x800000 / off-chip SRAM 8MB /
SRAM : ORIGIN = 0x60000000 , LENGTH = 0x20000 / on-chip SRAM 128KB */
}
通过MEMORY命令中的ORIGIN和LENGTH关键字定义了内存的起始地址和长度。
为了便于区分每一段的作用,在示例工程的ld文件内还通过REGION_ALIAS为内存分配了别名。
REGION_ALIAS("REGION_TEXT", I-SRAM);
REGION_ALIAS("REGION_RODATA", I-SRAM);
REGION_ALIAS("REGION_DATA", D-SRAM);
REGION_ALIAS("REGION_BSS", D-SRAM);
2.2- 可执行文件起始点
Linker可以通过几种不同的方式来决定可执行文件的入口地址,即第一条被执行指令的地址。Linker将按以下方式循序搜寻,如找到相关定义就停止搜寻并采用该入口点。
linker选项”-e”定义
链接脚本内通过ENTRY命令定义
目标定义的关键符号,对于大多数目标来说是start
”.text”段内的第一个byte地址
地址0x0
在示例工程中,通过在链接脚本中用ENTRY定义文件入口,
ENTRY(Reset_Handler)
2.3- 输出文件的布局
通过SECTIONS命令定义输出文件的内存布局。
在我们的示例工程中,主要包含以下的文件段,代码段(.text),只读数据段(.rodata),数据段(.data),全局变量段(.bss),堆数据段(.user_heap)。
以定义.data输出段为例。通过*.data : { }*定义了.data 段,data段的VMA和LMA地址不一样,通过”> REGION_DATA”指定了运行时该段的地址,”AT > REGION_RODATA”指定了加载时该段的地址。如果该段的LMA和VMA相同,则只需要通过”> REGION_DATA”指定即可。
在.data定义的段内,定义了.data段在内存中如何放置。
对于常见的一些符号,参见注释。
.data : {
. = ALIGN(0x4) ; /强制地址对齐,返回当前地址,此处是4-byte对齐/
__sdata = . ; /定义符号 sdata,其值为当前地址。”.”表示当前地址/
data_start = . ;
data_start = . ;
KEEP(startup.o(.vectors*)) /保留startup.o文件名字包含vectors的段,即中断向量表,并将其放在.data段的起始位置/
*(.got.plt)
*(.got)
(.gnu.linkonce.r)
*(.data)
(.data)
*(.data1)
(.data.)
......
__edata = .;
data_end = .;
. = ALIGN(0x4) ;
} > REGION_DATA AT > REGION_RODATA /运行时,.data 段将放置在REGION_DATA,存储时将存放在REGION_RODATA区域,在初始化时,将对数据进行搬移/
在startup.S文件内可以看到相关数据搬移代码。
/* Load data section */
la a0, __erodata //end of .rodata(REGION_RODATA, I-SRAM)
la a1, data_start //start of .data(REGION_DATA, D-SRAM)
la a2, data_end //end of .data
bgeu a1, a2, 2f //拷贝完成,跳出循环
1:
lw t0, (a0)
sw t0, (a1)
addi a0, a0, 4
addi a1, a1, 4
bltu a1, a2, 1b //a1<=a2, 拷贝未完成,继续循环
//REGION_DATA(VMA):.data运行时的地址
//REGION_RODATA(LMA):.data加载时的地址,
//.rodata 放在 REGION_RODATA, 范围是__srodata ~ __erodata
//.data 放在 .rodata后面, 拷贝到 __data_start开始的区域
2.4- 堆栈地址初始化
示例工程中,在链接脚本内对堆做了内存地址分配。
._user_heap : {
. = ALIGN(0x4) ;
__heap_start = .; /定义了符号,值为堆的起始地址,可以在后续堆的操作中引用/
. += __min_heap_size; /堆的大小做了预留/
. = ALIGN(0x4) ;
} > REGION_BSS AT > REGION_BSS
在实际工程创建中,可以根据系统需求,在连接文件内进行堆或栈的初始化操作。
具体的链接脚本可以参考SDK内的对应脚本
|
+10
|