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:
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:
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:
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:
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:
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:
9.2) After loading the module followed by unloading it, the output of the dmesg utility is:
9.3) To unload the module use the command:
NOTE: There is no .ko postfix in the kernel module name in this command.
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.