编译内核模块

出自 ArchWiki

有时您可能希望编译 Linux 的内核模块,而无需重新编译整个内核。

注意: 只有当现有模块被编译为模块 (M) 而不是内置 (y) 到内核时,您才可以替换它。

构建环境

首先,您需要安装构建依赖项,例如编译器 (base-devel) 和 linux-headers

接下来,您需要获取模块计划运行的内核版本的源代码。您可以尝试使用较新的内核源代码,但编译后的模块很可能无法加载。

如果目标内核版本是已安装的内核,请使用以下命令查找其版本

$ uname -r

获取所需源代码主要有两种选择。每种选择的使用方法和目录结构略有不同。

传统编译

请参阅 Kernel/Traditional compilation#Download the kernel source。如果您使用 Git 获取最新源代码,您将需要使用标签检出所需的版本(例如 v4.1)。

Arch 构建系统

有关 Arch 构建系统的概述,请阅读 ABS。有关获取内核源代码、目录结构和其他详细信息,请参阅 Kernel/Arch build system

源码配置

当您拥有源代码时,进入其目录。对于#Arch 构建系统 的情况,该目录将是 PKGBUILD 所在的 src/archlinux-linux/

make help 的输出在这里很有用。首先使用以下命令进行清理

$ make mrproper
注意: 它将删除 .config.config.old。您可能需要在清理之前将这些文件保存到其他位置。

现在需要一个合适的 .config 文件。如果没有附近的,也许来自已保存的 .config,并且目标内核版本是正在运行的内核,您可以使用其配置文件

$ zcat /proc/config.gz > .config

接下来,确保 .config 文件已针对内核版本进行调整。如果您使用的是与当前版本完全相同的内核源代码,则它不应询问任何内容。但对于与当前内核不同的版本,可能会询问您一些选项。在任何情况下,对于#Arch 构建系统 选项,您可能需要检查 PKGBUILD::prepare() 函数。

如果您要编译的模块有一些编译选项,例如调试构建,或者之前未编译过,您也可以(可能必须)调整内核配置。您可以使用 make help 中提到的许多配置目标之一来执行此操作。

$ make oldconfig

模块编译

为了干净地编译和加载我们的模块,我们必须找到当前内核版本号的 EXTRAVERSION 组件的值,以便我们可以在内核源代码中完全匹配版本号。EXTRAVERSION 是内核顶级 Makefile 中设置的变量,但 vanilla 内核源代码中的 Makefile 的 EXTRAVERSION 为空;它仅作为 Arch 内核构建过程的一部分设置。如果相关,可以通过查看 uname -r 命令的输出来找到当前内核的 EXTRAVERSION 值。通常,内核版本是三个组件的串联。即,数字版本、EXTRAVERSION 和 LOCALVERSION。数字版本本身是三个数字的串联。如果由 PKGBUILD 文件构建,LOCALVERSION 将从 pkgrel 变量中获取,并以连字符为前缀。EXTRAVERSION 将是 pkgver 变量的后缀,其中数字版本的第三个数字右侧的点字符将替换为连字符。例如,对于 linux 软件包 linux 5.5.8.arch1-1,LOCALVERSION 是 -1。EXTRAVERSION 是 -arch1。在该示例中,uname -r 的输出将是 5.5.8-arch1-1

一旦 EXTRAVERSION 值已知,我们就准备用于模块编译的源代码

$ make EXTRAVERSION=<YOUR EXTRAVERSION HERE> modules_prepare

示例

$ make EXTRAVERSION=-arch1 modules_prepare

或者,如果您很乐意使用 modprobe 加载模块,并使用 --force-vermagic 选项来忽略内核版本号中的不匹配,您可以简单地运行

$ make modules_prepare
注意: 使用顶级 Makefile 中记录的 EXTRAVERSION 以避免在命令行上手动指定 EXTRAVERSION 可能会导致版本不匹配。内核构建过程会识别到这一点,导致它将 + 字符附加到 LOCALVERSION 配置设置。例如:5.5.8-arch1-1+

最后,通过指定其目录名称来编译所需的模块。您可以使用 modinfo 或 find 找到模块位置,也就是它的目录名称。

$ make M=fs/btrfs

作为最后的手段,如果其他方法都无效,您可以

$ make modules

这将构建内核配置中的所有模块。

树外模块编译

获取当前运行的 linux 内核的官方源代码,如 Kernel/Arch build system 中所述

$ cd && mkdir build
$ pkgctl repo clone linux

然后在编译模块时指向检出的源代码

$ cd build/mymod
$ make -C ~/build/linux/src/archlinux-linux M=$PWD modules

模块安装

现在成功编译后,您只需将其 gzip 压缩并复制到当前内核。

如果您要替换某些现有模块,则需要覆盖它(并记住重新安装 linux 将会用默认模块替换它)

$ zstd fs/btrfs/btrfs.ko
# cp -f fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/kernel/fs/btrfs/

或者,您可以将更新后的模块放在 updates 文件夹中(如果不存在则创建它)。

$ cp fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/updates

但是,如果您要添加新模块,则可以将其复制到 extramodules(注意,这只是示例,因为 btrfs 不会从此处加载)

# cp fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/extramodules/

您需要使用“depmod”重建模块依赖树才能使用已安装的模块。

如果您正在编译用于早期启动的模块(例如,更新的模块),该模块已复制到 Initramfs,那么您必须记住使用以下命令重新生成它(否则您编译的模块将不会被加载)。

# mkinitcpio -p linux

可能出现的错误

如果 EXTRAVERSION 设置不正确,可能会发生以下错误

# insmod mymod.ko
insmod: ERROR: could not insert module mymod.ko: Invalid module format
# modprobe mymod
modprobe: ERROR: could not insert 'mymod': Exec format error

添加 force-vermagic 使其忽略版本不匹配

modprobe mymod --force-vermagic

参见