转自:
https://mp.weixin.qq.com/s?__biz=MzAwMDUwNDgxOA==&mid=2652663356&idx=1&sn=779762953029c0e0946c22ef2bb0b754&chksm=810f28a1b678a1b747520ba3ee47c9ed2e8ccb89ac27075e2d069237c13974aa43537bff4fba&mpshare=1&scene=1&srcid=0111Ys4k5rkBto22dLokVT5A&pass_ticket=bGNWMdGEbb0307Tm%2Ba%2FzAKZjWKsImCYqUlDUYPZYkLgU061qPsHFESXlJj%2Fyx3VM#rd
本文详细讲解了利用__attribute__((section()))构建初始化函数表,以及Linux内核各级初始化的原理。
作者简介:
廖威雄,2016年本科毕业于暨南大学,目前就职于珠海全志科技股份有限公司从事linux嵌入式系统(Tina Linux)的开发,主要负责文件系统和存储的开发和维护,兼顾linux测试系统的设计和持续集成的维护。
拆书帮珠海百岛分舵的组织长老,二级拆书家,热爱学习,热爱分享。
欢迎投稿:
2018年给Linuxer投稿原创Linux技术文章,一经录取,赠送人民邮电出版社任意在售图书,获得读者红包打赏,和公众号站长宋宝华200元微信红包。
Linuxer-"Linux开发者自己的媒体"第五月稿件和赠书名单
欢迎关注Linuxer
问题导入
传统的应用编写时,每添加一个模块,都需要在main中添加新模块的初始化
使用__attribute__((section()))构建初始化函数表后,由模块告知main:“我要初始化“,添加新模块再也不需要在main代码中显式调用模块初始化接口。
以此实现main与模块之间的隔离,main不再关心有什么模块,模块的删减也不需要修改main。
那么,如何实现这个功能呢?如何实现DECLARE_INIT呢?联想到内核驱动,所有内核驱动的初始化函数表在哪里?为什么添加一个内核驱动不需要修改初始化函数表?
下文会从 构建初始化函数表的原理分析、分析内核module_init实现、演练练习 的3个角度给小伙伴分享。
构建初始化函数表的原理分析 __attribute__((section(”name“)))是gcc编译器支持的一个编译特性(arm编译器也支持此特性),实现在编译时把某个函数/数据放到name的数据段中。因此实现原理就很简单了: 1. 模块通过__attribute__((section("name")))的实现,在编译时把初始化的接口放到name数据段中 2. main在执行初始化时并不需要知道有什么模块需要初始化,只需要把name数据段中的所有初始化接口执行一遍即可 首先: gcc -c test.c -o test.o 此时编译过程中处理了__atribute__((section(XXX))),把标记的变量/函数放到了test.o的XXX的数据段,可用 readelf命令查询。 最后:ld -Ttest.o -otest.bin 链接时,test.o的XXX数据段(输入段),最终保存在test.bin的XXX数据段(输出段),如此在bin中构建了初始化函数表。 由于自定义了一个数据段,而默认链接脚本缺少自定义的数据段的声明,因此并不能使用默认的链接脚本。 ld链接命令有两个关键的选项: ld -T