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
強制轉化為struct driver類型進行使用。_u_boot_list_2_ driver_2_ usb_generic_hub
如何被定義以及如何初始化為struct driver類型将在宏_u_boot_list_2_ driver_2_ usb_generic_hub
中講解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。

這裡最終目的就是建立了的變量,這個變量的類型為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.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存儲清單如下