天天看點

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源碼分析