u-boot的linker list源码分析

linker list文件位置

include\linker_lists.h

linker_list文件中的宏

宏名含义
llsym(_type, _name, _list)访问链接器生成的_lsit数组中类型为_type的名称为name的元素
ll_entry_declare(_type, _name, _list)在_list数组中声明一个链接器产生的_type类型命名为name的元素
ll_entry_declare_list(_type, _name, _list)在_list数组中声明一个链接器产生的_type类型命名为name数组的数组
ll_entry_start(_type, _list)声明一个指向_type类型的_list数组中第一个元素的类型为_type指针
ll_entry_end(_type, _list)声明一个指向_type类型的_list数组中最后一个元素的末尾地址的下一个地址的类型为_type指针
ll_entry_count(_type, _list)返回_type类型的_list数组中元素个数
ll_entry_get(_type, _name, _list)获取_type类型_list数组中命名为name的元素

宏llsym

 - #define llsym(_type, _name, _list)  \
 - 	((_type *)&_u_boot_list_2_##_list##_2_##_name)
  • 宏的作用
    用于访问由_u_boot_list_2_##_list##2##_name组成的(_type *)类型变量的访问,其中##_list和##name就是将宏中参数_list和_name连接起来,如llsym(struct driver, usb_generic_hub, driver)就是(struct driver *)&u_boot_list_2 driver_2 usb_generic_hub

  • 参数
    _type:数据类型:如int,long,结构体,函数等等
    _name:数组元素名称
    _list: 数组名称

  • 使用案列
    llsym(struct driver, usb_generic_hub, driver)就等价于(struct driver *)&_u_boot_list_2_ driver_2_ usb_generic_hub,意思是将全局变量_u_boot_list_2_ driver_2_ usb_generic_hub强制转化为struct driver类型进行使用。_u_boot_list_2_ driver_2_ usb_generic_hub如何被定义以及如何初始化为struct driver类型将在宏ll_entry_declare(_type, _name, _list)中讲解

宏ll_entry_declare

 - #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)))
  • 宏的作用
    声明一个_type类型的变量,该变量四字节对齐,并且将该变量放置在_u_boot_list_2_##_list##2##_name段下,使用此宏声明的变量必须在编译时初始化

  • 参数
    _type:数据类型:如int,long,结构体,函数等等
    _name:数组元素名称
    _list: 数组名称
    __aligned(4):变量进行4字节对齐
    attribute:给变量增加属性
    unused:变量的一个属性,如果该变量未使用则不产生警告
    section(“Demo”):变量的一个属性,将变量放到Demo段下

  • 使用注意
    –参数_type不能包含”static”关键字
    –如果声明了一个包含一些数组元素的段,并且声明了该段的一个子段并包含了一些元素,那么这些元素必须是同一类型的。
    –如果声明了一个包含一些数组元素的外部段,并且声明了该段的一个内部子段并包含了一些元素,那么在遍历外部段时,甚至内部段的元素也出现在数组中。

  • 使用案例

1.	struct env_clbk_tbl {
2.		const char *name;		/* Callback name */
3.		int (*callback)(const char *name, const char *value, enum env_op op,
4.				int flags);
5.	};
6.	#define U_BOOT_ENV_CALLBACK(name, callback) \
7.		ll_entry_declare(struct env_clbk_tbl, name, env_clbk) = \
8.		{#name, callback}
9.	#endif

这是一个环境变量在创建、删除或修改时进行一个回调操作,struct env_clbk_tbl就是为环境变量回调操作定义的一个结构体,结构体第一个元素name就是环境变量名称,结构体第二个元素就是一个指向环境变量回调函数的函数指针callback。

U_BOOT_ENV_CALLBACK(name, callback)注册环境变量的回调函数
这里最终目的就是创建了的变量,这个变量的类型为struct env_clbk_tbl类型,变量名为_u_boot_list_2_ env_clbk 2 ipaddr,__aligned(4)让变量进行4字节对齐,也就是说如果整个结构体所占内存没有4字节对齐时进行填充,例如整个结构体占10个字节,那就在结构体后面填充2个空字节,补充到12字节,实现4字节对齐。

attribute((unused, section(.u_boot_list_2_ env_clbk 2 ipaddr)))给这个变量添加了两个属性

  • unused属性
    这个变量如果没有被使用,编译器不会报“变量从未使用“的警告
  • section属性
    section(.u_boot_list_2_ env_clbk_2_ipaddr)属性:将这个变量放置到.u_boot_list_2_env_clbk_2_ipaddr字段下,与.text段或.data等其他段独立出来,从uboot.map文件中可以查看
    在这里插入图片描述

可见在.u_boot_list_2_env_clbk_2_ipaddr字段下保存有一个变量_u_boot_list_2_env_clbk_2_ipaddr

由此可见通过宏U_BOOT_ENV_CALLBACK(name, callback)就可以创建一个struct env_clbk_tbl 类型,命名为_u_boot_list_2_env_clbk_2_##_name的变量,name就是环境变量名,而且根据struct env_clbk_tbl 类型,此环境变量还做了初始化,

  _u_boot_list_2_env_clbk_2_##_name->name=name
   _u_boot_list_2_env_clbk_2_##_name->callback=callback

因此环境变量name在创建,删除或修改时就可以使用_u_boot_list_2_env_clbk_2_##_name->callback函数做回调操作。

宏ll_entry_declare_list

 - #define ll_entry_declare_list(_type, _name, _list)			\
 - 	_type _u_boot_list_2_##_list##_2_##_name[] __aligned(4)		\
 - 			__attribute__((unused,				\
 - 			section(".u_boot_list_2_"#_list"_2_"#_name)))

与ll_entry_declare功能类似,只是用于声明一个变量数组,这个宏直接参考ll_entry_declare,只是它在定义时为数组,调用时直接为
u_boot_list_2##_list##2##_name[0]
u_boot_list_2##_list##2##_name[1]

ll宏_entry_start和ll_entry_end

为了计算数组中元素的个数定义了ll_entry_start和ll_entry_end两个宏,在link_list数据结构中段的命名都是以.u_boot_list_2_开始,.u_boot_list_2_"#_list"_2_"#_name中_list为数组项名称,_name数组项下的元素名称,下面整理了u_boot部分_list,_name

.u_boot_list_2_"#_list"_2_"#_name

在这里插入图片描述在这里插入图片描述
在u-boot.lds中.u_boot_list_*段进行排序
在这里插入图片描述
因此以.u_boot_list_开头的段都会按照字符顺序进行排序,如_u_boot_list_3*_u_boot_list_2*_u_boot_list_1*,则链接器链接的时候就以_u_boot_list_1*_u_boot_list_2*_u_boot_list_3*的顺序进行链接。

所以在所有_u_boot_list_2*段前面插入一个_u_boot_list_1段,在_u_boot_list_2*段后面插入一个_u_boot_list_3段,这两个段不分配任何内存,让_u_boot_list_1指向_u_boot_list_2*开始的起始地址,让_u_boot_list_3指向最后_u_boot_list_2*最后一个地址的下一个地址,这样{(_u_boot_list_3)-(_u_boot_list_1)=(_u_boot_list_2*所占内存大小)},在这里使用一个空数组start[0]进行定位,因为数组长度为0,所以不分配内存,但是它指向的地址时当前所在位置,他赋予属性,将他放在_u_boot_list_1段,这样start[0]就可以指向下一个段的起始地址,下个段为_u_boot_list_2*

同理在.u_boot_list_2*末尾插入.u_boot_list_3段,使用一个空数组end[0],并且将它放到.u_boot_list_3段,这样end[0]就指向.u_boot_list_3后面的第一个地址。如果start和end强制为char型指针,那(end-start)就是_u_boot_list_2*所占内存大小,如果时_type类型,那么(_u_boot_list_2*所占内存大小)=sizeof(_type)*(start-end)

同理也可以计算_u_boot_list_2_##_list##_2*的内存大小或数组元素个数,为什么将这个_list列表称之为数组,是因为在ll_entry_declare宏定义时就要求相同的_list下的元素的数据类型必须一致,例如_list名为cmd的数据类型就为struct cmd_tbl_s结构体,driver的数据类型为struct driver结构体,因为连接脚本中对.u_boot_list_*段进行了排序,例如所有的会.u_boot_list_2_cmd_2*段会排放在一起,自然而然这些段中的变_u_boot_list_2_ cmd _2_*就会被放在连续的地址空间,数据类型又是相同的,所以和数组的属性一致,因此将之称之为数组。同上在.u_boot_list_2_cmd_2*段的起始地址插一个.u_boot_list_2_cmd_1段,并且存放一个空数组start[0],让指向_u_boot_list_2_ cmd _2_*数组列第一个变量,在_u_boot_list_2_ cmd _2_*数组列结束后插入一个.u_boot_list_2_cmd_3段,存放一个空数组end[0],end指向_u_boot_list_2_ cmd _2_*数组列结束后的第一个地址,然后将end和start强制转化为struct cmd_tbl_s结构体指针,这样(end-start)=(_u_boot_list_2_ cmd _2_*)数组元素的个数

在map文件中可查看:
在这里插入图片描述
可见.u_boot_list_2_cmd_1的地址和.u_boot_list_2_cmd_2_adc段的地址相同,而且每个段都按.u_boot_list_2_cmd_2_*都按照顺序排列,变量_u_boot_list_2_cmd_2_*地址是连续的,每个变量_u_boot_list_2_cmd_2_*大小都是一致的。

在末尾.u_boot_list_2_cmd_3段指向下一个list变了的首地址,下一个是driver的list,同样在driver list首地址插入.u_boot_list_2_driver_1段,指向driver list 段的第一个变量_u_boot_list_2_driver_2_clk_fixed_factor,其他list参考cmd list。
根据u-boot.map文件整理的list存储列表如下
在这里插入图片描述

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
U-Boot 2015 是一个开的引导加载程序(bootloader),用于嵌入式设备的启动过程管理。在适应 DDR2(Double Data Rate 2) 内存之前,我们需要了解 U-Boot 的基本架构以及 DDR2 内存的特点。 U-Boot 的基本架构包括启动文件(boot files),链接脚本(linker script),设备树(device tree)和配置文件(configuration file)。在适配 DDR2 之前,我们需要确保 U-Boot 的配置文件中包含了正确的 DDR2 内存控制器的参数设置。这些参数包括 DDR2 内存的时序、速度等。 首先,我们需要在 U-Boot 的配置文件中定义 DDR2 控制器的基本参数,例如内存时钟频率、等待时间等。接下来,我们还需要指定 DDR2 内存的芯片配置,例如芯片容量、地址映射等。这些参数的正确设置是确保 DDR2 内存能够正常工作的关键。 然后,我们需要修改链接脚本,确保 U-Boot 在编译链接过程中正确指定 DDR2 内存的起始地址和大小。这样,U-Boot 才能正确加载并管理 DDR2 内存的数据。 最后,我们还需要创建适当的设备树文件,该文件用于描述嵌入式设备的硬件组件和连接。在设备树文件中,我们需要添加 DDR2 内存控制器的相关节点,并在节点中指定 DDR2 内存的属性和配置。 总结来说,适配 DDR2 内存需要在 U-Boot 的配置文件中正确设置 DDR2 内存控制器的参数,修改链接脚本以适配 DDR2 内存的起始地址和大小,以及创建适当的设备树文件来描述 DDR2 内存的属性和配置。通过这些步骤,我们可以确保 U-Boot 2015 正确适配 DDR2 内存,并能够成功启动嵌入式设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值