如何针对内核头文件进行编译?

我正在尝试编译一个第三方专有驱动模块,但是一直失败。我已经安装了linux-headers-5.4.0包,但是我不知道如何编写一个能够成功使用它的gcc命令行。
作为一个最简单的示例,考虑一下test.c,它除了包含内核头文件之外什么都不做。
#include <asm/linkage.h>
#include <linux/module.h>

因为内核包含这两个文件,并且它本身可以编译,所以肯定有某种组合的GCC标志会导致test.c编译。专有驱动程序的.c文件包含了许多其他文件,但它们足以说明我的问题。
eyes=(
  -I/usr/src/linux-headers-5.4.0-21/include            # for linux/module.h
  -I/usr/src/linux-headers-5.4.0-21/arch/x86/include   # for asm/linkage.h
)
gcc -c "${eyes[@]}" test.c

结果为

In file included from /usr/src/linux-headers-5.4.0-21/include/linux/seqlock.h:36,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/time.h:6,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/stat.h:19,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/module.h:10,
                 from test.c:2:
/usr/src/linux-headers-5.4.0-21/include/linux/spinlock.h:60:10: fatal error: asm/mmiowb.h: No such file or directory
   60 | #include <asm/mmiowb.h>
      |          ^~~~~~~~~~~~~~

dpkg -L linux-headers-5.4.0-21 | grep mmiowb.hinclude/asm-generic 目录下显示了这个文件,同时也在 arch/{ia64,mips,powerpc,riscv,sh}/include/asm 目录中找到。进一步调整我的 -I 参数以包含其中一个目录后,错误信息转移到了:

In file included from /usr/src/linux-headers-5.4.0-21/include/linux/mmzone.h:19,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/gfp.h:6,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/umh.h:4,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/kmod.h:9,
                 from /usr/src/linux-headers-5.4.0-21/include/linux/module.h:13,
                 from test.c:2:
/usr/src/linux-headers-5.4.0-21/include/linux/page-flags-layout.h:6:10: fatal error: generated/bounds.h: No such file or directory
    6 | #include <generated/bounds.h>
      |          ^~~~~~~~~~~~~~~~~~~~

这个文件在我的系统上根本不存在,但是有一个叫做/usr/src/linux-headers-5.4.0-21/KBuild的文件,看起来像一个可以创建它的Makefile,尽管我搞不清楚如何调用这个Makefile。

我应该怎么继续呢?在安装linux-headers-*之后,是否需要运行某个命令来填充generated文件夹?是否有在线指南可以参考这类问题呢?

1个回答

Linux内核具有基于make的构建系统,该系统生成适用于成功编译内核组件和模块的正确gcc选项。 包含在外部内核模块中的Kbuild文件旨在与该内核构建系统一起使用。此外,外部内核模块还应包括一个调用内核构建系统的Makefile。外部模块的一个最小Makefile可能如下所示:

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

正如你所看到的,内核构建系统的主要Makefile实际上位于文件夹/lib/modules/$(KVERSION)/build中,其中$(KVERSION)是您正在构建的内核版本号。如果您感到好奇,可以尝试追溯Makefile网格的内容及其最终使用哪组选项来调用gcc编译器。

1谢谢。第三方代码包含一个Makefile,其中有obj-m行(但没有其他内容),并且从shell手动运行make -C /lib/modules/5.4.0-21-generic/build M=$(pwd) modules已经构建了一个.ko文件。为了后人,请参阅kbuild文档 - Dan