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

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.hqwc.cn/news/491406.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux字符设备驱动中同类型多设备节点的创建---一个驱动程序支持多个同类型设备

文章目录 前言1 代码解析1.1 驱动层1.2 应用层 2 运行结果总结 前言 本期分享的内容相对比较简单,那就是同时注册多个同类型的字符设备驱动,那么这样我们就可以同时支持多个同类型的设备了!下面来带大家看一下: 1 代码解析 1.1 …

【Flink精讲】Flink性能调优:CPU核数与并行度

常见问题 举个例子 提交任务命令: bin/flink run \ -t yarn-per-job \ -d \ -p 5 \ 指定并行度 -Dyarn.application.queuetest \ 指定 yarn 队列 -Djobmanager.memory.process.size2048mb \ JM2~4G 足够 -Dtaskmanager.memory.process.size4096mb \ 单个 TM2~8G 足…

MySQL知识点总结(五)——锁

MySQL知识点总结(五)——锁 锁分类表锁 & 行锁如何添加表锁?如何添加行锁? 读锁 & 写锁行锁 & 间隙锁(gap lock)& 临键锁(next-key lock) 加锁机制分析可重复读隔离…

OpenAI视频生成Sora技术简析

基本介绍 Sora是春节期间OpenAI发布的产品,主要是通过文字描述生成视频,通过大规模视频数据训练而成的生成模型,当前还没开放试用。官方发布的技术报告:https://openai.com/research/video-generation-models-as-world-simulators…

趣学贝叶斯定理:贝叶斯定理的先验概率、似然和后验概率(1)

在第7章中,我们讨论了如何利用空间推理去推导贝叶斯定理。现在研究如何将贝叶斯定理当作一种概率工具,对不确定性进行逻辑推理。本章将利用贝叶斯定理来计算和量化在给定数据的情况下,信念有多大的可能性为真。为此,需要使用该定理…

019 Spring Boot+Vue 电影院会员管理系统(源代码+数据库+文档)

部分代码地址: https://github.com/XinChennn/xc019-cinema 一、系统介绍 cinema项目是一套电影院会员管理系统,使用前后端分离架构开发包含管理员、会员管理、会员卡管理、电影票、消费记录、数据统计等模块 二、所用技术 后端技术栈: …

【泰山派RK3566】智能语音助手(一)移植Kaldi语音转文字

文章目录 移植过程硬件资源下载测试 移植过程 参考我的这篇博客 【RV1126】移植kaldi实时语音识别 硬件 资源下载 链接:https://pan.baidu.com/s/1x1udT5eNzzQHoPOTCQ182A?pwdlief 提取码:lief –来自百度网盘超级会员V6的分享 下载的文件里面有一个…

全志H713/H618方案:调焦电机(相励磁法步进电机)的驱动原理、适配方法

一、篇头 全志H713平台,作为FHD投影的低成本入门方案,其公板上也配齐了许多投影使用的模组,本文即介绍投影仪调焦所用的步进电机,此模组的驱动原理、配制方法、调试方法。因为条件限制,本文采用的是H618香橙派Z3平台&…

Escalate_Linux(4)-利用SUDO实现提权

利用SUDO实现提权 利用用户的sudo授权获得root的shell cat /etc/passwd cat /etc/sudoers 命令没有权限 echo "cat /etc/sudoers" >/tmp/ls chmod 755 /tmp/ls export PATH/tmp:$PATH /home/user5/script 想办法更改user1的口令 echo echo "user1:xiao…

sql-labs第46关(order by盲注脚本)

一、环境 网上有自己找 二、解释 order by 注入我们看他的true和false来进行注入出来 二、实操 让我们用sort 看看源码 最终我们的id是放到order by后面了 如果我们直接用列去排序 ?sortusername/password username: password: 可以看到顺序是不…

每日五道java面试题之spring篇(六)

目录: 第一题 ApplicationContext通常的实现是什么?第二题 什么是Spring的依赖注入?第三题 依赖注入的基本原则第四题 依赖注入有什么优势?第五题 有哪些不同类型的依赖注入实现方式? 第一题 ApplicationContext通常的…

Python及Pycharm专业版下载安装教程(Python 3.11版)附JetBrains学生认证教程

目录 一、Python下载及安装1、Python下载2、Python安装3、验证是否安装成功 二、PyCharm下载及安装1、PyCharm下载2、PyCharm安装3、激活PyCharm 三、JetBrains学生认证 本篇主要介绍Python和PyCharm专业版的下载及安装方式,以及通过两种方式进行JetBrains学生认证。…