u-boot.lds是U-boot的总链接脚本,通过根目录下的config.mk中LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds 知道我们使用的是位于u-boot-2010.06/arch/arm/cpu/hi3521a/ 里的u-boot.lds。
关于config.mk的分析可以参考博客《顶层config.mk分析》,其它的博客可以参考《序言和目录》
u-boot.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
- 指定输出可执行文件是elf格式,32位ARM指令,小端
- 指定整个程序的入口地址,可以认为是第一句指令,_start是start.S的第一个lable
- 值得注意的是,程序入口并不代表它位于存储介质的起始位置。一般起始位置存放的是16字节校验头和异常向量表
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
- SECTIONS表示正式开始地址划分
- .的意思是当前地址,这句将当前地址(代码段起始地址)设为0x00000000,但是其实这个地址会被config.mk用-Ttext $(TEXT_BASE)指定的虚拟地址0x80800000(/board/hi3521a/config.mk中定义)覆盖掉
.text :
{
__text_start = .;
arch/arm/cpu/hi3521a/start.o (.text)
drivers/ddr/ddr_training_impl.o (.text)
drivers/ddr/ddr_training_ctl.o (.text)
drivers/ddr/ddr_training_boot.o (.text)
drivers/ddr/ddr_training_custom.o (.text)
__init_end = .;
ASSERT(((__init_end - __text_start) < 0x4000), "init sections too big!");
*(.text)
}
- .text表示开始代码段的链接
- 代码段的链接顺序很重要,首先start.o必须在第一个
- 由于uboot需要重定位,故所有和重定位有关的代码必须链接在最前面,uboot启动的第一阶段需要用到这些文件。
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
- . = ALIGN(4)的意思是将当前地址(代码段结束地址)四字节对齐,然后将其作为只读数据段的起始地址(存放只读的全局变量)
- 同理,对数据段(存放全局变量)和got段进行相同设置
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
. = ALIGN(4);
_end = .;
- 设置自定义段u_boot_cmd,里面存放着的是一个个命令结构体(结构体内都是命令的信息),它们是紧挨着的,其实有点像结构体数组,只不过是乱序的。写出 __u_boot_cmd_start, __u_boot_cmd_endt的地址是为了要在源码中引用这两个地址,由此来使用命令结构体
- 最后设置bss段(存放初始值为0的全局变量),写出__bss_start,_end就是为了要在.s或.c中引用这两个地址。
- __bss_start = . 表示bss段的开始地址,这个地址是不固定的,在编译的时候是不知道的,只有在连接的时候才知道,它是从text段开始,接着rodata、data、got、__u_boot_cmd_start这些段都链接完才知道.代表的当前位置是在那个地址。
参考博客:
- https://blog.csdn.net/itxiebo/article/details/50938753
- https://feng-qi.github.io/2016/09/07/linker-script/
- https://www.jianshu.com/p/42823b3b7c8e
- https://blog.csdn.net/qq_33160790/article/details/78887349