Creating a simple loadable kernel module (LKM) on CentOS 7 (Linux kernel development – part 2)

1.Preface
Hi all, as I continue with my Linux kernel development, and after I have made all the “basic” preparations to be able to build (loadable) kernel modules – in this previous post ,
in this post I will go over the basic steps and will write a very basic and minimal “Hello world” loadable kernel module (AKA, LKM).

2. Loadable Kernel Module
2.1) Loadable Kernel Module (an LKM), is a “piece” of code, that is built in the form of an object file (not an “entire” executable) that performs some functionality and is “loaded” (added) to the running kernel during run-time. Note that the LKM can also be un-loaded (“removed”) from the running kernel.
This is in contrast, to a “static” kernel module, that is “loaded” (added) to the kernel upon its build procedure – meaning it is an integral part of the kernel.
2.2) Once the LKM is loaded it is part of the running kernel with “full accesses privileges”. This is an important note that will be relevant throughput this entire Linux kernel development series – it means that extra care MUST be taken when developing kernel modules,cause eventually when they will be loaded – every mistake in them can impact the entire running kernel and/or the entire system – with great power comes great responsibility .

3. LKM utilities
Here I will explore some of the basic utilities that can (and will) be used when developing a kernel module for debugging and “monitoring” purposes.
3.1) lsmod – is a utility that can be used in order to view all the current modules that are loaded (running) in the kernel, for example:

[liveuser@localhost ~]$ lsmod 
Module                  Size  Used by
tcp_lp                 12663  0 
rfcomm                 69552  2 
fuse                  100463  3 
ip6t_rpfilter          12595  1 
ip6t_REJECT            12625  2 
nf_reject_ipv6         13717  1 ip6t_REJECT
ipt_REJECT             12541  2 
nf_reject_ipv4         13373  1 ipt_REJECT

NOTE: This information (in a different format) can be also shown by “cating” (displaying) the content of the “pseudo file system” file /proc/modules, for example:

$cat /proc/modules 
tcp_lp 12663 0 - Live 0xffffffffc06cb000
fuse 100463 3 - Live 0xffffffffc06a8000
vboxsf 81052 0 - Live 0xffffffffc068d000 (OE)
xt_CHECKSUM 12549 1 - Live 0xffffffffc0688000
ipt_MASQUERADE 12678 3 - Live 0xffffffffc0678000
nf_nat_masquerade_ipv4 13430 1 ipt_MASQUERADE, Live 0xffffffffc0673000
tun 32026 1 - Live 0xffffffffc067f000
bridge 151336 0 - Live 0xffffffffc064d000
stp 12976 1 bridge, Live 0xffffffffc0641000
llc 14552 2 bridge,stp, Live 0xffffffffc0648000

4. General guidelines when writing kernel modules
4.1) Kernel modules are (generally) written in C, yet in some “specific cases”, i.e.- when implementing architecture specific commands/methods – they are written in assembly for every “supported” target.
4.2) Kernel modules share the same address space with the kernel itself – so any errors such as memory overruns might probably affect the entire system (kernel crashes/”oopses”).
4.3) Kernel has a fixed stack with size of 4/8KB.
4.4) Floating points variables can NOT be used.
4.5) Kernel memory can NOT be swapped.
4.6) Kernel modules can NOT use the C standard library, yet they have their “own” alternative with similar basic function such as printk, kmalloc, etc…

5. Kernel modules build prerequisites
5.1) Suitable tool chain for the target that the module is built on and intended to run on (or cross compile tool chain in case the build machine is not the same machine where the module will run).
5.2) Configured and proper kernel headers.
NOTE: Kernel modules will NOT load to kernel with “mismatching” kernel headers for instance (the insmod utility will fail).

6. Minimal simple kernel module
The very minimal code that is required to implement the very basic kernel module is as follows:

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple kernel module");
MODULE_AUTHOR("Guy Avraham");

static int __init hello_init(void)
{
	printk(KERN_ALERT "hello_init - module is loaded\n");
	return 0;
}

static void __exit hello_exit(void)
{
	printk(KERN_ALERT "hello_exit - module is unloaded\n");
}

module_init(hello_init);
module_exit(hello_exit);

NOTES:
6.1) The function hello_init is “registered” as the “entry point” of the module by providing it to the module_init macro. This way it will be called when the module will be loaded (for example, using the insmod command).
6.2) The function hello_exit is “registered” as the “exit point” of the module by providing it to the module_exit macro. This way it will be called when the module will be loaded (for example, using the rmmod command).
6.3) The three MODULE_xxx macros are quit “self explanatory” and can be noted when running the command modinfo (which displays information about the module).
6.4) The __init macro indicate that this function can be removed from RAM once the module was loaded into the kernel.
6.5) The loader of the kernel modules will NOT call the hello_exit function until the “reference count” for this module is NOT zero.
6.6) Tainted kernel warning is logged in the kernel logs (can be observed by the dmesg utility) when a module was loaded and it does NOT have a GPL licence.

7. Makefile for the LKM
The Makefile that is required in order to build the above module, is as follows:

# Makefile for the simple kernel module
obj-m += simpleKernelModule.o 
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

EXTRA_CFLAGS = -g

default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

NOTES:
7.1) obj-m should be set to the name of the C file of the kernel module (replaced by the .o postfix).
7.2) KDIR is a variable that “points” to the path of the kernel source directory.
7.3) shell is a “Make” keyword and it is used to “invoke” a shell, execute a command, and place the output “back” in the row.
Note that the $(uname -r) is used to detect the current running kernel so that the compilation indeed will be compatible to the running kernel (otherwise, as mentioned earlier, when loading the module, an error will raise and the module will NOT be loaded).
7.4) In order to build the kernel module one need to call the make utility where the Makefile and the C source file of the above module reside in. The “artifact” of this build procedure will be a file with the same name as the C file, which has the .ko postfix (ko == kernel object, recall that this is not a “normal” user space executable !!).
7.5) Once the build is over, the content of the “build folder” (to which the build output was directed to) has the following content:

$ ls build
Makefile  modules.order  Module.symvers  simpleKernelModule.c  simpleKernelModule.ko  simpleKernelModule.mod.c  simpleKernelModule.mod.o  simpleKernelModule.o

7.5.1) The file with the C source file name with the postfix .ko is the module Kernel Object. This is the file that will be loaded.

8. Loading the kernel module
Once the kernel module was built, all that there is left to do is to load the kernel module to the current running kernel.For this purpose there are two approaches:
8.1) insmod – when using this utility to load the kernel module, then ONLY the kernel module will be loaded, meaning, if it has any dependencies, i.e. – other modules that it depends on, they will NOT be loaded – so if they are not already running in the current running kernel – the module loading will fail.
8.2) modprobe – loads the module along with all its dependencies.
8.3) rmmod – removed a kernel module.
8.4) modprobe -r – unloads a module along with all its dependent modules.
NOTE: In case the module being developed is dependent on other modules, then eventually it MUST be loaded with modprobe –> to make sure that before it is loaded ALL its dependent modules are loaded. Nevertheless, when developing a module, it is easier to “keep” the dependent modules loaded and only load & unload the developed module itself with the insmod/rmmod respectively.

9. Inspecting kernel module “printk”
It is possible to inspect the kernel module (and all kernel code in general) messages that were “written” to the “kernel’s stdout” via the printk function using the dmesg utility.
9.1) In order to load the module use the command:

$ sudo insmod simpleKernelModule.ko

9.2) After loading the module followed by unloading it, the output of the dmesg utility is:

$ dmesg
[ 2321.396592] hello_init - module is loaded
[ 2344.763916] hello_exit - module is unloaded

9.3) To unload the module use the command:

$ sudo rmmod simpleKernelModule

NOTE: There is no .ko postfix in the kernel module name in this command.
Resources:
a) kernel.org documentation about building kernel modules
b) nice explanation about the __init and __exit macros

The picture: Vila Madalena neighborhood, São Paulo, Brazil.

4 thoughts on “Creating a simple loadable kernel module (LKM) on CentOS 7 (Linux kernel development – part 2)

  1. Oh my goodness! Awesome article dude! Thanks, However I am having troubles with your RSS.
    I don’t know the reason why I am unable to subscribe to it.
    Is there anybody getting similar RSS problems? Anyone who knows the solution can you kindly
    respond? Thanx!! http://kep39.ru

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s