The Kernel Newbie Corner: Your First Loadable Kernel Module, Part Deux

546

Moving on from where we left off last time, this article isn’t so much the next in the series as much as it’s just tidying up some random loose ends. And so, to business.

This is ongoing content from the Linux Foundation training program. If you want more content please, consider signing up for one of these classes.

I Can Haz Kernel Source Tree?

Yes, you can. As you already know, you can’t compile a loadable kernel module without at least a partial kernel source tree — the part that contains the general build infrastructure and the essential header files. But since it’s handy to have a full source tree hanging around for the sake of perusing its contents every so often, you might as well go get one:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/
git/torvalds/linux-2.6.git git-kernel

and keep it up to date on a regular basis:

$ git pull

Note that, unlike installing the official kernel development package we mentioned last time, you don’t need root privilege to simply dump a kernel source tree somewhere under your home directory. And you can make that last git clone argument any name you want for the destination directory.

(If you can’t use git for some reason, a reasonably recent tarball will work just as well. It’s just cooler to use git.)

Now go get a tree. I’ll wait.

NOTE: We’ll still be building our modules against the installed kernel development package. Your personal source tree is strictly for educational purposes, at least for now.

“Prepping” Your Source Tree, Examining the Header Files

Recall from the original article that your module source file will normally contain references to kernel space header files such as:

 #include <linux/module.h>      // for all modules
#include <linux/init.h> // for entry/exit macros
#include <linux/kernel.h> // for printk priority macros
#include <asm/current.h> // process information, just for fun
#include <linux/sched.h> // for "struct task_struct"

Such references are always relative to the top-level include/ directory in the source tree, so an include of, say, <linux/module.h> refers to the kernel header file include/linux/module.h, and so on.

If you look closely, however, you’ll notice that there is no include/asm/ directory, but that’s easy enough to fix. Even if you don’t plan on building against your personal source tree, you should still “prep” it so that it more accurately reflects what you plan on doing with it:

 $ make defconfig
$ make modules_prepare

While the above does a number of things, the only one we care about right this minute is the creation of the appropriate symlink under the include/ directory to reflect our system architecture.

Before the prep:

 $ ls -ld include/asm*
drwxrwxr-x. ... include/asm-arm
drwxrwxr-x. ... include/asm-generic
drwxrwxr-x. ... include/asm-x86

After the prep:

 $ ls -ld include/asm*
lrwxrwxrwx. ... include/asm -> asm-x86 <-- aha!
drwxrwxr-x. ... include/asm-arm
drwxrwxr-x. ... include/asm-generic
drwxrwxr-x. ... include/asm-x86

Once that’s done, your preprocessor includes will now make sense, and we can use the generic names to refer to the appropriate header files from now on.

(It should also go without saying that the above “prep” must have been part of any official kernel development package because, without it, you couldn’t build against that tree. You’ll see this again later when we switch to building our modules against our personal kernel source tree.)

I Can Haz Module Output?

No, you can’t. Well, not really. As a beginning module author, you need to understand that your module will be running in kernel space, not user space, so you have to stop thinking about printing debug messages back to your terminal. Or, for that matter, having your module open, read, write and close regular files. Get out of that mindset.

The canonical way to generate debugging messages from your module is with printk calls thusly:

 printk(KERN_INFO "hi module being loaded.n");
printk(KERN_INFO "User space process is '%s'n", current->comm);
printk(KERN_INFO "User space PID is %in", current->pid);

At the risk of over-simplification, the output from your printk calls will almost certainly end up being added to the /var/log/messages file so, while you’re inserting and removing your module, it’s handy to have a separate terminal window open and tailing, in real time, the contents of that file (for which, yes, you do need root privilege):

# tail -f /var/log/messages

For the more ambitious, it’s useful to note the definition of the printk log levels in the header file <linux/kernel.h>:

 #define KERN_EMERG    "<0>"  /* system is unusable                 */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */

If some of that looks vaguely familiar, it’s not surprising — those are the log levels supported by the system syslog facility, so you’re free to customize syslog if you want to redirect your module output messages wherever you want based on their log levels, which is totally beyond the scope of this article.

NOTE: The exceptionally observant among you will have noticed that the log level macros used by printk are simply defined as character strings “<0>” and so on, which explains why you don’t use a comma there–all the preprocessor is doing is concatenating two adjacent character strings, so it would have been entirely equivalent to have written any of:

 printk(KERN_INFO "hi module being loaded.n");
printk("<6>" "hi module being loaded.n");
printk("<6>hi module being loaded.n");

Use the first form, though. Even in kernel programming, aesthetics matter.

Loading Your Module Full of Information

As you noticed last time, you have the right to load your compiled module with useful information thusly:

 MODULE_AUTHOR("Robert P. J. Day");
MODULE_DESCRIPTION("You have to start somewhere.");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("3.14159");
MODULE_INFO(flavour, "Rocky Road");

that you can subsequently examine with the modinfo command:

 $ modinfo hi.ko
filename: hi.ko
flavour: Rocky Road
version: 3.14159
license: Dual BSD/GPL
description: You have to start somewhere.
author: Robert P. J. Day
srcversion: B6AADA40910DFF104897E21
depends:
vermagic: 2.6.29.5-191.fc11.x86_64 SMP mod_unload

Some handy facts about this feature:

  • The full set of these macros is defined in the header file <linux/module.h>, where you’ll see macros related to firmware, device tables and so on.

  • In addition to those specific macros, there is the less well-known, generic MODULE_INFO macro, which you can use to cram into the module whatever you want. Curiously, very few kernel programmers take advantage of this.

  • The only value that you really want to supply is the license. If you don’t specify some variation of a GPL license, then loading the module will officially “taint” the kernel, a topic for a future article. The full set of valid licenses is defined in that same header file.

Regarding the module license, if you’re feeling bold and daring, remove the license line from your module source, then see the difference in building and loading it. Then put it back.

Building Against Your Own Kernel Tree

Finally, you might want to build your module, not against the installed kernel development package tree, but against your personal source tree. If that’s your plan, it’s actually fairly simple.

First, as we discussed above, you need to “prep” your source tree for module building:

 $ make defconfig
$ make modules_prepare

All that’s left is to tell the build process to compile against that source tree.

Recall the salient part of your Makefile:

 ifeq ($(KERNELRELEASE),)

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

What that second line is doing is assigning the location of the kernel source tree to be used, provided it hasn’t been set yet. So, no problem — set it in your shell environment appropriately:

 $ export KERNELDIR=/home/rpjday/k/git

and this is where you start to notice a few differences. Assuming that my personal tree is version 2.6.31-rc1, here’s what happens on my system:

 $ make
make -C /home/rpjday/k/git M=/home/rpjday/lf/1b modules
make[1]: Entering directory `/home/rpjday/k/git'

WARNING: Symbol version dump /home/rpjday/k/git/Module.symvers
is missing; modules will have no dependencies and modversions.

Building with KERNELRELEASE = 2.6.31-rc1
CC [M] /home/rpjday/lf/1b/hi.o
Building modules, stage 2.
Building with KERNELRELEASE = 2.6.31-rc1
MODPOST 1 modules
CC /home/rpjday/lf/1b/hi.mod.o
LD [M] /home/rpjday/lf/1b/hi.ko
make[1]: Leaving directory `/home/rpjday/k/git'

Because I’m building against a source tree that does not match the currently running kernel, I lose the current symbol table, but the build still works.

The modinfo command also shows the tree against which the module was built:

 $ modinfo hi.ko
filename: hi.ko
flavour: Rocky Road
version: 3.14159
license: Dual BSD/GPL
description: You have to start somewhere.
author: Robert P. J. Day
srcversion: B6AADA40910DFF104897E21
depends:
vermagic: 2.6.31-rc1 SMP mod_unload

And then there’s the acid test — can I load it? Won’t the version mismatch cause problems? That depends on whether the currently running kernel was configured to allow version differences, and there’s one quick way to find out. On my Fedora 11 system:

 # insmod hi.ko
insmod: error inserting 'hi.ko': -1 Invalid module format

Oh, dear … apparently not. And tracking /var/log/messages would have made the problem more obvious:

 ...
localhost kernel: hi: version magic '2.6.31-rc1 SMP mod_unload '
should be '2.6.29.5-191.fc11.x86_64 SMP mod_unload '
...

In short, then, from here on, we’ll play it safe and just build against the matching kernel development tree. There’s no point making this any more difficult than it has to be.

Join us next time when we get into those module entry and exit routines.

[Editor’s Note: A Spanish version of this article is now available.]

Robert P. J. Day is a Linux consultant and long-time corporate trainer who lives in Waterloo, Ontario. He provides assistance to the Linux Foundation’s Training Program. Robert can be reached at
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
, and can be followed at http://twitter.com/rpjday.