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",
* 架構是"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 < 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規定的內存中;一般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開始的 ,并在后續初始化中調用了很多鏈接腳本中定義的地址符號表。
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
ARM
+關注
關注
134文章
9084瀏覽量
367386 -
Uboot
+關注
關注
4文章
125瀏覽量
28214 -
ARMv8
+關注
關注
1文章
35瀏覽量
14158
發布評論請先 登錄
相關推薦
U-boot的基本介紹
從本文開始,將陸續推送“手把手教你移植U-boot”系列文章,目標是由淺入深地講解U-boot的工作流程、原理、配置方法和移植方法,手把手教你完成U-boot的移植工作,默認硬件開發平臺為ARM,操作系統為Linux。
發表于 07-14 16:52
?2867次閱讀
我的U-Boot鏈接腳本筆記
以下是我學習u-boot的鏈接腳本時做的筆記,歡迎指正錯誤。/* 指明輸出的可執行文件格式為elf,即小端模式的32位ARM指令 */OUTPUT_FORMAT("
發表于 07-22 10:04
SDK下使用make u-boot編譯NXP官方下載的u-boot編譯不成功怎么辦?
.imgMKIMAGE u-boot-dtb.imgCATu-boot-dtb.binCOPY u-boot.binLDu-boot.elfCCspl/common/spl/spl.oCCspl/arch/arm/cpu/armv8
發表于 12-31 06:24
基于armv8架構對u-boot進行啟動流程分析(一)
,比如mkimage的實現代碼在此處;4 u-boot armv8鏈接腳本在進行源碼分析之前,首先看看u-boot的
發表于 05-23 15:59
基于armv8架構對u-boot進行啟動流程分析(二)
為全局可見并在鏈接腳本中被聲明為入口地址,表示u-boot的入口地址為_start;(2)首先進入入口后有兩種可配置情況,一種就是定義了LINUX_KERNEL_IMAGE_HEADER
發表于 05-23 16:05
Porting U-Boot to the Control
In this paper, the way of porting U-Boot to Control Computer Based MPC8349 will beintroduced
發表于 01-25 15:45
?13次下載
u-boot的Makefile分析
u-boot的Makefile分析
U-BOOT是一個LINUX下的工程,在編譯之前必須已經安裝對應體系結構的交叉編譯環境,這里只針對ARM,編譯器系列軟件為arm-linux-*。
U-BOOT的下載
發表于 05-17 09:16
?2064次閱讀
u-boot簡介
U-Boot,全稱 Universal Boot Loader,是遵循GPL條款的開放源碼項目。U-Boot的作用是系統引導。U-Boot從FADSROM、
發表于 10-14 11:17
?3560次閱讀
u-boot中Hush shell的功能及編寫腳本的方法介紹
了解u-boot中Hush shell的功能,以及如何為其編寫腳本。
說明了存儲和檢索腳本的方法。
U-Boot架構淺析
導讀:嵌入式Linux系統搭建,bootloader是必不可少的一環,而U-Boot已成嵌入式Linux事實標準。所以較為深入的分析U-Boot的設計,對于更...
發表于 02-07 11:56
?7次下載
基于armv8架構來對u-boot進行啟動流程分析
首先引用wiki上的簡介:u-boot 是一個主要用于嵌入式系統的引導加載程序,可以支持多種不同的計算機系統結構。
發表于 06-09 09:39
?846次閱讀
armv8 u-boot的啟動介紹
先看arm官網提供的一張圖: 上圖詳細概括了arm官方推薦的armv8的啟動層次結構: 官方將啟動分為了BL1,BL2,BL31,BL32,BL33階段,根據順序,芯片啟動后首先執行BL1階段代碼
u-boot在匯編啟動階段的相關操作介紹
boot參數, 進行地址無關fixed,系統寄存器復位,底層平臺相關初始化等 ,啟動代碼位于arch/arm/cpu/armv8/start.S, 入口地址為_start。 啟動前為后續流程做的一些平臺
評論