uboot.lds 链接文件分析
lds文件与scatter文件相似都是决定一个可执行程序的各个段的存储位置,以及入口地址,这也是链接定位的作用。
U-boot的lds文件说明如下:
SECTIONS{
...
secname start BLOCK(align)(NOLOAD):AT(ldadr)
{contents}>region:phdr = fill
...
}
secname和contents是必须的,前者用来命名这个段,后者用来确定代码中的什么部分放在这个段,以下是这个描述中的一些关键字的解释。
1、secname :段名
2、contents :决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段,数据段等)
3、start:是段的重定位地址,本段连接(运行)的地址, 如果代码中有位置无关指令,程序运行时这个段必须放在这个地址上,start可以用任意一种描述地址的符号来描述。
例如:
/*nand.lds*/
SECTIONS {
first 0x00000000:{head.o init.o}
second 0x30000000:AT(4096) {main.o}
}
以上,head.o放在0x00000000地址开始处,init.o放在head.o后面他的运行地址也是0x00000000,即连接地址和存储地址相同 (没有AT指定);
main.o放在4096(0x1000是AT指定的存储地址)开始处,但他的运行地址在0x30000000,运行之前需要从0x1000(加载地址)复制到0x30000000(运行地址处),此过程也就需要读取flash,把程序拷贝到相应位置才能运行。这就是存储地址和运行的不同,称为加载时域和运行时域可以在.lds连接脚本文件中分别制定。
编写好的.lds文件,在用arm-linux-ld连接命令时带-Tfilename来调用执行,如
arm-linux-ld -Tnand.lds x.o y.o -o xy.o
也可用-Ttext参数直接指定连接地址如:
arm-linux-ld -Ttext 0x30000000 x.o y.o -o xy.o
既然程序有了 两种地址,就涉及到一些跳转指令的区别。
-
b step: b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的bit[23:0]算出来的,这使得使用b指令的不依赖于要跳到的代码的位置,只看指令本身。
-
ldr pc,=step;该指令是一个伪指令编译后会生成以下代码:
ldr pc,0x30008000
<0x30008000>
step
是从内存中的某个位置(step)读出数据并赋给PC,同样依赖于当前PC的值,但是偏移量是step的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转。
- 此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实现当前程序是在RAM中还是在Flash中:
relocate: /*把u-boot重新定位到RAM*/
adr r0,_start /*r0是代码的当前位置*/
ldr r1,_TEXT_BASE /*测试判断从Flash启动,还是从RAM启动此句执行的结果r1始终是0x33FF80000*/ /*因为此值是链接指定的*/
cmp r0,r1 /*比较r0和r1,调试的时候不要执行重定位*/
/adr伪指令,汇编器会自动通过当前PC的值得算出这条指令中“_start" 的值,执行到start时PC的值放到r0中:
当此段在flash中执行时 r0=_start=0;当此段在RAM中执行时_start=_TEXT_BASE(在board/smdk2410/config.mk)中指定的值为0x33F80000,即U-Boot在把代码拷贝到RAM中去执行的代码段的开始/
iMX6U Uboot.lds 分析
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
指定输出可执行文件elf格式,32位ARM指令,小端模式
OUTPUT_ARCH(arm) 指定输入平台为ARM
ENTRY(_start) 指定输出可执行文件的起始代码为_start
_start 在文件arch/arm/lib/vectors.S
SECTIONS
{
. = 0x00000000; 定位当前地址为0地址
. = ALIGN(4); 代码以4字节对齐
.text : 指定代码段
{
*(.__image_copy_start) 指定地址为 0X87800000
*(.vectors) .vectors段
vectors 段保存中断向量表,在vectors.S 中
arch/arm/cpu/armv7/start.o (.text*) arch/arm/cpu/armv7/start.s 的生成文件
*(.text*) 其他的代码段
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 指定只读数据段
. = ALIGN(4);
.data : {
*(.data*) 指定读写数据段
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .;
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}