Skip to content

Kernel Module Headers

In recent projects, we need to hack on Linux Kernel with various customization on multiple architectures. However, common distros like Fedora/Debian/Centos will not provide compatible kernel development headers (staffs under /lib/modules/$version) required by building kernel modules.

For example, if we compile a kernel module without a compatible kernel development header, it complains:

make -C /lib/modules/6.1.11/build M=... modules
make[1]: *** /lib/modules/6.1.11/build: No such file or directory.  Stop.
make: *** [Makefile:6: all] Error 2

To fix this issue, we have to build ourselves a kernel development header from the source. It took some time to find a solution.

Build Kernel Module Preparation

First, we should get the exact version of the source code and the same configuration (.config) with your customized kernel. Then, run modules_prepare target.

cd /path/to/linux-6.1.11
make modules_prepare

Note that modules_prepare target will not generate Module.symvers, which contains a list of exported symbols from a kernel build. Module.symvers is crucial for module versioning but is not necessary for an experimental kernel module. Run a full kernel build if we need Module.symvers.

Copy this kernel directory to /usr/src and rename.

(sudo) cp /path/to/linux-6.1.11 /usr/src/linux-headers-6.1.11

Link it to /lib/modules/6.1.11/build.

(sudo) ln -s /usr/src/linux-headers-6.1.11 /lib/modules/6.1.11/build

Done!

Test with a Kernel Module

We test the self-built kernel development header with an example kernel module (hello.c) from The Linux Kernel Module Programming Guide

Source code:

/* 
 * hello.c - The simplest kernel module. 
 */ 
#include <linux/kernel.h> /* Needed for pr_info() */ 
#include <linux/module.h> /* Needed by all modules */ 

int init_module(void) 
{ 
    pr_info("Hello world.\n"); 

    /* A non 0 return means init_module failed; module can't be loaded. */ 
    return 0; 
} 

void cleanup_module(void) 
{ 
    pr_info("Goodbye world.\n"); 
} 

MODULE_LICENSE("GPL");

Building script:

obj-m += hello.o

PWD := $(CURDIR) 

all: 
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 

clean: 
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Build and test:

$ make

$ (sudo) insmod hello.ko
[15394.319943] hello: loading out-of-tree module taints kernel.
[15394.371244] Hello world.

$ (sudo) rmmod hello
[15436.280762] Goodbye world.

Cheers!