首页 > 其他分享 >Uboot-3链接脚本lds分析

Uboot-3链接脚本lds分析

时间:2023-12-15 10:46:11浏览次数:39  
标签:.__ end ALIGN boot bss lds start Uboot 链接

1 u-boot.lds解读(armv8)

文件位于u-boot-2021.10\arch\arm\cpu\armv8\u-boot.lds。分析过程已在lds内部注释了.

/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * (C) Copyright 2013
 * David Feng <[email protected]>
 *
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, <[email protected]>
 */

#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将启动初始化分为了两个部分,重定向前初始化board_f和
 *    重定向后初始化  board_r,在重定向之前完成一些必要初始化,
 *    包括可能的ddr初始化,然后通过__image_copy_start和__image_copy_end
 *    将u-boot搬运到ddr中,并在ddr中进行重定向后初始化。
  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
}

2 u-boot-spl.ldc解读(armv8)

文件位于u-boot-2021.10\arch\arm\cpu\armv8\u-boot-spl.lds。一般u-boot-spl只有很小的可运行内存块,所以spl中会舍去大量不需要用的段只保留关键的文本段数据段等,并且通过>.sram的形式将不在ddr初始化前用到的段定义到sdram中。

/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * (C) Copyright 2013
 * David Feng <[email protected]>
 *
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, <[email protected]>
 *
 * (C) Copyright 2010
 * Texas Instruments, <www.ti.com>
 *	Aneesh V <[email protected]>
 */

MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE,
		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)
SECTIONS
{
	.text : {
		. = ALIGN(8);
		*(.__image_copy_start)
		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
	.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

	.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*) }
}

链接脚本的开头定义了两段内存空间,分别定义了sram和sdram的起始地址和长度。在i.MX8中,include/config/imx8mp_evk.h, 这两段定义对应于CPU内部的sram和外部的ddr。
image

这里定义了spl-uboot两段空间,一段是从0x920000开始的152K空间,这段空间是内部RAM中的一段。
而0x96e000开始的8K空间则是用来存放未初始化的全局变量和未初始化的静态局部变量的BSS数据段,位于外部存储即SDRAM如DDR上,如下图:
image
bss段存放的是未初始化的全局变量和局部静态变量,.bss不占据实际的文件大小,只在段表中记录大小,在符号表中记录符号。当文件加载运行时,才分配空间以及初始化。所以实际.bss段只会在运行时才分配空间,分配的空间起始地址也就是从.sdram定义的空间里面。此外,一般重定向也是将boot拷贝搬移到外部sdram中去运行,而对于cpu来说这里bss指定的地址本身就已经在sdram中了。这样也说明了,对于 cpu来说,要使用bss的数据,需要将外部sdram初始化后才能使用。s3c2440裸机-清bss原理及实现

3 u-boot.lds (armv7)

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
 . = 0x00000000;
 . = ALIGN(4);
 .text :
 {
  *(.__image_copy_start)
  *(.vectors)
  arch/arm/cpu/armv7/start.o (.text*)
  *(.text*)
 }
 . = ALIGN(4);
 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
 . = ALIGN(4);
 .data : {
  *(.data*)
 }
 . = ALIGN(4);
 . = .;
 . = ALIGN(4);
 .u_boot_list : {
  KEEP(*(SORT(.u_boot_list*)));
 }
 . = ALIGN(4);
 .image_copy_end :
 {
  *(.__image_copy_end)
 }
 .rel_dyn_start :
 {
  *(.__rel_dyn_start)
 }
 .rel.dyn : {
  *(.rel*)
 }
 .rel_dyn_end :
 {
  *(.__rel_dyn_end)
 }
 .end :
 {
  *(.__end)
 }
 _image_binary_end = .;
 . = ALIGN(4096);
 .mmutable : {
  *(.mmutable)
 }
 .bss_start __rel_dyn_start (OVERLAY) : {
  KEEP(*(.__bss_start));
  __bss_base = .;
 }
 .bss __bss_base (OVERLAY) : {
  *(.bss*)
   . = ALIGN(4);
   __bss_limit = .;
 }
 .bss_end __bss_limit (OVERLAY) : {
  KEEP(*(.__bss_end));
 }
 .dynsym _image_binary_end : { *(.dynsym) }
 .dynbss : { *(.dynbss) }
 .dynstr : { *(.dynstr*) }
 .dynamic : { *(.dynamic*) }
 .plt : { *(.plt*) }
 .interp : { *(.interp*) }
 .gnu.hash : { *(.gnu.hash) }
 .gnu : { *(.gnu*) }
 .ARM.exidx : { *(.ARM.exidx*) }
 .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}

_start 在文件 arch/arm/lib/vectors.S 中有定义,表示代码执行入口,也就是第一条指令要放的位置。注意armv7的入口在vectors.S(armv8在start.s),中断向量表放在指令入口最开始的位置:可以看到—_start后面就是中断向量表,从图中的“.section ".vectors", "ax”可以得到,此代码存放在.vectors 段里面。

image
打开u-boot.map如下图:因此代码段的排列顺序为:先放中断向量表,也就是vectors.s,然后再放start.s相关内容,最后放其他的.text段(一大堆built-in.o)。
image
image
注意这里为什么uboot.map中_start入口地址为什么是0x8780,0000。 链接脚本指定了程序的运行(链接)地址:
程序链接时会指定程序的运行(链接)地址:

arm-linux-gnueabihf-ld.bfd   -pie  --gc-sections -Bstatic -Ttext 0x87800000 -o u-boot -T u-boot.lds arch/arm/cpu/armv7/start.o
--start-group  arch/arm/cpu/built-in.o
arch/arm/cpu/armv7/built-in.o
arch/arm/imx-common/built-in.o
arch/arm/lib/built-in.o
board/freescale/common/built-in.o
......
-L /media/cvitek/robin.lee/my_test/study/openedv/toolchain/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4
-lgcc -Map u-boot.map

运行地址0x87800000定义在:

include/configs/mx6_common.h:86:#define CONFIG_SYS_TEXT_BASE    0x87800000

标签:.__,end,ALIGN,boot,bss,lds,start,Uboot,链接
From: https://www.cnblogs.com/fuzidage/p/17901516.html

相关文章

  • 04_uboot全面讲解
    04_uboot全面讲解本课程希望达到目标理解底层代码的编写方式u-boot版本选择uboot官方源码地址https://ftp.denx.de/pub/u-boot/Uboot版本一直在迭代,加入的东西也越来越多,所以我们学习,只要选择适合的版本就可以了这里我们先选择下载2010.12版本的解压下来之后,进入\arch\a......
  • navicat链接oracle时报错,检查是否是oci.dll库不匹配的问题
     1:安装Oracle数据库,安装时类型选择共享服务器,不要选专享服务器。2:确定Oracle,Navicat,OracleClient的位数,确保你的oracle数据库的位数与navicat位数一致,即:32v32,64v643:http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html,在这个页面下载和......
  • UBUNTU 18.04.6 的Quartus里面转换sof到rbf文件在uboot阶段加载时出错或者在kernel启
    参考Intel的SD卡image设计的教程(https://rocketboards.org/foswiki/Documentation/EmbeddedLinuxBeginnerSGuide) 确认DE10-Nano的MSEL设置为01010,插上SD卡 给DE10-Nano上电,发现可以启动,但卡死在这里不动了: 如果只测试Preloader和uboot的时候也有这个错误: ......
  • Navicat16.1链接SQL server失败
    问题:[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并目未指定默认驱动程序(0) 解决办法:找到本地Navicat安装目录,搜索*.msi,双击进行安装(无脑安装)。安装成功后再去Navicat测试链接,应该就可以了。 ......
  • 浏览器中导出导入证书,https网站报非安全链接
    1、查看证书信息2、点击“网站非安全链接”3、点击“证书信息”,然后导出。4、保存证书至文件夹5、打卡Internet选项,找到内容-证书6、找到“受信任的根证书颁发机构”点击导入。7、找到导出的证书文件,导入即可。......
  • uboot移植及图形化配置
    一、编译通过官方的uboot,修改uboot,参考文档一些配置文件,lcd、网络配置二、图形化配置界面1.指定默认配置文件makemx6ull_alientek_emmc_defconfig2.打开图形化配置界面makemenuconfig3.完成配置后编辑完成之后要保存好当前的配置文件如./configs/test_defconfigsave ......
  • 短链接技术探究与应用
    一、引言在互联网世界中,URL(统一资源定位符)是网络资源的标识,但由于URL的长度限制和不易记忆等问题,短链接应运而生。短链接是一种将长URL转换为短地址的技术,不仅提高了用户体验,还为网站运营、数据分析等提供了便利。本文将对短链接技术进行深入探讨,分析其原理、实现方法及应用场......
  • 知识点链接
    在科研中时常会遇到新的知识点与代码书写问题,把网上参考的信息进行汇总,以备不时之需。R语言经典包的cheatsheet:https://rstudio.github.io/cheatsheets/contributed-cheatsheets.html中国官方最新地图的下载:https://www.jianshu.com/p/74e1475884afR语言当中的数据结构与算法:ht......
  • 设备链接 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/driver-api/device_link.html默认情况下,驱动核心仅强制执行设备之间的依赖关系,这些依赖关系源自设备层次结构中的父/子关系:在挂起、恢复或关闭系统时,设备的顺序是基于这种关系的,即子设备总是在其父设备之前挂起,父设备总是在其子设备之前......
  • 【C语言】编译和链接
    1、翻译环境和运行环境在ANSIC的任何⼀种实现中,存在两个不同的环境。第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它⽤于实际执行代码。2、翻译环境那翻译环境是怎么将源代码转换为可执⾏的机器指令的呢?这里我们就得展开开讲解⼀下翻译环境所做......