一,前言
在开发嵌入式Linux驱动程序时,我们通常使用module_init及module_exit宏来指定一个驱动程序的入口函数和出口函数。如果驱动模块被编译并链接至内核中,内核启动的过程中会自动加载它。通过对这两个宏定义的分析,可以写出一个简化的间接地调用函数的例子,其基本的方法是通过GNU GCC支持的__attribute__扩展功能将与入口函数地址链接至ELF文件中的特定的段区,并使用链接脚本导出该段区的地址,这样就可以做到间接调用函数了。这可以视为使用C语言开发软件,模块化的一个高级方法。
二,GNU GCC的链接脚本
GNU binutils的文档中指出,链接器ld中内嵌了一个默认的链接脚本,使用--verbose(或者执行ld --verbose)参数可以输出该脚本,我们把它保存到internal-ldscript.ld:
![](https://i-blog.csdnimg.cn/blog_migrate/7a8f176600d1d4037aa7a8d931e4131d.png)
在修改这个链接脚本之前,我们要确定如何修改它;而在此之前,来让我们先编写一个用于测试的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,改动如下:
![](https://i-blog.csdnimg.cn/blog_migrate/79e31e9fe8b431906658567e29c72ee9.png)
至此,接下来我们就可以来编译了。
五,编译并测试
编译时需要指定链接脚本:
![](https://i-blog.csdnimg.cn/blog_migrate/4668ff52251c3a15c29f19dbec3f2ec2.png)
从图中可以看到,这样间接地调用函数是可行的。当然,Linux驱动开发使用的宏比这个要复杂得多,还要解决一些模块加载次序、依赖关系等,这就需要更深入的探究了。