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文章
9073浏览量
367235 -
Uboot
+关注
关注
4文章
125浏览量
28204 -
ARMv8
+关注
关注
1文章
35浏览量
14154
发布评论请先 登录
相关推荐
U-boot的基本介绍
从本文开始,将陆续推送“手把手教你移植U-boot”系列文章,目标是由浅入深地讲解U-boot的工作流程、原理、配置方法和移植方法,手把手教你完成U-boot的移植工作,默认硬件开发平台为ARM,操作系统为Linux。
发表于 07-14 16:52
•2853次阅读
我的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
•2061次阅读
u-boot简介
U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目。U-Boot的作用是系统引导。U-Boot从FADSROM、
发表于 10-14 11:17
•3553次阅读
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
•833次阅读
armv8 u-boot的启动介绍
先看arm官网提供的一张图: 上图详细概括了arm官方推荐的armv8的启动层次结构: 官方将启动分为了BL1,BL2,BL31,BL32,BL33阶段,根据顺序,芯片启动后首先执行BL1阶段代码
u-boot在汇编启动阶段的相关操作介绍
boot参数, 进行地址无关fixed,系统寄存器复位,底层平台相关初始化等 ,启动代码位于arch/arm/cpu/armv8/start.S, 入口地址为_start。 启动前为后续流程做的一些平台
评论