【嵌入式移植】7、U-Boot源码分析4—链接脚本分析

news2025/1/18 4:46:57

U-Boot源码分析4—链接脚本分析

  • 1 u-boot-spl.lds
    • 1.1 链接脚本的生成
    • 1.2 u-boot-spl.lds内容分析
    • 1.3 text - 程序代码段
    • 1.4 `sram`其它段定义
      • 1.4.1 `.rodata`只读数据段
      • 1.4.2 `.data`数据段
      • 1.4.3 `.u_boot_list`段
    • 1.5 `BSS`段
    • 1.6 `/DISCARD/`

从上一篇文章【嵌入式移植】6、U-Boot源码分析3—make可以知道U-Boot编译过程最后按照spl/u-boot-spl.lds链接脚本进行链接,因此启动过程分析首先分析此链接脚本

1 u-boot-spl.lds

1.1 链接脚本的生成

scripts/Makefile.spl第105行~第129行可知,
请添加图片描述
由于$(srctree)/board/$(BOARDDIR)/u-boot-spl.lds./board/sunxi/u-boot-spl.lds不存在,因此继续往下执行,LDSCRIPT变量的值为$(srctree)/$(CPUDIR)/u-boot-spl.lds./arch/arm/cpu/armv8/u-boot-spl.lds

然后在第370行~371行,调用if_changed_dep函数,根据./arch/arm/cpu/armv8/u-boot-spl.lds的内容生成spl/u-boot-spl.lds,因此可以看到此两文件基本一致(宏定义的值也转换过来了)

1.2 u-boot-spl.lds内容分析

这里对./arch/arm/cpu/armv8/u-boot-spl.lds进行分析

第15行~18行定义了2段内存空间
请添加图片描述
其中CONFIG_SPL_TEXT_BASECONFIG_SPL_MAX_SIZECONFIG_SPL_BSS_START_ADDRCONFIG_SPL_BSS_MAX_SIZE均在include/configs/sunxi-common.h中定义(值与spl/u-boot-spl.lds中的值一致)

#define CONFIG_SPL_TEXT_BASE		0x10060		/* sram start+header */
#define CONFIG_SPL_MAX_SIZE		    0x7fa0		/* 32 KiB */
#define CONFIG_SPL_BSS_START_ADDR	0x4ff80000
#define CONFIG_SPL_BSS_MAX_SIZE		0x00080000 /* 512 KiB */

其中TEXT为程序代码段,BSSBlock Started by Symbol的简称,通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序运行初始前会清0

第20行~22行指定输出格式,以及入口地址,这里为小端aarch64,入口地址为_start
请添加图片描述
接下来是各段的定义,SECTIONS关键字表示后续将描述输出文件的内存布局,一般包含textrodatadatabss4种类型的段空间

1.3 text - 程序代码段

第25行~30行,定义了程序代码段
请添加图片描述
第30行的>.sram表示将这一段放进上面定义的sram对应的地址中,即放入0x10060位置

起始的. = ALIGN(8);表示首地址8字节对齐:其中.为定位计数器,表示当前地址,这里为0x10060ALIGN(8)表示插入填充字节,直到当前位置在 8 字节边界上对齐。这里首地址0x10060已经为8字节对齐

随后存放.__image_copy_start段,这里.__image_copy_startarch/arm/lib/section.c文件中定义:

char __image_copy_start[0] __attribute__((section(".__image_copy_start")));

即定义了一个长度为0(不占存储空间)的字符数组,并通过attribute属性声明,通过属性section规定放在.__image_copy_start段中;

对应的还有第47行~50行中的.__image_copy_end
请添加图片描述

char __image_copy_end[0] __attribute__((section(".__image_copy_end")));

在链接脚本中,.__image_copy_start放在代码段的前面,.__image_copy_end放在.u_boot_list段后面,由于其为0长度数组,不占用内存空间,因此仅表示当前的sram地址,作为U-Boot拷贝自身代码的起始地址和结束地址

接下来第28行将arch/arm/cpu/armv8/start.S中的代码段单独拿出来,保证start.S文件编译后的代码放在最终生成的u-boot-spl文件的最前面

第29行为所有其它文件编译的代码段

u-boot-spl,map文件中可查看编译生成的目标文件的内存分布,最开始是内存定义,可见定义了sramsdram两段内存空间对应的起始地址和长度;随后为各段的定义,对于代码段,从0x10060地址开始,8字节对齐,同时.__image_copy_start符号对应的地址也为0x10060,即U-Boot拷贝自身代码的起始地址;随后是 arch/arm/cpu/armv8/start.o的代码段;最后是各个.o文件的代码段,与链接脚本定义的一致
请添加图片描述

1.4 sram其它段定义

第32行~57行sram中其它段的定义
请添加图片描述

1.4.1 .rodata只读数据段

.rodata为只读数据段,通常保存一些常量值等;这里同样首地址8字节对齐,然后通过SORT_BY_ALIGNMENT,按照对齐需求以降序的方式排列在输出文件u-boot-spl中,即大的对齐放在小的对齐前面,可以减少为了对齐需要的额外空间;通过SORT_BY_NAME按照名字上升顺序排列在输出文件中;且这里是先按对齐方式排,再按名字排

1.4.2 .data数据段

.data为数据段,主要用于存放全局已初始化的数据段和已初始化的局部静态变量

1.4.3 .u_boot_list

.u_boot_list段用于存放所有的U-Boot命令,所有通过attribute属性声明放置在.u_boot_list*的U-Boot命令都存放在此段,且通过SORT关键字按照名称递增排序;通过KEEP关键字保留所有.u_boot_list段的内容,即使这一段中的符号在程序中未被直接引用

通过查找.u_boot_list关键字,发现在include/linker_lists.h中有相关内容,结合u-boot-spl,map文件中相关内容,大多为.u_boot_list_2_xxx_1.u_boot_list_2_xxx_2_xx.u_boot_list_2_xxx_3的形式
请添加图片描述
因此.u_boot_list段来源与include/linker_lists.h中的ll_entry_declarell_entry_startll_entry_end有关:

#define ll_entry_declare(_type, _name, _list)				\
	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))

#define ll_entry_start(_type, _list)					\
({									\
	static char start[0] __aligned(4) __attribute__((unused,	\
		section(".u_boot_list_2_"#_list"_1")));			\
	(_type *)&start;						\
})

#define ll_entry_end(_type, _list)					\
({									\
	static char end[0] __aligned(4) __attribute__((unused,		\
		section(".u_boot_list_2_"#_list"_3")));			\
	(_type *)&end;							\
})

ll_entry_declare为例搜索
请添加图片描述
cmd/help.c中的内容为例
请添加图片描述
可知此.c文件中定义了一个do_help函数,并通过ll_entry_start.u_boot_list_2_cmd_1的地址赋值给start变量,这里.u_boot_list_2_cmd_1正好在u-boot-spl.map第2260行,这里顺便提一下,从2258行的u_boot_list_2_blk_driver_3到2278行的.u_boot_list_2_spl_image_loader_2_spl_mmc_load_image0BOOT_DEVICE_MMC1,其对应的地址均为0x0000000000017708,这是因为程序中未直接引用这些符号,但通过KEEP关键字仍进行保留。

最后调用_do_help函数真正实现其功能,_do_help函数位于common/command.c文件中,事实上所有U-Boot命令均在此文件中定义

后续通过U_BOOT_CMD宏定义

#define U_BOOT_CMD(_name,_maxargs,_rep,_cmd,_usage,_help) U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
Expands to:

cmd_tbl_t _u_boot_list_2_cmd_2_help __aligned(4) __attribute__((unused, section(".u_boot_list_2_""cmd""_2_""help"))) = { "help", 16, 1, do_help, "print command description/usage", "\n" "	- print brief description of all commands\n" "help command ...\n" "	- print detailed usage of 'command'", ((void *)0), };

即定义了一个cmd_tbl_t结构体,并将其存放在.u_boot_list_2_cmd_2_help段中

其它相关的宏定义位于include/command.h文件中:

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\
	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
	ll_entry_declare(cmd_tbl_t, _name, cmd) =			\
		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
						_usage, _help, _comp);

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
				_usage, _help, _comp)			\
		{ #_name, _maxargs, _rep, _cmd, _usage,			\
			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }

1.5 BSS

最后则是将.bss段规划到外部存储sdram中,并同样定义了两个空字符数组变量__bss_start__bss_end指定其起始和结束地址
请添加图片描述
但这里.bss段无需搬运,因为.bss段存放的是未初始化的全局变量和局部静态变量,不占据实际的文件大小,只在段表中记录大小,在符号表中记录符号。当文件加载运行时,才分配空间以及初始化。所以实际.bss段只会在运行时才分配空间,分配的空间起始地址也就是从.sdram定义的空间里面。

此外,一般重定向也是将boot启动代码拷贝搬移到外部sdram中去运行,这里bss指定的地址本身就已经在sdram中了,因此要使用bss的数据,需要将外部sdram初始化后才能使用

1.6 /DISCARD/

/DISCARD/关键字用于指定后续的段不会出现在输出文件中。

本章分析完毕~
完结撒花✿✿ヽ(°▽°)ノ✿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1470287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

130 如何通过vs2017开发linux c++程序

使用VS2017开发linux下的应用程序(C/C)_vc_linux.exe vs2017-CSDN博客 参考上面这哥们的,写的很详细 前言 本文章记录如何使用VS2017进行linux应用程序的开发(针对新手小白),VS2017能较为方便的通过SSH编辑…

电脑蓝牙在哪里打开?1分钟轻松打开蓝牙!

“我在操作电脑的时候想将电脑的蓝牙打开来连接音响和键盘,但是不知道电脑蓝牙应该如何打开,有什么比较简单的方法吗?” 随着无线技术的日益发展,蓝牙已成为连接各种设备的重要桥梁。无论是传输文件、音频还是与外部设备进行通信&…

YOLO学习中的琐碎知识点

目录 一、导入的库 二、名词介绍 (1)pytorch张量 (2)边界框(bounding box) 三、pycharm操作 (1)参数设置 四、文件认识 五、YOLO如何训练自己的模型 一、导入的库 import to…

笔记本Win 10系统查看电池健康状况

博主最近换了个笔记本电池,之前的电池容量明显变小了很多,而且出现了轻微鼓包的情况。所以用gpt问了一下怎么用系统的方法查看电池情况。 在Windows 10系统中,您可以通过以下步骤来查看笔记本电脑电池的健康状况: 打开命令提示符&…

神经网络系列---归一化

文章目录 归一化批量归一化预测阶段 测试阶段γ和β(注意)举例 层归一化前向传播反向传播 归一化 批量归一化 (Batch Normalization)在训练过程中的数学公式可以概括如下: 给定一个小批量数据 B { x 1 , x 2 , … …

线程普通任务执行流程

(1)先判断是否存在空闲线程,存在直接分配,不存在执行(2); (2)判断工作线程数量小于核心数量,未超出创建核心线程执行线程任务,超出执行&#xff…

为什么会员模式是一种明智的扩张方式

会员模式看起来是一种有趣、令人兴奋且很酷的业务发展方式,但当您真正深入研究时,您可能会惊讶地发现它远不止于此。 会员资格为我们提供了一条道德扩展的途径。我们可以就地为客户提供服务。 这就是为什么会员模式可能成为您企业的下一步,…

LeetCode 热题 100 | 二叉树(一)

目录 1 基础知识 1.1 先序遍历 1.2 中序遍历 1.3 后序遍历 2 94. 二叉树的中序遍历 3 104. 二叉树的最大深度 4 226. 翻转二叉树 5 101. 对称二叉树 菜鸟做题,语言是 C 1 基础知识 二叉树常见的遍历方式有: 先序遍历中序遍历后序遍历…

LeetCode | 寻找两个正序数组的中位数 Python C语言

Problem: 4. 寻找两个正序数组的中位数 文章目录 思路解题方法Code结果结果一些思考 思路 先合并,后排序,最后找中间轴。 解题方法 由解题思路可知 Code 这是python3的代码。 class Solution:def findMedianSortedArrays(self, nums1: List[int], …

微服务知识02

1、九大高并发解决方案 2、系统架构图​​​​​​​ 3、分布式事务 本地事务、分布式事务 操作不同服务器的数据库(垂直分库) 4、分布式事务解决方案(没有seata之前) (1)XA协议(强一致性&a…

STM32单片机基本原理与应用(八)

温度传感器实验 实验内容: 单片机通过代码模拟1-Wire总线并对DS18B20进行读写,并在TFTLCD屏幕上显示当前实时温度。 电路原理图: 1-Wire总线 1-Wire总线:即单总线协议,采用单根信号线,既传输时钟&#…

深度学习基础(一)神经网络基本原理

之前的章节我们初步介绍了机器学习相关基础知识,目录如下: 机器学习基础(一)理解机器学习的本质-CSDN博客 机器学习基础(二)监督与非监督学习-CSDN博客 机器学习基础(四)非监督学…

python jupyter notebook打开页面方便使用

如果没安装jupyter, 请安装: pip install jupyter notebook 运行jupyter notebook jupyter-notebook

【SpringBoot】Spring常用注解总结

目录 ⭐spring springmvc和springboot的区别 Autowired 和Resource的区别和联系 1. SpringBootApplication 2. Spring Bean 相关 2.1. Autowired 2.2. Component,Repository,Service, Controller 2.3. RestController 2.4. Scope 2.5. Configuration 3. 处理常见的 HT…

利用R语言进行聚类分析实战(数据+代码+可视化+详细分析)

🍉CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一|统计学|干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项,参与研究经费10w、40w级横向 文…

红队攻防之powershell上线基础免杀(一)

不努力,你背井离乡干嘛?当卧底啊 环境为win10,在哥斯拉生成的webshell下,执行powershell命令。 测试杀毒软件为:火绒,腾讯电脑管家 哥斯拉生成php文件的webshell 如图 哥斯拉进行连接 把要执行命令的文件…

[树形DP] 树的最大独立集

题目 这个挺简单的&#xff0c;注意状态转移时&#xff0c;如果选这个点&#xff0c;那么它的子结点状态应该为不选&#xff0c;如果这个点的状态是不选&#xff0c;那么可以在它的子结点里选择&#xff1a;选/不选两个状态&#xff0c;所以最后结果是max挑选。 #include<b…

业务流程管理系统(BPMS):一文掌握,组织业务流程优化必备。

大家好&#xff0c;我是大美B端工场&#xff0c;本期继续分享商业智能信息系统的设计&#xff0c;欢迎大家关注&#xff0c;如有B端写系统界面的设计和前端需求&#xff0c;可以联络我们。 一、什么是BPMS系统 BPMS是Business Process Management System&#xff08;业务流程管…

Java面试:Spring Cloud Alibaba

文章目录 引言I Spring Cloud Alibaba1.1 配置文件加载的优先级(由高到低)1.2 注册中心1.3 rpcII 高并发场景:缓存穿透/缓存失效/雪崩如何解决2.1 缓存穿透2.2 缓存击穿(失效)2.3 缓存雪崩引言 微服务涉及的中间件分布式事务事务的传播方式事务的隔离级别缓存穿透/缓存失效…

基于springboot+vue的校园社团信息管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…