u-boot armv8鏈接腳本
在進(jìn)行源碼分析之前,首先看看u-boot的鏈接腳本,通過(guò)鏈接腳本可以從整體了解一個(gè)u-boot的組成,并且可以在啟動(dòng)分析中知道某些邏輯是在完成什么工作。
在armv8中,u-boot使用arch/arm/cpu/armv8/u-boot.lds進(jìn)行鏈接。
u-boot-spl和u-boot-tpl使用arch/arm/cpu/armv8/u-boot-spl.lds進(jìn)行鏈接,因?yàn)槊總€(gè)board的情況可能不同,所以u(píng)-boot可以通過(guò)Kconfig來(lái)自定義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)首先定義了二進(jìn)制程序的輸出格式為"elf64-littleaarch64",
* 架構(gòu)是"aarch64",程序入口為"_start"符號(hào);
*/
SECTIONS
{
#ifdef CONFIG_ARMV8_SECURE_BASE -------------------------------------------------- (2)
/*
*(2)ARMV8_SECURE_BASE是u-boot對(duì)PSCI的支持,在定義時(shí)可以將PSCI的文本段,
* 數(shù)據(jù)段,堆棧段重定向到指定的內(nèi)存,而不是內(nèi)嵌到u-boot中。
* 不過(guò)一般廠商實(shí)現(xiàn)會(huì)使用atf方式使其與bootloader分離,這個(gè)功能不常用;
*/
/DISCARD/ : { *(.rela._secure*) }
#endif
. = 0x00000000; -------------------------------------------------------------- (3)
/*
*(3)定義了程序鏈接的基地址,默認(rèn)是0,通過(guò)配置CONFIG_SYS_TEXT_BASE可修改
* 這個(gè)默認(rèn)值。
*/
. = ALIGN(8);
.text :
{
*(.__image_copy_start) --------------------------------------------------- (4)
/*
*(4)__image_copy_start和__image_copy_end用于定義需要重定向的段,
* u-boot是一個(gè)分為重定向前初始化和重定向后初始化的bootloader,
* 所以此處會(huì)定義在完成重定向前初始化后需要搬運(yùn)到ddr中數(shù)據(jù)的起始地址和結(jié)束地址;
*
* 大多數(shù)時(shí)候u-boot是運(yùn)行在受限的sram或者只讀的flash上,
* u-boot為了啟動(dòng)流程統(tǒng)一會(huì)在ddr未初始化和重定位之前不去訪問(wèn)全局變量,
* 但是又為了保證u-boot能夠正常讀寫(xiě)全局變量,內(nèi)存,調(diào)用各類(lèi)驅(qū)動(dòng)能力,
* 所以u(píng)-boot將啟動(dòng)初始化分為了兩個(gè)部分,重定向前初始化board_f和
* 重定向后初始化 board_r,在重定向之前完成一些必要初始化,
* 包括可能的ddr初始化,然后通過(guò)__image_copy_start和__image_copy_end
* 將u-boot搬運(yùn)到ddr中,并在ddr中進(jìn)行重定向后初始化,這個(gè)時(shí)候的u-boot就可以
* 正常訪問(wèn)全局變量等信息了。
*
* 如果想要在board_f過(guò)程中讀寫(xiě)一些全局變量信息該怎么辦呢?
* u-boot通過(guò)定義global_data(gd)來(lái)完成此功能,
* 后續(xù)在分析到時(shí)會(huì)詳細(xì)講解實(shí)現(xiàn)方式。
*/
CPUDIR/start.o (.text*) -------------------------------------------------- (5)
/*
*(5)定義了鏈接程序的頭部文本段,armv8就是
* arch/arm/cpu/armv8/start.S,
* start.S中所有文本段將會(huì)鏈接到此段中并且段入口符號(hào)就是_start;
*/
}
/* This needs to come before *(.text*) */
.efi_runtime : { ------------------------------------------------------------ (6)
/*
*(6)在定義了efi運(yùn)行時(shí)相關(guān)支持時(shí)才會(huì)出現(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,其他的所有文本段將會(huì)鏈接到此段中;
*/
{
*(.text*)
}
#ifdef CONFIG_ARMV8_PSCI -------------------------------------------------------- (8)
/*
*(8)同(2),是PSCI相關(guān)功能的支持,一般不會(huì)使用;
*/
.__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ù)將會(huì)在這個(gè)段中對(duì)齊排序存放好;
*/
. = ALIGN(8);
.data : { -------------------------------------------------------------------- (10)
/*
*(10)所有數(shù)據(jù)段將會(huì)鏈接到此段中;
*/
*(.data*)
}
. = ALIGN(8);
. = .;
. = ALIGN(8);
.u_boot_list : { ------------------------------------------------------------- (11)
/*
*(11)u_boot_list段定義了系統(tǒng)中當(dāng)前支持的所有命令和設(shè)備驅(qū)動(dòng),此段把散落在各個(gè)文件中
* 通過(guò)U_BOOT_CMD的一系列拓展宏定義的命令和U_BOOT_DRIVER的拓展宏定義的設(shè)備驅(qū)動(dòng)收集到一起,
* 并按照名字排序存放,以便后續(xù)在命令行快速檢索到命令并執(zhí)行和檢測(cè)注冊(cè)的設(shè)備和設(shè)備樹(shù)匹配
* probe設(shè)備驅(qū)動(dòng)初始化;(設(shè)備驅(qū)動(dòng)的probe只在定義了dm模塊化驅(qū)動(dòng)時(shí)有效)
*/
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運(yùn)行時(shí)是根據(jù)定義的基地址開(kāi)始執(zhí)行,如果加載地址和鏈接地址
* 不一致則會(huì)出現(xiàn)不能執(zhí)行u-boot的問(wèn)題。通過(guò)一個(gè)
* 配置CONFIG_POSITION_INDEPENDENT即可打開(kāi)地址無(wú)關(guān)功能,
* 此選項(xiàng)會(huì)在鏈接u-boot時(shí)添加-PIE參數(shù)。此參數(shù)會(huì)在u-boot ELF文件中
* 生成rela*段,u-boot通過(guò)讀取此段中表的相對(duì)地址值與實(shí)際運(yùn)行時(shí)地址值
* 依次遍歷進(jìn)行修復(fù)當(dāng)前所有需要重定向地址,使其可以實(shí)現(xiàn)地址無(wú)關(guān)運(yùn)行;
* 即無(wú)論鏈接基地址如何定義,u-boot也可以在任意ram地址
* 運(yùn)行(一般需要滿(mǎn)足最低4K或者64K地址對(duì)齊);
*
* 注意此功能只能在sram上實(shí)現(xiàn),因?yàn)榇斯δ軙?huì)在運(yùn)行時(shí)修改文本段數(shù)據(jù)段中的地址,
* 如果此時(shí)運(yùn)行在片上flash,則不能寫(xiě)flash,導(dǎo)致功能失效無(wú)法實(shí)現(xiàn)地址無(wú)關(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)一些在鏈接時(shí)無(wú)用需要丟棄的段;
*/
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
#ifdef CONFIG_LINUX_KERNEL_IMAGE_HEADER ----------------------------------- (15)
/*
*(15)在efi加載時(shí)會(huì)很有用,主要在u-boot的二進(jìn)制頭部添加了一些頭部信息,
* 包括大小端,數(shù)據(jù)段文本段大小等,以便于efi相關(guān)的加載器讀取信息,
* 此頭部信息來(lái)自于Linux arm64的Image的頭部信息;該頭部也不屬于u-boot的
* 一部分只是被附加上去的;
*/
#include "linux-kernel-image-header-vars.h"
#endif
}
4.2 u-boot-spl.lds
此鏈接腳本是標(biāo)準(zhǔn)的spl鏈接腳本,還包含了u_boot_list段,如果對(duì)應(yīng)自己board不需要命令行或者模塊化驅(qū)動(dòng)設(shè)備,只作為一個(gè)加載器則可以自定義更簡(jiǎn)略的鏈接腳本。
/* 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只有
* 很小的可運(yùn)行內(nèi)存塊,所以spl中會(huì)舍去大量不需要用的段只保留關(guān)鍵的
* 文本段數(shù)據(jù)段等,并且通過(guò) >.sram的形式將不在ddr初始化前用到的段定義到sdram中,
* 后續(xù)只需在完成ddr初始化后將這些段搬運(yùn)到ddr中即可,而不需要額外的
* 地址修復(fù)邏輯,如下:有一個(gè)sram 0x18000-0x19000,
* 一個(gè)sdram 0x80000000 - 0x90000000,
* 那么通過(guò) >.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需要重定向則會(huì)使用此段定義,大多數(shù)情況下spl中會(huì)用上重定向;
*/
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在初始化時(shí)修改data段數(shù)據(jù),并在后續(xù)某個(gè)階段
* 從此段中恢復(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后直接對(duì)此段地址清零
* 即可使用全局未初始化變量,并且不會(huì)帶來(lái)副作用。
*/
.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的啟動(dòng)是從arch/arm/cpu/armv8/start.S中的_start開(kāi)始的 ,并在后續(xù)初始化中調(diào)用了很多鏈接腳本中定義的地址符號(hào)表。
-
ARM
+關(guān)注
關(guān)注
134文章
9312瀏覽量
375163 -
Uboot
+關(guān)注
關(guān)注
4文章
128瀏覽量
28895 -
ARMv8
+關(guān)注
關(guān)注
1文章
35瀏覽量
14413
發(fā)布評(píng)論請(qǐng)先 登錄
U-boot的基本介紹

我的U-Boot鏈接腳本筆記
SDK下使用make u-boot編譯NXP官方下載的u-boot編譯不成功怎么辦?
基于armv8架構(gòu)對(duì)u-boot進(jìn)行啟動(dòng)流程分析(一)
基于armv8架構(gòu)對(duì)u-boot進(jìn)行啟動(dòng)流程分析(二)
Porting U-Boot to the Control
u-boot的Makefile分析
u-boot簡(jiǎn)介
u-boot中Hush shell的功能及編寫(xiě)腳本的方法介紹
U-Boot架構(gòu)淺析

Linux U-Boot開(kāi)發(fā)指南

基于armv8架構(gòu)來(lái)對(duì)u-boot進(jìn)行啟動(dòng)流程分析

armv8 u-boot的啟動(dòng)介紹

評(píng)論