由module_init()宏想到的

2019-07-13 06:32发布

 
一,前言
    在开发嵌入式Linux驱动程序时,我们通常使用module_init及module_exit宏来指定一个驱动程序的入口函数和出口函数。如果驱动模块被编译并链接至内核中,内核启动的过程中会自动加载它。通过对这两个宏定义的分析,可以写出一个简化的间接地调用函数的例子,其基本的方法是通过GNU GCC支持的__attribute__扩展功能将与入口函数地址链接至ELF文件中的特定的段区,并使用链接脚本导出该段区的地址,这样就可以做到间接调用函数了。这可以视为使用C语言开发软件,模块化的一个高级方法。
 
二,GNU GCC的链接脚本
    GNU binutils的文档中指出,链接器ld中内嵌了一个默认的链接脚本,使用--version参数可以输出该脚本,我们把它保存到internal-ldscript.ld:
 
    在修改这个链接脚本之前,我们要确定如何修改它;而在此之前,来让我们先编写一个用于测试的C代码。
 
三,使用__attribute__将函数地址信息指定到特定的段区
    编写的测试代码如下:
 
    从图中可以看到,在main函数中没有直接地调用simple_add和simple_mul这两个函数,而是访问了两个外部的结构体符号,即mfunc_info_start和mfunc_info_end,请注意这两个是“符号”,而不是指针(指针是变量,它们两个不是变量);同时,包含simple_add和simple_mul的函数地址和名称的结构体被指定到了.data.mfunc段区。

 
四,修改链接脚本
    上图C代码中引用的两个符号mfunc_info_start和mfunc_info_end需要在链接脚本中导出,修改后的链接脚本为modified.ld,改动如下:
 
    至此,接下来我们就可以来编译了。
 
五,编译并测试
    编译时需要指定链接脚本:
 
    从图中可以看到,这样间接地调用函数是可行的。当然,Linux驱动开发使用的宏比这个要复杂得多,还要解决一些模块加载次序、依赖关系等,这就需要更深入的探究了。