加载内核模块-Unknown symbol错误分析

Unknown symbol 说明  有些函数不知道(未定义)


在调试过程中,发现一个问题,编译生成一个KO文件,insmod加载后报错:

Unknown symbol var_set_integer (err 0)

Unknown symbol parse_arg_eq (err 0)


问题分析思路:

一、用命令查看内核中是否已有这个内核符号

例如要查看是否有var_set_integer这个内核符号,输入命令:

#cat   /proc/kallsyms  | grep "var_set_integer"

没有相关打印,而输入其它的函数名则会有打印信息。

如果内核中已经包含了这个符号,那么就会有相关的打印信息,否则不打印。

注:

/proc/kallsyms会显示内核中所有的符号,但是这些符号不是都能被其他模块引用的(绝大多数都不能),能被导出的是符号的类型是大写的那些(例如T,U)。


二、使用modinfo查看内核相关信息,确定模块依赖关系,再进一步确认符号调用

例如:

[root@localhost sw_64-3_8]# modinfo linux-bcm-core.ko 
filename:        linux-bcm-core.ko
license:          GPL                                            //权限
description:    BCM Core Device Driver
depends:        linux-kernel-bde                         // 由此可看出linux-bcm-core.ko 依赖于linux-kernel-bde.ko
vermagic:       3.8.0-sw2f SMP mod_unload modversions  //内核版本


三、在源码中搜索这几个函数定义的地方,看是否有使用EXPORT_SYMBOL,是否有extern声明;

       并且查看是否要做GPL声明:修改为 MODULE_LICENSE("GPL");

  (1)如果你的模块需要输出符号给其他模块使用, 应当使用下面的宏定义:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);//只用于包含 GPL 许可权的模块。


符号必须在模块文件的全局部分输出, 在任何函数之外, 因为宏定义扩展成一个特殊用途的并被期望是全局存取的变量的声明. 这个变量存储于模块的一个特殊的可执行部分( 一个 "ELF 段" ), 内核用这个部分在加载时找到模块输出的变量. 


  (2)EXPORT_SYMBOL使用方法
1)在模块函数定义之后使用EXPORT_SYMBOL(函数名);
2)在调用该函数的模块中使用extern对之声明;
3)首先加载定义该函数的模块,再加载调用该函数的模块。【模块加载顺序的前后要求,一般就是依赖于符号调用】

编译生成ko模块之后,用insmod命令加载此模块到内核。这个程序加载模块的代码段和数据段到内核。

接着, 连接模块中任何未解决的符号到内核的符号表上.

也就是说:

【insmod使用公共内核符号表来解析模块中未定义的符号】,公共内核符号表中包含了所有的全局内核项(即函数和变量)的地址,这是实现模块化驱动程序所必需的。

同时也可以【导出自身模块中的任何内核符号到公共内核符号表】,如图:


在通常情况下,模块只需实现自己的功能,而无需导出任何符号。但是,如果其他模块需要从某个模块中获得好处时,我们也可以导出符号。


四、在模块目录下查看Module.symvers,看是否存在要找的符号。

Module.symvers contains a list of all exported symbols from a kernel build.  

Module.symvers包含所有要导出的列表符号。

 Module.symvers file 的语法格式:
      <CRC>         <Symbol>              <module>
   0x2d036834    scsi_remove_host     drivers/scsi/scsi_mod

当内核编译选项CONFIG_MODVERSIONS关闭时,所有的CRC值都为0x00000000。



补充

1,

 Linux模块间通讯方法非常的多,最便捷的方法莫过于函数符号导出,然后直接调用。】

2,

驱动也是存在于内核空间的,它的每一个函数每一个变量都会有对应的符号,这部分符号也可以称作内核符号,
它们不导出(EXPORT_SYMBOL)就只能为自身所用,导出后就可以成为公用,对于导出的那部分内核符号就是我们常说的内核符号表

insmod的时候并不是所有的函数都得到内核符号表去寻找对应的符号,
每一个驱动在自已的分配的空间里也会存在一份符号表,里面有关于这个驱动里使用到的变量以及函数的一些符号,首先驱动会在这里面找,如果发现找不到就会去公共内核符号表中搜索,搜索到了则该模块加载成功,搜索不到则该模块加载失败。

2.6内核默认情况下,是不会在模块加载后把模块中的非静态全局变量以及非静态函数自动导出到内核符号表中的,需要显式调用宏EXPORT_SYMBOL才能导出。
对于一个模块来讲,如果仅依靠自身就可以实现自已的功能,那么可以不需要要导出任何符号,只有其他模块中需要使用到该模块提供的函数时,就必须要进行导出操作。
由此启发,因为LKM中所存取的每一个符号(像函数名)也会被列在内核符号表中,有时候我们可以看内核符表就可以看到LKM调用的那些函数,如果这个LKM为非法目的,那么我们可以杀掉这个LKM.


名词解释:【LKM:可装载模块(Loadable Kernel Module )】


参考:

http://lxr.free-electrons.com/source/Documentation/kbuild/modules.txt

http://blog.csdn.net/macrossdzh/article/details/4601648

http://secisland.blog.51cto.com/787880/319760



转自:http://blog.csdn.net/wuyongpeng0912/article/details/46739233

posted @ 2017-12-10 10:22  隔壁王叔叔a  阅读(4011)  评论(0编辑  收藏  举报