16 c linux insmod kernel-module linux-kernel
我在Ubuntu工作.我正在尝试制作两个使用彼此功能的内核模块.我的问题是我正确编译了模块,但是其中一个符号没有得到解决.
为简单起见,我们将这些模块称为m1和m2.
m2是出口功能void func_m2(void).在m1调用此函数.两个模块都正确编译.
在它全部编译之后,我需要先加载m2模块(因为它有导出的func_m2函数)和之后的m1模块.所以,让我们做到:
volodymyr@sv1:~/development/kmodules/m2$ sudo insmod ./m2.ko
Run Code Online (Sandbox Code Playgroud)
现在,让我们m1尝试使用的加载模块func_m2:
volodymyr@sv1:~/development/kmodules/m1$ sudo insmod ./m1.ko
insmod: error inserting './m1.ko': -1 Unknown symbol in module
Run Code Online (Sandbox Code Playgroud)
以下是我在日志中看到的内容:
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
Run Code Online (Sandbox Code Playgroud)
因此,似乎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]
Run Code Online (Sandbox Code Playgroud)
如您所见,func_m2它实际上存在于符号表中.那为什么m1不能装?
我为我的内核和Linux源安装了正确的Linux头文件.我没有对内核进行任何修改,它没有被触及,它的版本是:2.6.31-16-generic(我运行x64)
现在,为了向您展示完整的图片,我在这里放置源代码和Makefile,我用于这个测试m1和m2模块.
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");
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
基本上我的问题是:为什么无法m1加载?
如果有人能回答,那会很有帮助.
构建m2时,它会创建一个Module.symvers文件.
将此文件复制到您正在构建m1的位置.然后制作m1,然后输入.
您之前构建m1时可能会收到警告,例如:
警告:"func_m2"[/tmp/m1/m1.ko]未定义!
一旦您使用Module.symversm2模块,这应该会消失.
来自http://www.kernel.org/doc/Documentation/kbuild/modules.txt:
--- 6.2符号和外部模块
构建外部模块时,构建系统需要访问内核中的符号以检查是否定义了所有外部符号.这是在MODPOST步骤中完成的.modpost通过从内核源代码树中读取Module.symvers来获取符号.如果在构建外部模块的目录中存在Module.symvers文件,则也将读取此文件.在MODPOST步骤中,将编写一个新的Module.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
然后将完成预期并编译两个模块,同时充分了解来自任一模块的符号.
使用额外的Module.symvers文件构建外部模块时,会生成Module.symvers文件,其中包含未在内核中定义的所有导出符号.要从bar.ko访问符号,请将module.symvers文件从bar.ko的编译复制到构建foo.ko的目录.在模块构建期间,kbuild将读取外部模块目录中的Module.symvers文件,并且在构建完成时,将创建一个新的Module.symvers文件,其中包含已定义的所有符号的总和,而不是内核的一部分.
使用"make"变量KBUILD_EXTRA_SYMBOLS如果从另一个模块复制Module.symvers是不切实际的,则可以在构建文件中将空格分隔的文件列表分配给KBUILD_EXTRA_SYMBOLS.这些文件将在其符号表初始化期间由modpost加载.
以下是我在您的代码中发现的一些问题:
(A)。您的初始化和终止函数应声明为静态并正确标识。例如,在 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");
}
Run Code Online (Sandbox Code Playgroud)
对 m2.c 重复此操作
(二)。使用相同的 Makefile 将两个模块一起构建。我敢打赌,如果您仔细查看 m1.c 的现有 Makefile 的输出,您将看到一条警告,表明 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) Module.markers modules.order
Run Code Online (Sandbox Code Playgroud)
构建两个模块后,在“m2.ko”上运行 insmod,然后再为“m1.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
Run Code Online (Sandbox Code Playgroud)
m2_func.h 显示为:
#ifndef M2_FUNC_H
#define M2_FUNC_H
void m2_func(void);
#endif
Run Code Online (Sandbox Code Playgroud)
顶层 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) Module.markers modules.order
Run Code Online (Sandbox Code Playgroud)
位于 mod1/ 中的 Makefile 和 module1.c 显示为:
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) Module.markers modules.order
Run Code Online (Sandbox Code Playgroud)
#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");
Run Code Online (Sandbox Code Playgroud)
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) Module.markers modules.order
Run Code Online (Sandbox Code Playgroud)
#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);
Run Code Online (Sandbox Code Playgroud)
注意:我无法使用你的 makefile,因为它为每个 c 文件生成 *.ko。Makefile 正在完成它的工作。“ko”文件是内核对象文件;每个 .c 源文件都会有一个。没有办法解决这个问题。如果您不需要多个 ko 文件,请将所有代码放入一个源文件中。