天天看点

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的linker list源码分析

这里最终目的就是创建了的变量,这个变量的类型为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的linker list源码分析

可见在.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_"#_nam

e中_list为数组项名称,_name数组项下的元素名称,下面整理了u_boot部分_list,_name

.u_boot_list_2_"#_list"_2_"#_name
           
u-boot的linker list源码分析
u-boot的linker list源码分析

在u-boot.lds中.u_boot_list_*段进行排序

u-boot的linker 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的linker list源码分析

可见.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存储列表如下

u-boot的linker list源码分析