u-boot armv8鏈接腳本
在進行源碼分析之前,首先看看u-boot的鏈接腳本,通過鏈接腳本可以從整體了解一個u-boot的組成,并且可以在啟動分析中知道某些邏輯是在完成什么工作。
在armv8中,u-boot使用arch/arm/cpu/armv8/u-boot.lds進行鏈接。
u-boot-spl和u-boot-tpl使用arch/arm/cpu/armv8/u-boot-spl.lds進行鏈接,因為每個board的情況可能不同,所以u-boot可以通過Kconfig來自定義u-boot-spl.lds和u-boot-tpl.lds。
4.1 u-boot.lds
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2013
* David Feng < fenghua@phytium.com.cn >
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, < garyj@denx.de >
*/
#include < config.h >
#include < asm/psci.h >
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start) -------------------------------------------------------------------- (1)
/*
*(1)首先定義了二進制程序的輸出格式為"elf64-littleaarch64",
* 架構(gòu)是"aarch64",程序入口為"_start"符號;
*/
SECTIONS
{
#ifdef CONFIG_ARMV8_SECURE_BASE -------------------------------------------------- (2)
/*
*(2)ARMV8_SECURE_BASE是u-boot對PSCI的支持,在定義時可以將PSCI的文本段,
* 數(shù)據(jù)段,堆棧段重定向到指定的內(nèi)存,而不是內(nèi)嵌到u-boot中。
* 不過一般廠商實現(xiàn)會使用atf方式使其與bootloader分離,這個功能不常用;
*/
/DISCARD/ : { *(.rela._secure*) }
#endif
. = 0x00000000; -------------------------------------------------------------- (3)
/*
*(3)定義了程序鏈接的基地址,默認是0,通過配置CONFIG_SYS_TEXT_BASE可修改
* 這個默認值。
*/
. = ALIGN(8);
.text :
{
*(.__image_copy_start) --------------------------------------------------- (4)
/*
*(4)__image_copy_start和__image_copy_end用于定義需要重定向的段,
* u-boot是一個分為重定向前初始化和重定向后初始化的bootloader,
* 所以此處會定義在完成重定向前初始化后需要搬運到ddr中數(shù)據(jù)的起始地址和結(jié)束地址;
*
* 大多數(shù)時候u-boot是運行在受限的sram或者只讀的flash上,
* u-boot為了啟動流程統(tǒng)一會在ddr未初始化和重定位之前不去訪問全局變量,
* 但是又為了保證u-boot能夠正常讀寫全局變量,內(nèi)存,調(diào)用各類驅(qū)動能力,
* 所以u-boot將啟動初始化分為了兩個部分,重定向前初始化board_f和
* 重定向后初始化 board_r,在重定向之前完成一些必要初始化,
* 包括可能的ddr初始化,然后通過__image_copy_start和__image_copy_end
* 將u-boot搬運到ddr中,并在ddr中進行重定向后初始化,這個時候的u-boot就可以
* 正常訪問全局變量等信息了。
*
* 如果想要在board_f過程中讀寫一些全局變量信息該怎么辦呢?
* u-boot通過定義global_data(gd)來完成此功能,
* 后續(xù)在分析到時會詳細講解實現(xiàn)方式。
*/
CPUDIR/start.o (.text*) -------------------------------------------------- (5)
/*
*(5)定義了鏈接程序的頭部文本段,armv8就是
* arch/arm/cpu/armv8/start.S,
* start.S中所有文本段將會鏈接到此段中并且段入口符號就是_start;
*/
}
/* This needs to come before *(.text*) */
.efi_runtime : { ------------------------------------------------------------ (6)
/*
*(6)在定義了efi運行時相關(guān)支持時才會出現(xiàn)使用的段,一般不用關(guān)心;
*/
__efi_runtime_start = .;
*(.text.efi_runtime*)
*(.rodata.efi_runtime*)
*(.data.efi_runtime*)
__efi_runtime_stop = .;
}
.text_rest : ---------------------------------------------------------------- (7)
/*
*(7)除了start.o,其他的所有文本段將會鏈接到此段中;
*/
{
*(.text*)
}
#ifdef CONFIG_ARMV8_PSCI -------------------------------------------------------- (8)
/*
*(8)同(2),是PSCI相關(guān)功能的支持,一般不會使用;
*/
.__secure_start :
#ifndef CONFIG_ARMV8_SECURE_BASE
ALIGN(CONSTANT(COMMONPAGESIZE))
#endif
{
KEEP(*(.__secure_start))
}
#ifndef CONFIG_ARMV8_SECURE_BASE
#define CONFIG_ARMV8_SECURE_BASE
#define __ARMV8_PSCI_STACK_IN_RAM
#endif
.secure_text CONFIG_ARMV8_SECURE_BASE :
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) :
#ifdef __ARMV8_PSCI_STACK_IN_RAM
AT(ADDR(.secure_stack))
#else
AT(LOADADDR(.secure_data) + SIZEOF(.secure_data))
#endif
{
KEEP(*(.__secure_stack_start))
. = . + CONFIG_ARMV8_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE;
. = ALIGN(CONSTANT(COMMONPAGESIZE));
KEEP(*(.__secure_stack_end))
}
#ifndef __ARMV8_PSCI_STACK_IN_RAM
. = LOADADDR(.secure_stack);
#endif
.__secure_end : AT(ADDR(.__secure_end)) {
KEEP(*(.__secure_end))
LONG(0x1d1071c); /* Must output something to reset LMA */
}
#endif
. = ALIGN(8);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ------------------- (9)
/*
*(9)所有僅讀數(shù)據(jù)將會在這個段中對齊排序存放好;
*/
. = ALIGN(8);
.data : { -------------------------------------------------------------------- (10)
/*
*(10)所有數(shù)據(jù)段將會鏈接到此段中;
*/
*(.data*)
}
. = ALIGN(8);
. = .;
. = ALIGN(8);
.u_boot_list : { ------------------------------------------------------------- (11)
/*
*(11)u_boot_list段定義了系統(tǒng)中當前支持的所有命令和設(shè)備驅(qū)動,此段把散落在各個文件中
* 通過U_BOOT_CMD的一系列拓展宏定義的命令和U_BOOT_DRIVER的拓展宏定義的設(shè)備驅(qū)動收集到一起,
* 并按照名字排序存放,以便后續(xù)在命令行快速檢索到命令并執(zhí)行和檢測注冊的設(shè)備和設(shè)備樹匹配
* probe設(shè)備驅(qū)動初始化;(設(shè)備驅(qū)動的probe只在定義了dm模塊化驅(qū)動時有效)
*/
KEEP(*(SORT(.u_boot_list*)));
}
. = 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)
}
. = ALIGN(8);
.rel_dyn_start : -------------------------------------------------------- (12)
/*
*(12)一般u-boot運行時是根據(jù)定義的基地址開始執(zhí)行,如果加載地址和鏈接地址
* 不一致則會出現(xiàn)不能執(zhí)行u-boot的問題。通過一個
* 配置CONFIG_POSITION_INDEPENDENT即可打開地址無關(guān)功能,
* 此選項會在鏈接u-boot時添加-PIE參數(shù)。此參數(shù)會在u-boot ELF文件中
* 生成rela*段,u-boot通過讀取此段中表的相對地址值與實際運行時地址值
* 依次遍歷進行修復(fù)當前所有需要重定向地址,使其可以實現(xiàn)地址無關(guān)運行;
* 即無論鏈接基地址如何定義,u-boot也可以在任意ram地址
* 運行(一般需要滿足最低4K或者64K地址對齊);
*
* 注意此功能只能在sram上實現(xiàn),因為此功能會在運行時修改文本段數(shù)據(jù)段中的地址,
* 如果此時運行在片上flash,則不能寫flash,導(dǎo)致功能失效無法實現(xiàn)地址無關(guān);
*/
{
*(.__rel_dyn_start)
}
.rela.dyn : {
*(.rela*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
_end = .;
. = ALIGN(8);
.bss_start : { -------------------------------------------------------- (13)
/*
*(13)眾所周知的bbs段;
*/
KEEP(*(.__bss_start));
}
.bss : {
*(.bss*)
. = ALIGN(8);
}
.bss_end : {
KEEP(*(.__bss_end));
}
/DISCARD/ : { *(.dynsym) } -------------------------------------------- (14)
/*
*(14)一些在鏈接時無用需要丟棄的段;
*/
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
#ifdef CONFIG_LINUX_KERNEL_IMAGE_HEADER ----------------------------------- (15)
/*
*(15)在efi加載時會很有用,主要在u-boot的二進制頭部添加了一些頭部信息,
* 包括大小端,數(shù)據(jù)段文本段大小等,以便于efi相關(guān)的加載器讀取信息,
* 此頭部信息來自于Linux arm64的Image的頭部信息;該頭部也不屬于u-boot的
* 一部分只是被附加上去的;
*/
#include "linux-kernel-image-header-vars.h"
#endif
}
4.2 u-boot-spl.lds
此鏈接腳本是標準的spl鏈接腳本,還包含了u_boot_list段,如果對應(yīng)自己board不需要命令行或者模塊化驅(qū)動設(shè)備,只作為一個加載器則可以自定義更簡略的鏈接腳本。
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2013
* David Feng < fenghua@phytium.com.cn >
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, < garyj@denx.de >
*
* (C) Copyright 2010
* Texas Instruments, < www.ti.com >
* Aneesh V < aneesh@ti.com >
*/
MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE, ---------------------------------------- (1)
/*
*(1) >XXX 的形式可以將指定段放入XXX規(guī)定的內(nèi)存中;一般u-boot-spl只有
* 很小的可運行內(nèi)存塊,所以spl中會舍去大量不需要用的段只保留關(guān)鍵的
* 文本段數(shù)據(jù)段等,并且通過 >.sram的形式將不在ddr初始化前用到的段定義到sdram中,
* 后續(xù)只需在完成ddr初始化后將這些段搬運到ddr中即可,而不需要額外的
* 地址修復(fù)邏輯,如下:有一個sram 0x18000-0x19000,
* 一個sdram 0x80000000 - 0x90000000,
* 那么通過 >.sram方式則map文件可能如下:
* 0x18000 stext
* ...
* 0x18100 sdata
* ...
* 0x80000000 sbss
* ...
*/
LENGTH = IMAGE_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,
LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start) -------------------------------------------------------------------- (2)
/*
*(2)同u-boot.lds一致,共用一套邏輯入口_start;
*/
SECTIONS
{
.text : {
. = ALIGN(8);
*(.__image_copy_start) -------------------------------------------------- (3)
/*
*(3)同樣的,如果spl需要重定向則會使用此段定義,大多數(shù)情況下spl中會用上重定向;
*/
CPUDIR/start.o (.text*)
*(.text*)
} >.sram
.rodata : {
. = ALIGN(8);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
} >.sram
.data : {
. = ALIGN(8);
*(.data*)
} >.sram
#ifdef CONFIG_SPL_RECOVER_DATA_SECTION ---------------------------------------- (4)
/*
*(4)SPL_RECOVER_DATA_SECTION段用于保存數(shù)據(jù)段數(shù)據(jù),
* 一些board在初始化時修改data段數(shù)據(jù),并在后續(xù)某個階段
* 從此段中恢復(fù)data的原始數(shù)據(jù);
*/
.data_save : {
*(.__data_save_start)
. = SIZEOF(.data);
*(.__data_save_end)
} >.sram
#endif
.u_boot_list : {
. = ALIGN(8);
KEEP(*(SORT(.u_boot_list*)));
} >.sram
.image_copy_end : {
. = ALIGN(8);
*(.__image_copy_end)
} >.sram
.end : {
. = ALIGN(8);
*(.__end)
} >.sram
_image_binary_end = .;
.bss_start (NOLOAD) : {
. = ALIGN(8);
KEEP(*(.__bss_start));
} >.sdram -------------------------------------------------------------- (5)
/*
*(5)將bss段數(shù)據(jù)定義到 >.sdram中,即可在初始化ddr后直接對此段地址清零
* 即可使用全局未初始化變量,并且不會帶來副作用。
*/
.bss (NOLOAD) : {
*(.bss*)
. = ALIGN(8);
} >.sdram
.bss_end (NOLOAD) : {
KEEP(*(.__bss_end));
} >.sdram
/DISCARD/ : { *(.rela*) }
/DISCARD/ : { *(.dynsym) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
從上述的鏈接腳本可以看出,armv8的u-boot的啟動是從arch/arm/cpu/armv8/start.S中的_start開始的 ,并在后續(xù)初始化中調(diào)用了很多鏈接腳本中定義的地址符號表。
-
ARM
+關(guān)注
關(guān)注
134文章
9165瀏覽量
369168 -
Uboot
+關(guān)注
關(guān)注
4文章
125瀏覽量
28347 -
ARMv8
+關(guān)注
關(guān)注
1文章
35瀏覽量
14195
發(fā)布評論請先 登錄
相關(guān)推薦
U-boot的基本介紹
![<b class='flag-5'>U-boot</b>的基本介紹](https://file1.elecfans.com/web2/M00/8C/C4/wKgaomSxDC-AIi1QAACKegMzhAU876.png)
我的U-Boot鏈接腳本筆記
SDK下使用make u-boot編譯NXP官方下載的u-boot編譯不成功怎么辦?
基于armv8架構(gòu)對u-boot進行啟動流程分析(一)
基于armv8架構(gòu)對u-boot進行啟動流程分析(二)
Porting U-Boot to the Control
u-boot的Makefile分析
u-boot簡介
u-boot中Hush shell的功能及編寫腳本的方法介紹
U-Boot架構(gòu)淺析
![<b class='flag-5'>U-Boot</b>架構(gòu)淺析](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
Linux U-Boot開發(fā)指南
![Linux <b class='flag-5'>U-Boot</b>開發(fā)指南](https://file.elecfans.com//web2/M00/95/B6/poYBAGQFT7mAWRkKAAFz-Z4R0zY051.png)
基于armv8架構(gòu)來對u-boot進行啟動流程分析
![基于<b class='flag-5'>armv8</b>架構(gòu)來對<b class='flag-5'>u-boot</b>進行啟動流程分析](https://file1.elecfans.com/web2/M00/89/62/wKgaomSCg4eAEbCIAABbZ4u9mlI903.png)
armv8 u-boot的啟動介紹
![<b class='flag-5'>armv8</b> <b class='flag-5'>u-boot</b>的啟動介紹](https://file1.elecfans.com/web2/M00/B3/0C/wKgaomVxNu-AIvqcAAIPpgxwPoE779.jpg)
評論