本文基于 armv8 架構來對 u-boot 進行啟動流程分析,u-boot版本為2022-01。
1 概述
首先引用wiki上的簡介:u-boot 是一個主要用于嵌入式系統的引導加載程序,可以支持多種不同的計算機系統結構。
u-boot最先是由德國DENX軟件中心團隊開發,后續眾多有志于開放源碼bootloader移植工作的嵌入式開發人員將各個不同系列嵌入式處理器的移植工作不斷展開和深入,以支持了更多的嵌入式操作系統的裝載與引導。
選擇u-boot的理由:
開放源碼;支持多種嵌入式操作系統內核的引導,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android;支持多個處理器系列,如PowerPC、ARM、x86、MIPS;
較高的可靠性和穩定性;高度靈活的功能設置,適合U-Boot調試、操作系統不同引導要求、產品發布等;
豐富的設備驅動源碼,如串口、以太網、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、鍵盤等;
較為豐富的開發調試文檔與強大的網絡技術支持;
基于以上理由本篇文章對現在主流的armv8架構的u-boot啟動流程進行詳細分析,以便所有人快速學習和理解u-boot的工作流程。
2 armv8 u-boot的啟動
先看arm官網提供的一張圖:
上圖詳細概括了arm官方推薦的armv8的啟動層次結構:
官方將啟動分為了BL1,BL2,BL31,BL32,BL33階段,根據順序,芯片啟動后首先執行BL1階段代碼,接著驗簽啟動BL2,BL2根據具體設計啟動BL31或者BL33,BL32只有在有BL31時才可能會存在并被驗簽加載啟動。
armv8分為Secure World和Non-Secure World(Normal World),四種異常級別從高到低分別為EL3,EL2,EL1,EL0。
Secure World就是可以執行可信的firmware和app,比如密碼支付,指紋識別等一系列依賴安全保證的服務。Non-Secure World就是我們常見的u-boot,linux,qnx等裸機程序或者操作系統。
EL3具有最高管理權限,負責安全監測和安全模式切換。EL2主要提供了對虛擬化的支持。EL1是一個特權模式,能夠執行一些特權指令,用于運行各類操作系統,在安全模式則是可信任OS。EL0是無特權模式,所有APP應用都在EL0。
上圖中的BL1,BL2,BL31,BL32,BL33分別對應如下功能:
BL1:是一切信任的根,一般就是固化在ROM中的一段啟動加載代碼,用于引導bl2,并對bl2進行驗簽保證可信任執行;
BL2:一般是在flash中的一段可信安全啟動代碼,它的可信建立在bl1對它的驗證,主要完成一些平臺相關的初始化,比如對ddr的初始化等,并在完成初始化后尋找BL31或者BL33進行執行;如果找到了BL31則不會繼續調用BL33,如果沒有BL31則BL33必須有;
BL31:BL31不像BL1和BL2是一次性運行的,它作為最后一道可信任固件存在,在系統運行時通過smc指令陷入EL3調用系統安全服務或者在Secure World和Non-Secure World之間進行切換;在完成BL31初始化后會去尋找BL32或者BL33進行驗簽后加載執行;
BL32:OPTee OS + 安全app,它是一個可信安全的OS運行在EL1并在EL0啟動可信任APP(上述的指紋驗證等app),并在Trust OS運行完成后通過smc指令返回BL31,BL31切換到Non-Seucre World繼續執行BL33;
BL33:非安全固件,也就是我們常見的UEFI firmware或者u-boot也可能是直接啟動Linux kernel;
啟動BL1,BL2,BL31,BL32則是一個完整的ATF信任鏈建立流程(ARM Trusted Firmware),像常見的PSCI(Power State Coordination Interface)功能則是在ATF的BL31上實現;
最后一張圖完整展示整個調用流程:
BL2根據是否存在BL31和BL32可選擇性的加載不同firmware;
綜上所述,可知u-boot是一個運行在非安全世界的bootloader,負責加載各類操作系統,并提供豐富的驅動接口;并根據是否存在安全固件還可以進行不同的boot流程,如下。
u-boot,u-boot-spl,u-boot-tpl的關系:對于一般嵌入式而言只需要一個u-boot作為bootloader即可,但是在小內存,或者有atf的情況下還可以有spl,tpl;
spl:Secondary Program Loader,二級加載器?
tpl:Tertiary Program Loader,三級加載器
出現spl和tpl的原因最開始是因為系統sram太小,rom無法在ddr未初始化的情況下一次性把所有代碼從flash,emmc,usb等搬運到sram中執行,也或者是flash太小,無法完整放下整個u-boot來進行片上執行。
所以u-boot又定義了spl和tpl,spl和tpl走u-boot完全相同的boot流程,不過在spl和tpl中大多數驅動和功能被去除了,根據需要只保留一部分spl和tpl需要的功能,通過CONFIG_SPL_BUILD和CONFIG_TPL_BUILD控制;
一般只用spl就足夠了,spl完成ddr初始化,并完成一些外設驅動初始化,比如usb,emmc,以此從其他外圍設備加載u-boot,但是如果對于小系統spl還是太大了,則可以繼續加入tpl,tpl只做ddr等的特定初始化保證代碼體積極小,以此再次從指定位置加載spl,spl再去加載u-boot。
從目前來看,spl可以取代上圖中bl2的位置,或者bl1,根據具體廠商實現來決定,有一些芯片廠商會將spl固化在rom中,使其具有從emmc,usb等設備加載u-boot或者其他固件的能力。
當然在有atf的情況下可以由atf加載u-boot,或者由spl加載atf,atf再去加載u-boot。甚至在快速啟動的系統中可以直接由spl啟動加載linux等操作系統而跳過啟動u-boot;在上圖中arm官方只是給出了一個建議的啟動信任鏈,具體實現都需要芯片廠商來決定;
后續分析啟動流程中會在具有SPL和TPL的地方拓展它們的分叉執行路徑盡量把SPL和TPL的功能也一并分析;
3 u-boot源碼整體結構和一些編譯配置方式
3.1 編譯配置方式
u-boot使用了同Linux一樣的編譯配置方式,即使用kbuild系統來管理整體代碼的配置和編譯,通過defconfig來定制各種不同廠商的芯片bootloader二進制程序。編譯只需要注意通過環境變量或者命令行參數的方式引入一個交叉編譯工具即可:
CROSS_COMPILE:定義交叉編譯工具鏈,可以是aarch64-linux-gnu-,arm-none-eabi-或者ppc-linux-gnu-等等;u-boot有幾個配置是需要由對應board配置的。
SYS_ARCH,SYS_CPU,SYS_SOC,SYS_BOARD,SYS_VENDOR,SYS_CONFIG_NAME;一般在board/vendor/board/Kconfig中可全部定義,部分SYS_CPU,SYS_SOC也可以在arch/xxx/Kconfig中定義,根據這幾個配置即可確定使用的cpu架構,廠商,板級信息,soc信息。
Makefile會自動根據上述信息進入對應目錄組織編譯規則,一般如果沒有自己對應的這些board信息,需要自己在對應目錄建立這些Kconfig和在configs中建立defconfig。
在configs目錄中保存了uboot中所有支持的board配置,比如要使用rk3399的evb板的配置信息使用如下方式即可編譯出來:
?
make?CROSS_COMPILE=aarch64-linux-gnu-?evb-rk3399_defconfig make
?
如果沒有對應的defconfig可以找一個與自己板級信息類似的defconfig生成一個.config,再通過menuconfig來完成自己board的配置,并最后通過savedefconfig保存為自己board的defconfig:
?
make?CROSS_COMPILE=aarch64-linux-gnu-?evb-rk3399_defconfig make?menuconfig make?savedefconfig cp?defconfig?configs/my_defconfig
?
下面是evb rk3399的定義:
?
CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="armv8" CONFIG_SYS_SOC="rk3399" CONFIG_SYS_VENDOR="rockchip" CONFIG_SYS_BOARD="evb_rk3399" CONFIG_SYS_CONFIG_NAME="evb_rk3399"
?
根據CONFIG_SYS_BOARD的定義還會為每個源文件自動包含include/configs/xxxx.h頭文件,evb rk3399則是include/configs/evb_rk3399.h頭文件。這個頭文件可在其中定義board的一些關鍵配置,系統的ram大小,環境變量的起始地址和大小,GIC基地址,時鐘頻率,是否開啟看門狗等定義,可根據具體需求來定義。
u-boot使用Kconfig和include/configs/xxx.h來靈活的確定u-boot編譯流程及最終生成的文件。比如當定義CONFIG_SYS_CPU為"armv8",CONFIG_SYS_ARCH為"arm"時,即確定了目標架構為armv8會自動根據Makefile進入對應目錄進行編譯鏈接。
3.2 u-boot源碼結構
這里只對一些常用的目錄進行說明:
arch:各種架構的啟動初始化流程代碼,鏈接腳本等均在此目錄對應的架構中存放;
board:包含了大部分廠商的board初始化代碼,基本平臺化相關的代碼都在對應的board目錄中,早期的一些board代碼在arch/xxx/xxx-mach中,現在基本不會放在arch目錄下面了;
cmd:包含了大量實用的u-boot命令的實現,比如md,cp,cmp,tftp,fastboot,ext4load等命令的實現,我們也可以在此處添加自己實現的命令;
common:包含了u-boot的核心初始化代碼,包括board_f,board_r,spl等一系列代碼;
configs:包含了所有board的配置文件,可直接使用;
drivers:大量驅動代碼的存放處;
dts:編譯生成dtb,內嵌dtb到u-boot的編譯規則定義目錄;
env:環境變量功能實現代碼;
fs:文件系統讀寫功能的實現,里面包含了各類文件系統的實現;
include:所有公用頭文件的存放路徑;
lib:大量通用功能實現,提供給各個模塊使用;
net:網絡相關功能的實現;
scripts:編譯,配置文件的腳本文件存放處;
tools:測試和實用工具的實現,比如mkimage的實現代碼在此處;
4 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??* ?*?(C)?Copyright?2002 ?*?Gary?Jennejohn,?DENX?Software?Engineering,? ?*/ #include? #include? OUTPUT_FORMAT("elf64-littleaarch64",?"elf64-littleaarch64",?"elf64-littleaarch64") OUTPUT_ARCH(aarch64) ENTRY(_start)?--------------------------------------------------------------------?(1) /* ?*(1)首先定義了二進制程序的輸出格式為"elf64-littleaarch64", ?*????架構是"aarch64",程序入口為"_start"符號; ?*/ SECTIONS { #ifdef?CONFIG_ARMV8_SECURE_BASE?--------------------------------------------------?(2) /* ?*(2)ARMV8_SECURE_BASE是u-boot對PSCI的支持,在定義時可以將PSCI的文本段, ?*????數據段,堆棧段重定向到指定的內存,而不是內嵌到u-boot中。 ?*????不過一般廠商實現會使用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中數據的起始地址和結束地址; ?* ?*????大多數時候u-boot是運行在受限的sram或者只讀的flash上, ?*????u-boot為了啟動流程統一會在ddr未初始化和重定位之前不去訪問全局變量, ?*????但是又為了保證u-boot能夠正常讀寫全局變量,內存,調用各類驅動能力, ?*????所以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)來完成此功能, ?*????后續在分析到時會詳細講解實現方式。 ?*/ ??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運行時相關支持時才會出現使用的段,一般不用關心; ?*/ ????????__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相關功能的支持,一般不會使用; ?*/ ?.__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)所有僅讀數據將會在這個段中對齊排序存放好; ?*/ ?.?=?ALIGN(8); ?.data?:?{?--------------------------------------------------------------------?(10) /* ?*(10)所有數據段將會鏈接到此段中; ?*/ ??*(.data*) ?} ?.?=?ALIGN(8); ?.?=?.; ?.?=?ALIGN(8); ?.u_boot_list?:?{?-------------------------------------------------------------?(11) /* ?*(11)u_boot_list段定義了系統中當前支持的所有命令和設備驅動,此段把散落在各個文件中 ?*?????通過U_BOOT_CMD的一系列拓展宏定義的命令和U_BOOT_DRIVER的拓展宏定義的設備驅動收集到一起, ?*?????并按照名字排序存放,以便后續在命令行快速檢索到命令并執行和檢測注冊的設備和設備樹匹配 ?*???? probe設備驅動初始化;(設備驅動的probe只在定義了dm模塊化驅動時有效) ?*/ ??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運行時是根據定義的基地址開始執行,如果加載地址和鏈接地址 ?*?????不一致則會出現不能執行u-boot的問題。通過一個 ?*?????配置CONFIG_POSITION_INDEPENDENT即可打開地址無關功能, ?*?????此選項會在鏈接u-boot時添加-PIE參數。此參數會在u-boot ELF文件中 ?*?????生成rela*段,u-boot通過讀取此段中表的相對地址值與實際運行時地址值 ?*?????依次遍歷進行修復當前所有需要重定向地址,使其可以實現地址無關運行; ?*?????即無論鏈接基地址如何定義,u-boot也可以在任意ram地址 ?*?????運行(一般需要滿足最低4K或者64K地址對齊); ?*? ?*?????注意此功能只能在sram上實現,因為此功能會在運行時修改文本段數據段中的地址, ?*?????如果此時運行在片上flash,則不能寫flash,導致功能失效無法實現地址無關; ?*/ ?{ ??*(.__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的二進制頭部添加了一些頭部信息, ?*?????包括大小端,數據段文本段大小等,以便于efi相關的加載器讀取信息, ?*?????此頭部信息來自于Linux arm64的Image的頭部信息;該頭部也不屬于u-boot的 ?*?????一部分只是被附加上去的; ?*/ #include?"linux-kernel-image-header-vars.h" #endif }
?
4.2 u-boot-spl.lds
此鏈接腳本是標準的spl鏈接腳本,還包含了u_boot_list段,如果對應自己board不需要命令行或者模塊化驅動設備,只作為一個加載器則可以自定義更簡略的鏈接腳本。
?
/*?SPDX-License-Identifier:?GPL-2.0+?*/ /* ?*?(C)?Copyright?2013 ?*?David?Feng??* ?*?(C)?Copyright?2002 ?*?Gary?Jennejohn,?DENX?Software?Engineering,? ?* ?*?(C)?Copyright?2010 ?*?Texas?Instruments,? ?*?Aneesh?V? ?*/ MEMORY?{?.sram?:?ORIGIN?=?IMAGE_TEXT_BASE,?----------------------------------------?(1) /* ?*(1)>XXX 的形式可以將指定段放入XXX規定的內存中;一般u-boot-spl只有 ?*????很小的可運行內存塊,所以spl中會舍去大量不需要用的段只保留關鍵的 ?*????文本段數據段等,并且通過>.sram的形式將不在ddr初始化前用到的段定義到sdram中, ?*????后續只需在完成ddr初始化后將這些段搬運到ddr中即可,而不需要額外的 ?*????地址修復邏輯,如下:有一個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需要重定向則會使用此段定義,大多數情況下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段用于保存數據段數據, ?*????一些board在初始化時修改data段數據,并在后續某個階段 ?*????從此段中恢復data的原始數據; ?*/ ?.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段數據定義到>.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開始的,并在后續初始化中調用了很多鏈接腳本中定義的地址符號表。
原文:https://blog.csdn.net/maybeYoc/article/details/122937844
審核編輯:劉清
評論
查看更多