Compiling Kernel Modules
Kernel modules need to be compiled with certain gcc options to make them work. In addition, they also need to be compiled with certain symbols defined. This is because the kernel header files need to behave differently, depending on whether we’re compiling a kernel module or an executable. You can define symbols using gcc’s -D option, or with the #define preprocessor command. We’ll cover what you need to do in order to compile kernel modules in this section.
-c : A kernel module is not an independant executable, but an object file which will be linked into the kernel during runtime using insmod. As a result, modules should be compiled with the -c flag.
-O2 : The kernel makes extensive use of inline functions, so modules must be compiled with the optimization flag turned on. Without optimization, some of the assembler macros calls will be mistaken by the compiler for function calls. This will cause loading the module to fail, since insmod won’t find those functions in the kernel.
-W -Wall : A programming mistake can take take your system down. You should always turn on compiler warnings, and this applies to all your compiling endeavors, not just module compilation.
-isystem /lib/modules/`uname -r`/build/include : You must use the kernel headers of the kernel you’re compiling against. Using the default /usr/include/linux won’t work.
-D__KERNEL__ : Defining this symbol tells the header files that the code will be run in kernel mode, not as a user process.
-DMODULE : This symbol tells the header files to give the appropriate definitions for a kernel module.
We use gcc’s -isystem option instead of -I because it tells gcc to surpress some «unused variable» warnings that -W -Wall causes when you include module.h . By using -isystem under gcc-3.0, the kernel header files are treated specially, and the warnings are surpressed. If you instead use -I (or even -isystem under gcc 2.9x), the «unused variable» warnings will be printed. Just ignore them if they do.
So, let’s look at a simple Makefile for compiling a module named hello-1.c :
Example 2-2. Makefile for a basic kernel module
TARGET := hello-1 WARN := -W -Wall -Wstrict-prototypes -Wmissing-prototypes INCLUDE := -isystem /lib/modules/`uname -r`/build/include CFLAGS := -O2 -DMODULE -D__KERNEL__ $ $ CC := gcc-3.0 $.o: $.c .PHONY: clean clean: rm -rf $.o
As an exercise to the reader, compile hello-1.c and insert it into the kernel with insmod ./hello-1.o (ignore anything you see about tainted kernels; we’ll cover that shortly). Neat, eh? All modules loaded into the kernel are listed in /proc/modules . Go ahead and cat that file to see that your module is really a part of the kernel. Congratulations, you are now the author of Linux kernel code! When the novelty wares off, remove your module from the kernel by using rmmod hello-1 . Take a look at /var/log/messages just to see that it got logged to your system logfile.
Here’s another exercise to the reader. See that comment above the return statement in init_module() ? Change the return value to something non-zero, recompile and load the module again. What happens?
Compiling Linux kernel module using gcc with kernel header files
I have a Linux machine with kernel A header files. I want to compile a C program using GCC with kernel A while kernel B is currently running. How can I do that? How do I check that it works?
kernel module. I add the ‘-I/usr/src/linux-headers-2.6.32-38-server/include/’ option to my Makefile, but after that the system continue with the compilation process, is this change enough?
If it is a kernel module you must use the kernel Makefile present in the kernel source directory. Why are you using gcc ? Post the Makefile you are using to build the kernel module.
2 Answers 2
This is additional info to delve into. Post 2.6 version, as mentioned in other reply the Makefile takes care of most of the Linux kernel module compilation steps. However, at the core of it is still GCC, and this is how it does: (you too may compile it without Makefile)
Following GCC options are necessary:
- -isystem /lib/modules/`uname -r`/build/include: You must use the kernel headers of the kernel you’re compiling against. Using the default /usr/include/linux won’t work.
- -D__KERNEL__: Defining this symbol tells the header files that the code will be run in kernel mode, not as a user process.
- -DMODULE: This symbol tells the header files to give the appropriate definitions for a kernel module.
gcc -DMODULE -D__KERNEL__ -isystem /lib/modules/$(uname -r)/build/include -c hello.c -o hello.ko
In order to compile a kernel module, it is better to use the kernel Makefile resident in the Kernel source directory. You can use the following make command:
make -C $(KERNEL_SOURCE_DIR) M=`pwd` modules
Otherwise, you can choose to write your own Makefile like this:
KERNEL_DIR := /lib/modules/$(shell uname -r)/build obj-m := test.o driver: make -C $(KERNEL_DIR) M=`pwd` modules clean: make -C $(KERNEL_DIR) M=`pwd` clean
In this I have used the KERNEL_DIR as /lib/modules/$(shell uname -r)/build which uses the kernel headers of the kernel which is running currently. However, you can use the path of the kernel source directory you want to compile your module with.
This shows how you can do it using gcc .
Compile a linux 2.6 kernel module with newer compiler
I build embedded machines that run an RT_PREMPT version of Linux. It’s an Ubuntu 10.04 installation running an Linux 2.6 kernel. Yes, it’s an old kernel, but I’m stuck with it for awhile. When I compiled the kernel, I used gcc version 4.4. On this system, there is a kernel module I have been compiling successfully for three years. From my Makefile.
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) \ modules
My current project requires support for c++14, so I updated gcc and g++ to version 5.1.0 by building from source. All my user-mode software compiles, but when I went to build an updated version of my kernel module, I get the following error right away:
make[1]: Entering directory `/usr/src/linux-headers-2.6.32-54-generic' CC [M] /home/tbj/srcroot/ctsengine-hg/CtsRt/ctsrt_main.o In file included from include/linux/compiler.h:40:0, from include/linux/stddef.h:4, from include/linux/list.h:4, from include/linux/module.h:9, from /home/tbj/srcroot/ctsengine-hg/CtsRt/ctsrt_main.c:31: include/linux/compiler-gcc.h:86:30: fatal error: linux/compiler-gcc5.h: No such file or directory
fatal error: linux/compiler-gcc5.h: No such file or directory
If I use gcc 4.4 again (I left it installed on my computer in a different directory), it compiles and runs perfectly. Obviously, I’m missing something. Is it not possible to compile a kernel module with a newer version of the compiler than what the operating system was compiled with? That seems unlikely to me. Is there a configuration step I’m missing? Is there a system variable I need to update? Are there extra headers I’m supposed to download? I ran apt to update build-essential, but it was up to date. I have the source and headers for the kernel on my system. I’m not sure what else I would download. Any insights to this problem are greatly appreciated. Thank you in advance.