链接脚本之软件初始化解耦

看过Linux内核源码的人都知道内核中到处充斥着类似: arch_initcall(),fs_initcall(), module_init()等等, 也都知道它们的作用主要
是将各个不同模块的初始化函数的地址放到指定的段,然后方便内核在同一个地方集中初始化,同时解决了调用者和被调函数
之间的耦合。唯一不太方便的地方就是每新定义一个段就需要在链接脚本中同步加入类似下列描述代码:

__xxx_section_start = .;  // 表示段的起始地址
*(.xxx)
__xxx_section_end = .;    // 表示段的结束地址
那有没有什么简单点的方法尽量少改甚至不改链接脚本呢?  答案是肯定的。 请看如下代码:
#define __RO_SECTION_DECLARE(name, order, index_1, index_2, sym) \
__section(".rodata1.symbol." #name "." #order "." #index_1 "." #index_2 "." #sym) \
__used __aligned(4)

struct sysinit_item {
	void (*handler)(void);
}

#define _SYSINIT_INDEX_ITEM(sname, handler, nsec, index_1, index_2) \
__RO_SECTION_DECLARE(sname, nsec, index_1, index_2, handler) \
  static const struct sysinit_item __##sname##_item_##nsec = {handler}
  
#define SYS_INIT(handler, module, order) \
enum { _Sysinit_##handler = order*module }; \
_SYSINIT_INDEX_ITEM(sysinit, handler, 1, module, order)

#define SYSINIT_BOUNDARY(_name) \
_SYSINIT_INDEX_ITEM(_name, NULL, 0, 0, 0); \
_SYSINIT_INDEX_ITEM(_name, NULL, 2, 0, 0)

#define FOREACH_ITEM(_var, _name) \
for (_var = &__##_name##_item_##0, ++_var; \
	 _var < &__##_name##_item_##2; \
	 _var++)
	 
调用指定段的所有函数:
	 SYSINIT_BOUNDARY(sysinit);
	 
	 void sys_init(void) {
	 	const struct sysinit_item *item;
		FOREACH_ITEM(item, sysinit)
			item->handler();
	}
	
使用例程:
	#define SYSINIT_DEVICE  0x10
	#define DEVICE_ORDER   0x03
	static void xxx_device_setup(void)
	{
		//TODO
	}
	SYS_INIT(xxx_device_setup, SYSINIT_DEVICE,  DEVICE_ORDER);


	上面的实现代码中有两个关键宏:
#define SYS_INIT(handler, module, order) \
	enum { _Sysinit_##handler = order*module }; \
	_SYSINIT_INDEX_ITEM(sysinit, handler, 1, module, order)
	
这里的枚举类型 enum { _Sysinit_##handler = order*module }; 的主要作用并不是定义一个类型,而是为了强制触发编译器的预处理程序的替换操
作,即:如果module和order是宏则将他们替换成数字。

#define SYSINIT_BOUNDARY(_name) \
	_SYSINIT_INDEX_ITEM(_name, NULL, 0, 0, 0); \
	_SYSINIT_INDEX_ITEM(_name, NULL, 2, 0, 0)
	
	这个宏的作用主要用于定义段的起始地址和结束地址。让我们来看下,如果将这个宏展开应该是这样的,以SYSINIT_BOUNDARY(sysinit)为例:
	__section(".rodata1.symbol." sysinit "."0 "." 0 "." 0 "." "NULL") __used __aligned(4)\
	static const struct sysinit_item __sysinit_item_0 = {NULL} ; \
	__section(".rodata1.symbol." #name "."2 "." 0 "." 0 "." "NULL") __used __aligned(4) \
	static const struct sysinit_item __sysinit_item_2  = {NULL} 
	
	那这又代表什么意思呢? 聪明的你们可能早已看破了一切, 但我还是啰嗦一遍,SYSINIT_BOUNDARY(sysinit)的含义如下:
	定义并将变量__sysinit_item_0(初始值为NULL)放在段 .rodata1.symbol.sysinit .0 .0 .0 .NULL
	定义并将变量__sysinit_item_2(初始值为NULL) 放在段 .rodata1.symbol.sysinit .2 .0 .0 .NULL
	
	同理: SYS_INIT(xxx_device_setup, SYSINIT_DEVICE,  DEVICE_ORDER);表示:
	定义并将变量__sysinit_item_1(初始值为xxx_device_setup)放在段 .rodata1.symbol.sysinit .1 .0x10 .0x03.xxx_device_setup
	
	再利用链接器对段的排序功能(GNU-LINKER:  SORT(.xxx*)),最终生成的可执行文件的内存映像应该如下:
	0xxxxxxx0: .rodata1.symbol.sysinit .0 .0 .0 .NULL
	0xxxxxxx4: .rodata1.symbol.sysinit .1 .0x10 .0x03.xxx_device_setup
	0xxxxxxx8: .rodata1.symbol.sysinit .2 .0 .0 .NULL
	
	再看sys_init()的实现, 主要就是遍历段(&__sysinit_item_0, &__sysinit_item_2), 不包含__sysinit_item_0和__sysinit_item_2,因为这两个变量为
	空且主要作用是标识段的边界。 另外之所以选择选择段.rodata1*主要是为了利用C/C++的基本段.rodata*, 如果链接器默认对段rodata进行排序,
	则我们完全不用更改链接脚本,反之则需要在脚本中加入排序指令。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值