啟動流程
要分析 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)的内容。