启动流程
要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接脚本为arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds文件
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") /*输出文件类型 大小端*/
OUTPUT_ARCH(aarch64) /*输出架构*/
ENTRY(_start) /*指定start函数为起始点*/
SECTIONS #将各个不同的段合到一起
{
/DISCARD/ : { *(.rela._secure*) }
. = 0x00000000;
/*指定可执行文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。必须使编译器知道这个地址,通常都是修改此处来完成*/
. = ALIGN(8); /*代码段8字节对齐*/
.text : /*代码段*/
{
*(.__image_copy_start) /*uboot将自己从flash copy到RAM*/
arch/arm/cpu/armv8/start.o (.text*) /*start.S中的text*/
}
/*在定义了efi运行时相关支持时才会出现使用的段,一般不用关心*/
.efi_runtime : {
__efi_runtime_start = .;
*(.text.efi_runtime*)
*(.rodata.efi_runtime*)
*(.data.efi_runtime*)
__efi_runtime_stop = .;
}
.text_rest :
{
*(.text*)#除了start.o其余的代码段
}
.__secure_start :
{
KEEP(*(.__secure_start))/*KEEP告诉链接器不要优化掉此输入section,即使没有引用到此section*/
}
.secure_text 0x00001000 :
AT(ADDR(.__secure_start) + SIZEOF(.__secure_start))
{
*(._secure.text)
. = ALIGN(8);
__secure_svc_tbl_start = .;
KEEP(*(._secure_svc_tbl_entries))
__secure_svc_tbl_end = .;
}
.secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text))
{
*(._secure.data)
}
.secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data),
CONSTANT(COMMONPAGESIZE)) (NOLOAD) :
AT(LOADADDR(.secure_data) + SIZEOF(.secure_data))
{
KEEP(*(.__secure_stack_start))
. = . + 4 * (1 << 10);
. = ALIGN(CONSTANT(COMMONPAGESIZE));
KEEP(*(.__secure_stack_end))
}
. = LOADADDR(.secure_stack);
.__secure_end : AT(ADDR(.__secure_end)) {
KEEP(*(.__secure_end))
LONG(0x1d1071c);
}
. = ALIGN(8);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }/*只读数据段 8字节对齐*/
. = ALIGN(8);
.data : {
*(.data*)/*可读写数据段 8字节对齐*/
}
. = ALIGN(8);
. = .;
. = ALIGN(8);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)))/*data段结束后,紧接着存放u-boot自有的一些function,例如boot、tftpboot等*/
}
. = ALIGN(8);
.efi_runtime_rel : {
__efi_runtime_rel_start = .;
*(.rel*.efi_runtime)
*(.rel*.efi_runtime.*)
__efi_runtime_rel_stop = .;
}
. = ALIGN(8);
.image_copy_end :
{
*(.__image_copy_end)/*至此,uboot需要自拷贝的内容结束*/
}
. = ALIGN(8);
.rel_dyn_start :/*在lds文件中有__rel_dyn_start和__rel_dyn_end,这两个符号之间的区域存放着动态链接符号,只要给这里面的符号加上一定的偏移,拷贝到内存中代码的后面相应的位置处,就可以在绝对跳转中找到正确的函数。*/
{
*(.__rel_dyn_start)
}
.rela.dyn : {
*(.rela*)/*动态链接符放在的段*/
}
.rel_dyn_end :
{
*(.__rel_dyn_end) /*动态链接符段结束*/
}
_end = .;
. = ALIGN(8);
/*bss段开始:BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。和data段的区别是不分配空间*/
.bss_start : {
KEEP(*(.__bss_start));
}
.bss : {
*(.bss*)
. = ALIGN(8);
}
.bss_end : {
KEEP(*(.__bss_end));
}
/*bss段结束*/
/*一些链接时没有用上丢弃的段*/
/DISCARD/ : { *(.dynsym) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
u-boot.lds脚本文件告诉链接器linker如何布局代码段、数据段、bss段等,已经配置了u-boot自拷贝(从flash到RAM的copy)的内容。