How to Compile Linux Kernel Module

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.

Author: Srikanta

I write here to help the readers learn and understand computer programing, algorithms, networking, OS concepts etc. in a simple way. I have 20 years of working experience in computer networking and industrial automation.


If you also want to contribute, click here.

Leave a Reply

Your email address will not be published. Required fields are marked *

0
0
0
4
0
0