unknown symbol in module, or unknown parameter

Linux中我们加载驱动时,经常使用insmod指令来加载驱动ko文件,不过有时加载时,会弹出一些错误提示,例如我想导入pppoe的ko文件,使用insmod pppoe.ko,报错如下

insmod: can't insert 'pppoe.ko': unknown symbol in module, or unknown parameter

这时一般是插入的ko文件,依赖的一部分函数,内核的符号集中不存在, 所以加载ko的时候报了异常。

此时可以采用使用 dmesg 查看内核的环形缓冲区中的报错信息,看看详细的错误提示。

dmesg | tail -n 20

错误提示如下:

...
[ 4599.864471] pppoe: Unknown symbol pppox_ioctl (err 0)
[ 4599.869983] pppoe: Unknown symbol ppp_input (err 0)
[ 4599.876623] pppoe: Unknown symbol unregister_pppox_proto (err 0)
[ 4599.883344] pppoe: Unknown symbol register_pppox_proto (err 0)
[ 4599.889546] pppoe: Unknown symbol pppox_unbind_sock (err 0)
[ 4599.896494] pppoe: Unknown symbol ppp_register_net_channel (err 0)

剩下的只要根据提示的错误函数,搜索一下看看属于那个ko就好,在导入这个ko之前导入依赖的ko文件。

下面是看到的别人的以篇分析记录一下:

----------------------------------------------------------------------------------------------------------------------------------------------------

问题分析思路:

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

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

cat   /proc/kallsyms  | grep "var_set_integer"

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

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


二、使用modinfo查看内核相关信息

使用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  //内核版本

三、在源码中搜索报错函数定义的地方

在导入的ko源代码中,看是否有出错函数 使用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 的语法格式:
                             
   0x2d036834    scsi_remove_host     drivers/scsi/scsi_mod

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

补充:

  1.  Linux模块间通讯方法非常的多,最便捷的方法莫过于函数符号导出,然后直接调用。
  2. 驱动也是存在于内核空间的,它的每一个函数每一个变量都会有对应的符号,这部分符号也可以称作内核符号,它们不导出(EXPORT_SYMBOL)就只能为自身所用,导出后就可以成为公用,对于导出的那部分内核符号就是我们常说的内核符号表。insmod的时候并不是所有的函数都得到内核符号表去寻找对应的符号,每一个驱动在自已的分配的空间里也会存在一份符号表,里面有关于这个驱动里使用到的变量以及函数的一些符号,首先驱动会在这里面找,如果发现找不到就会去公共内核符号表中搜索,搜索到了则该模块加载成功,搜索不到则该模块加载失败。2.6内核默认情况下,是不会在模块加载后把模块中的非静态全局变量以及非静态函数自动导出到内核符号表中的,需要显式调用宏EXPORT_SYMBOL才能导出。对于一个模块来讲,如果仅依靠自身就可以实现自已的功能,那么可以不需要要导出任何符号,只有其他模块中需要使用到该模块提供的函数时,就必须要进行导出操作。由此启发,因为LKM【可装载模块(Loadable Kernel Module)】中所存取的每一个符号(像函数名)也会被列在内核符号表中,有时候我们可以看内核符表就可以看到LKM调用的那些函数,如果这个LKM为非法目的,那么我们可以杀掉这个LKM.

参考:

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

你可能感兴趣的:(Linux)