Driver porting: compiling external modules
| This article is part of the LWN Porting Drivers to 2.6 series. |
KERNELDIR = /usr/src/linux
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O
all: module.o
Real-world makefiles, of course, tended to be a bit more complicated, but the job of creating a loadable module was handled in a single, simple compilation step. All you really needed was a handy set of kernel headers to compile against.
With the 2.6 kernel, you still need those headers. You also, however, need a configured kernel source tree and a set of makefile rules describing how modules are built. There's a few reasons for this:
- The new module loader needs to have some extra symbols defined at
compilation time. Among other things, it needs to have the
KBUILD_BASENAME and KBUILD_MODNAME symbols defined.
- All loadable modules now need to go through a linking step - even those
which are built from a single source file. The link brings in
init/vermagic.o from the kernel source tree; this object
creates a special section in the loadable module describing the
environment in which it was built. It includes the compiler version
used, whether the kernel was built for SMP, whether kernel preemption
is enabled, the architecture which was compiled for, and, of course,
the kernel version. A difference in any of these parameters can
render a module incompatible with a given running kernel; rather than
fail in mysterious ways, the new module loader opts to detect these
compatibilities and refuse to load the module.
As of this writing (2.5.59), the "vermagic" scheme is fallible in that it assumes a match between the kernel's vermagic.o file and the way the module is being built. That will normally be the case, but people who change compiler versions or perform some sort of compilation trickery could get burned.
- The new symbol versioning scheme ("modversions") requires a separate post-compile processing step and yet another linkable object to hold the symbol checksums.
One could certainly, with some effort, write a new, standalone makefile which would handle the above issues. But that solution, along with being a pain, is also brittle; as soon as the module build process changes again, the makefile will break. Eventually that process will stabilize, but, for a while, further changes are almost guaranteed.
So, now that you are convinced that you want to use the kernel build system for external modules, how is that to be done? The first step is to learn how kernel makefiles work in general; makefiles.txt from a recent kernel's Documentation/kbuild directory is recommended reading. The makefile magic needed for a simple kernel module is minimal, however. In fact, for a single-file module, a single-line makefile will suffice:
obj-m := module.o(where module is replaced with the actual name of the resulting module, of course). The kernel build system, on seeing that declaration, will compile module.o from module.c, link it with vermagic.o, and leave the result in module.ko, which can then be loaded into the kernel.
A multi-file module is almost as easy:
obj-m := module.o module-objs := file1.o file2.oIn this case, file1.c and file2.c will be compiled, then linked into module.ko.
Of course, all this assumes that you can get the kernel build system to read and deal with your makefile. The magic command to make that happen is something like the following:
make -C /path/to/source SUBDIRS=$PWD modules
Where /path/to/source is the path to the source directory for the
(configured and built)
target kernel. This command causes make to head over to the kernel source
to find the top-level makefile; it then moves back to the original
directory to build the module of interest.
Of course, typing that command could get tiresome after a while. A trick posted by Gerd Knorr can make things a little easier, though. By looking for a symbol defined by the kernel build process, a makefile can determine whether it has been read directly, or by way of the kernel build system. So the following will build a module against the source for the currently running kernel:
ifneq ($(KERNELRELEASE),)
obj-m := module.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
endif
Now a simple "make" will suffice. The makefile will be read twice; the
first time it will simply invoke the kernel build system, while the actual
work will get done in the second pass. A makefile written in this way is
simple, and it should be robust with regard to kernel build changes.
