Linux has a build infrastructure called “kbuild” to build in-tree and out-of-tree kernel modules. Here I’ll focus on external or out-of-tree kernel module compilation. Linux “kbuild” infrastructure is fairly complicated, better not to go into that. Here we’ll see how to create a Makefile to compile one or more source files into an external kernel module. To compile a kernel module, you need the running kernel source code and kernel headers. If you are using distribution Linux, then you already have these. Otherwise, you can get the kernel source from kenel.org.
To install the linux-header package on Debian or Ubuntu Linux run this command as root.
# apt-get install linux-headers-$(uname -r)
For RHEL, CentOS or Fedora Linux
# yum install kernel-devel
Now your system is ready to compile kernel module. Here I assumed You have basic understanding of Linux kernel programming.
Read also: Linux Kernel Programming Basics
Compile Kernel Module with Single Source File
If you have a single file in your module, say samplefile.c, then the Makefile should look like
obj-m += samplefile.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
In this Makefile we have two targets “all” and “clean”. Target “all” will be used to build the kernel module (.ko) and “clean” will be used to delete all files that will created during build process in your source directory.
In our “all” target, we used a make command with target “modules” which is the default target to build external module. From M=$(PWD) option, “kbuild” knows that you are building an external module. The value given to “M” is the absolute path of the directory where your Makefile is located. We specified the path where the kernel source is located using -C options. Here we specified the source of the running kernel as “shell uname -r” will return the version of the running kernel.
When you will run the “make” command, the “kbuild” infrastructure will search a file, “samplefile.c” in your source directory to compile because of the line number 1 (obj-m += samplefile.o). You have to append your source file name with .o extension with the “obj-m” variable.
Now run make command from your source directory. Output will look like:
# make make -C /lib/modules/3.11.9-200.fc19.x86_64/build M=/root modules make[1]: Entering directory `/usr/src/kernels/3.11.9-200.fc19.x86_64' CC [M] /root/samplefile.o Building modules, stage 2. MODPOST 1 modules CC /root/samplefile.mod.o LD [M] /root/samplefile.ko make[1]: Leaving directory `/usr/src/kernels/3.11.9-200.fc19.x86_64'
After comilation, samplefile.ko will be generated in your source directory.
Different Module Name from the Source File
In the above example, you had to specify the source file name in “obj-m += samplefile.o”. As a result the generated kernel module name, samplefile.ko, was also same as the source file name. But you might want different name of you kernel module from your source file.
To do that you have to modify your Makefile like this.
obj-m += samplemodule.o samplemodule-y = samplefile.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Compile Kernel Module from Multiple Source Files
So far we learnt how to write make file for single source file. But in most of the practical cases, one kernel module will have multiple files. To compile multiple source files into one module, your make file should look like:
obj-m += samplemodule.o samplemodule-y = samplefile.o samplefile1.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
As the size of our Makefile is growing, better to reorganize it in a better way using variables. Same Makefile can be written as:
MODULE_MAME = samplemodule SRCS = samplefile.c samplefile1.c OBJS = $(SRCS:.c=.o) obj-m += $(MODULE_MAME).o $(MODULE_MAME)-y = $(OBJS) all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Compile Files Organized in Multiple Directories
For bigger and relatively complicated modules, source files are generally organized in multiple directories. For example, your source files are organized as:
.
|__ samplefile.c
|__ samplefile1.c
|__ Makefile
|
|__ subdir
|__ samplefile2.c
In this case you have to specify the relative paths of the source files in the Makefile like this:
MODULE_MAME = samplemodule SRCS = samplefile.c samplefile1.c subdir/samplefile2.c OBJS = $(SRCS:.c=.o) obj-m += $(MODULE_MAME).o $(MODULE_MAME)-y = $(OBJS) all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Specify Include Directories
If you header files are located in same location as your source files, then you don’t need to specify the path in the Makefile. But if they are located in different path, then you have to mention that in the Makefile for successful compilation. Lets say, your directory structure is like this:
.
|__ samplefile.c
|__ samplefile1.c
|__ Makefile
|
|__ subdir
| |__ samplefile2.h
|
|__ Include1
| |__ samplefile1.h
|
|__ Include2
|__ samplefile2.h
Your make will be this:
MODULE_MAME = samplemodule SRCS = samplefile.c samplefile1.c subdir/samplefile2.c INCLUDE_DIR = -I$(src)/Include1 -I$(src)/Include2 ccflags-y := $(INCLUDE_DIR) OBJS = $(SRCS:.c=.o) obj-m += $(MODULE_MAME).o $(MODULE_MAME)-y = $(OBJS) all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
One important thing you have to note here, the include directory is specified as “-I$(src)/Include1” not as “-I./Include1”. Because during compilation the current directory is not your source directory but the directory specified in -C <path> option. But good thing is the your source directory would be available in “src” variable. So don’t forget to add “$(src)/” before your actual include directory.