单独编译内核模块

最近在学习ipvs的内核源码,便想对源码加入一些注释,来帮助理解。
由于编译整个内核的时候过长,故研究了一番怎么编译单个内核模块。

1 编译一个最简单的内核模块

参考:
https://blog.csdn.net/zhangyifei216/article/details/49703435

/*helloworld.c*/
#include <linux/module.h>  //所有模块都必须包含的头文件
#include <linux/kernel.h> //一些宏定义,例如这里的KERN_INFO

int init_module(void)
{
        printk(KERN_INFO "Hello world 1.\n");
        /*
         *       * 返回非0表示模块初始化失败,无法载入
         *            */
        return 0;
}

void cleanup_module(void)
{
        printk(KERN_INFO "Goodbye world 1.\n");
}

Makefile

obj-m += helloworld.o
all:
      make -C /usr/src/linux-4.9.1 M=$(PWD) modules
clean:
     make -C /usr/src/linux-4.9.1 M=$(PWD) clean

/usr/src/linux-4.9.1是下载的内核源码。编译内核模块前,需要进行一些准备工作:
进入内核源码根目录下执行这些命令:

  1. cp /boot/config-4.9.0-7-686 .config
  2. make oldconfig
  3. make prepare
  4. cd scripts;make recordmcount
  5. 回到内核源码根目录,然后执行make modules_prepare

参考:
https://blog.csdn.net/robinsonmhj/article/details/41719383

然后进入到helloworld.c所在目录,执行make命令编译出内核模块。
尝试用insmod命令加载,却报错了:

root@debian2:~/kernelModuleTest/helloworld# insmod helloworld.ko
insmod: ERROR: could not insert module helloworld.ko: Invalid module format
root@debian2:~/kernelModuleTest/helloworld#
root@debian2:~/kernelModuleTest/helloworld# dmesg | tail -1
[14361.160431] helloworld: no symbol version for module_layout
root@debian2:~/kernelModuleTest/helloworld#

原来,是module_layout这个符号没有版本信息。
在编译的时候,已经有这样一个告警信息了:

  WARNING: Symbol version dump ./Module.symvers
           is missing; modules will have no dependencies and modversions.

我理解module_layout这个符号表示的意思就是关于内核模块的一些信息,只有编译内核模块所使用的源码的这些信息与运行环境的信息一致,内核才会同意加载此内核模块,从而保证不会出现不兼容的问题。

其实表示版本信息的是一个CRC校验值,内核编译完成之后,会在根目录下生成一个名叫Module.symvers的文件,里面就有module_layout这个符号的CRC值。

各个内核模块会继承module_layout这个符号的CRC值。
编译完成之后,可以在源码目录下的helloworld.mod.c中查看。
也可以通过modprobe命令解析ko文件来查看:

root@debian2:~/kernelModuleTest/helloworld# modprobe --dump-modversions helloworld.ko | grep module_layout
root@debian2:~/kernelModuleTest/helloworld#
root@debian2:~/kernelModuleTest/helloworld# modprobe --dump-modversions /lib/modules/4.9.0-7-686/kernel/net/netfilter/ipvs/ip_vs.ko | grep module_layout
0xd1fdcdd3      module_layout

运行环境module_layout符号的CRC值查看:

grep "module_layout" /boot/symvers-xxxx-default.gz

比较费解的是我的debian上,居然没有这个文件。搜索了很久,也没找到可以在什么地方查看运行环境上module_layout这个符号的CRC值。

不过换了一个思路,debian默认有很多内核模块在/lib/modules/4.9.0-7-686/kernel/目录下。通过modprobe命令查看这些内核模块得到的module_layout符号的CRC值一定和运行环境是一致的。

可以看出,我们出错是因为我们根本就没有编译内核,没有生成Module.symvers这个文件。
方法1:
比较费时的办法就是把整个内核全部编译一遍,然后再编译我们需要的内核模块。

  1. cp /boot/config-4.9.0-7-686 .config
  2. make oldconfig
  3. make prepare
  4. date;make -j 4>make.log;date
    这是一个非常标准正确的做法。

方法2:

  1. 将编译出的模块拷贝到/lib/modules/xxx/目录下
    cp helloworld.ko /lib/modules/4.9.0-7-686/kernel/
  2. demod
  3. modprobe -f helloworld

参考:
https://askubuntu.com/questions/14627/no-symbol-version-for-module-layout-when-trying-to-load-usbhid-ko
可以在/var/log/messages中看到对应的日志:

Apr 27 19:20:11 debian2 kernel: [20973.555866] helloworld: module_layout: kernel tainted.
Apr 27 19:20:11 debian2 kernel: [20973.555868] Disabling lock debugging due to kernel taint
Apr 27 19:20:11 debian2 kernel: [20973.555870] helloworld: loading out-of-tree module taints kernel.
Apr 27 19:20:11 debian2 kernel: [20973.556251] helloworld: loading module not compiled with retpoline compiler.
Apr 27 19:20:11 debian2 kernel: [20973.556254] helloworld: module license 'unspecified' taints kernel.
Apr 27 19:20:11 debian2 kernel: [20973.563420] Hello world 1.
Apr 27 19:21:02 debian2 kernel: [21024.583558] Goodbye world 1.

单独编译ipvs模块

编译

同样,对内核源码需要进行和上面一样的准备工作:

  1. cp /boot/config-4.9.0-7-686 .config
  2. make oldconfig
  3. make prepare

然后编译ipvs模块:
make -C /usr/src/linux-4.9.1 M=/usr/src/linux-4.9.1/net/netfilter/ipvs/ modules

如果修改了IP_VS的编译选项(.config),则需要执行上面的准备工作,再编译ip_vs模块,否则不会生效。

同样,编译出的模块不具有版本信息,需要强制加载。

注:

使用了 make SUBDIRS=./net/netfilter/ipvs modules命令进行编译,但是报错:
ld: no input files
scripts/Makefile.build:531: recipe for target ‘net/netfilter/ipvs/ip_vs.o’ failed
make[1]: *** [net/netfilter/ipvs/ip_vs.o] Error 1
Makefile:1490: recipe for target ‘module./net/netfilter/ipvs’ failed
make: *** [module./net/netfilter/ipvs] Error 2

强制加载

由于/lib/modules/4.9.0-7-686/目录下本身就已经有ipvs模块了,担心直接将编译出的ipvs模块拷贝过去会引起冲突。
本来应该在/lib/modules目录下再新建一个目录的。但是由于不知道改如何让depmode和modprobe到指定目录下去读取,所以没有采用此方法。
采用的方法是先将/lib/modules/4.9.0-7-686/目录备份。然后直接将/lib/modules/4.9.0-7-686/kernel/net/netfilter/ipvs/目录下的ko文件删除。
然后建立软链接到编译出的ko文件。
这样修改源码,重新编译之后,可以节省拷贝这一步动作。

执行depmod之后,执行modprobe -f强制加载即可。

猜你喜欢

转载自blog.csdn.net/qq_31567335/article/details/89603848