对于在另一个模块中定义的符号,insmod会失败,并显示“模块中的未知符号”

我在Ubuntu工作。 我正在试图使两个使用其他function的内核模块。 我的问题是,我得到了模块正确编译,但符号不解决其中之一。

为了简单起见,我们将这些模块称为m1m2

m2是导出函数void func_m2(void)m1正在调用这个函数。 两个模块都正确编译。

在编译完成之后,我需要首先加载m2模块(因为它已经导出了func_m2函数),之后又func_m2m1模块。 所以,让我们做到这一点:

 volodymyr@sv1:~/development/kmodules/m2$ sudo insmod ./m2.ko 

现在,让我们加载正在尝试使用func_m2 m1模块:

 volodymyr@sv1:~/development/kmodules/m1$ sudo insmod ./m1.ko insmod: error inserting './m1.ko': -1 Unknown symbol in module 

以下是我在日志中看到的内容:

 volodymyr@sv1:~/development/kmodules/m1$ dmesg | tail [ 3938.166616] Loading m2 module ... [ 3963.078055] m1: no symbol version for func_m2 [ 3963.078059] m1: Unknown symbol func_m2 

所以,似乎对符号func_m2的引用没有解决。 有趣。 让我们来看看它是否出现在符号表中:

 volodymyr@sv1:~/development/kmodules$ cat /proc/kallsyms | grep 'func_m2' ffffffffa00530d0 r __ksymtab_func_m2 [m2] ffffffffa00530e8 r __kstrtab_func_m2 [m2] ffffffffa00530e0 r __kcrctab_func_m2 [m2] ffffffffa0053000 T func_m2 [m2] 000000004edd543f a __crc_func_m2 [m2] 

正如你所看到的, func_m2实际上存在于符号表中。 那么为什么m1不能被加载?

我为我的内核和Linux源码安装了正确的Linux头文件。 我没有在内核做任何修改,它是未触及的,它的版本是:2.6.31-16-generic(我运行x64)

现在,为了向您展示完整的图片,我将这里的源代码和Makefile用于m1m2模块的testing。

m1模块:

m1.c:

 #include <linux/module.h> #include <linux/kernel.h> extern void func_m2(void); int hello_start(void) { printk(KERN_INFO "Loading m1 module ...\n"); func_m2(); return 0; } void hello_end(void) { printk(KERN_INFO "Unloading m1 ...\n"); } module_init(hello_start); module_exit(hello_end); MODULE_LICENSE("GPL"); 

Makefile文件:

 obj-m := m1.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 

m2模块:

m2.c:

 #include <linux/module.h> #include <linux/kernel.h> int hello_start(void) { printk(KERN_INFO "Loading m2 module ...\n"); return 0; } void hello_end(void) { printk(KERN_INFO "Unloading m2 ...\n"); } void func_m2(void) { printk(KERN_INFO "This a function in m2\n"); } module_init(hello_start); module_exit(hello_end); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(func_m2); 

Makefile文件:

 obj-m := m2.o export-objs := m2.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 

基本上我的问题是: 为什么不能加载m1

如果有人能回答,这将是有益的。

Solutions Collecting From Web of "对于在另一个模块中定义的符号,insmod会失败,并显示“模块中的未知符号”"

以下是我在代码中发现的一些问题:

(一个)。 你的初始化和终止函数应该被声明为静态的并且被正确地识别 例如,在m1.c中 –

 static int __init hello_start(void) { printk(KERN_INFO "Loading m1 module ...\n"); func_m2(); return 0; } static void __exit hello_end(void) { printk(KERN_INFO "Unloading m1 ...\n"); } 

重复这个m2.c

(b)中。 使用相同的Makefile将两个模块构建在一起。 我敢打赌,如果仔细观察现有的Makefile for m1.c的输出,您将看到一个警告,指出func_m2()未定义。 无论如何,整合的Makefile应该看起来像 –

 SRCS = m1.c m2.c OBJS = $(SRCS:.c=.o) obj-m += $(OBJS) EXTRA_CFLAGS = -O2 all: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules clean: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean $(RM) modulee.markers modules.order 

在构建这两个模块之后,在为“m1.ko”发出insmod之前,在“m2.ko”上运行insmod。 通过dmesg检查结果。

另外,在这里我假设m1.c和m2.c都在同一个目录下。 即使他们在不同的目录,这种技术将工作,但将是混乱。 如果他们在不同的目录中,请执行以下操作。

我做了很少的研究,并找到了在不同的目录中构建模块的方法。 我使用的例子比你所使用的例子要简单得多,但也许它是适应性的。

我有一个名为ExportSymbol的目录中的文件清单

 $ ls -CFR .: include/ Makefile mod1/ mod2/ ./include: m2_func.h ./mod1: Makefile module1.c ./mod2: Makefile module2.c 

m2_func.h显示为:

 #ifndef M2_FUNC_H #define M2_FUNC_H void m2_func(void); #endif 

顶级Makefile显示为:

 obj-y := mod1/ mod2/ all: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules clean: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean $(RM) modulee.markers modules.order 

Makefile和module1.c位于mod1 /,显示为:

 SRCS = module1.c OBJS = $(SRCS:.c=.o) obj-m += $(OBJS) EXTRA_CFLAGS += -I${PWD}/include all: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules clean: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean $(RM) modulee.markers modules.order 

 #include <linux/module.h> #include <linux/kernel.h> static int __init hello_start(void) { printk(KERN_INFO "Loading m1 module ...\n"); m2_func(); return 0; } static void __exit hello_end(void) { printk(KERN_INFO "Unloading m1 ...\n"); } module_init(hello_start); module_exit(hello_end); MODULE_LICENSE("GPL"); 

在mod2 /中的Makefile和module2.c显示为:

 SRCS = module2.c OBJS = $(SRCS:.c=.o) obj-m += $(OBJS) EXTRA_CFLAGS += -I${PWD}/include all: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules clean: $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean $(RM) modulee.markers modules.order 

 #include "m2_func.h" #include <linux/module.h> #include <linux/kernel.h> static int __init hello_start(void) { printk(KERN_INFO "Loading m2 module ...\n"); return 0; } static void __exit hello_end(void) { printk(KERN_INFO "Unloading m2 ...\n"); } void m2_func(void) { printk(KERN_INFO "This a function in m2\n"); } module_init(hello_start); module_exit(hello_end); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(m2_func); 

注:我不能使用您的生成文件,因为它生成每个c文件* .ko。 Makefile正在做它的工作。 'ko'文件是一个内核对象文件; 每个.c源文件都会有一个。 这是没有办法的。 如果你不想要多个ko文件,那么把你所有的代码放在一个源文件中。

当你建立m2时,它会创建一个modulee.symvers文件。

将此文件复制到您正在构建m1的位置。 然后让m1,并insmod它。

您之前可能会在建造m1时发出警告,例如:

警告:“func_m2”[/tmp/m1/m1.ko] undefined!

一旦你使用m2模块中的modulee.symvers ,这应该会消失。

http://www.kernel.org/doc/Documentation/kbuild/modules.txt

— 6.2符号和外部模块

构建外部模块时,构建系统需要访问内核中的符号来检查是否定义了所有的外部符号。 这在MODPOST步骤完成。 modpost通过从内核源码树中读取modulee.symvers来获得符号。 如果在构建外部模块的目录中存在一个modulee.symvers文件,则该文件也将被读取。 在MODPOST步骤中,将会写入一个新的modulee.symvers文件,其中包含内核中未定义的所有导出符号。

这也是值得一读的,从同一个文件:

— 6.3来自另一个外部模块的符号

有时,外部模块使用另一个外部模块的导出符号。 kbuild需要对所有符号有完整的了解,以避免吐出有关未定义符号的警告。 存在这种情况的三种解决方案。

注:推荐使用顶层kbuild文件的方法,但在某些情况下可能不切实际。

使用顶层的kbuild文件如果你有两个模块,foo.ko和bar.ko,其中foo.ko需要bar.ko中的符号,你可以使用一个公共的顶层kbuild文件,这样两个模块都编译成相同的建立。 考虑以下目录布局:

./foo/ <=包含foo.ko ./bar/ <=包含bar.ko

顶层的kbuild文件将如下所示:

$ ./Kbuild(或./Makefile):obj-y:= foo / bar /

并执行

$ make -C $ KDIR M = $ PWD

然后将做预期的并且编译两个模块完全知道符号从任一个模块。

使用额外的modulee.symvers文件建立外部模块时,会生成一个modulee.symvers文件,其中包含所有未在内核中定义的导出符号。 要访问bar.ko中的符号,请将bar.ko编译的modulee.symvers文件复制到foo.ko所在的目录。 在模块构建过程中,kbuild将读取外部模块目录中的modulee.symvers文件,并在构建完成时创建一个新的modulee.symvers文件,其中包含所有已定义符号的总和,而不是内核的一部分。

使用“make”变量KBUILD_EXTRA_SYMBOLS如果从另一个模块复制modulee.symvers是不切实际的,可以在构建文件中将空格分隔的文件列表分配给KBUILD_EXTRA_SYMBOLS。 这些文件将在其符号表初始化期间由modpost加载。