原文:(引用自: At 2012 Apr30)
Driver porting: compiling external modules
[Posted February 4, 2003 by corbet]
This article is part of the LWN Porting Drivers to 2.6 series.
The 2.5 development series saw extensive changes to the kernel build mechanism and the complete replacement of the module loading code. One result of
these changes is that compiling loadable modules has gotten a bit more complicated. In the 2.4 days, a makefile for an external module could be put
together in just about any old way; typically a form like the following was used:
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.o
In 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.
======================================================================
译文:
这篇文章是 “LWN移植驱动至2.6内核” 的一部分。
从2.5开始,内核构建机制有了很大的变化,模块加载部分的源代码也被完全重写。这些变化带来的一个结果是,编译一个可加载模块变得比以前更复杂点儿了。以前2.4的时候,一个外部模块的makefile差不多可以用传统方式没啥两样的方式编写;典型的样子就像下面这样:
KERNELDIR = /usr/src/linux
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O
all:module.o
当然实际上真正的makefile要比这个稍微复杂一些,但是制作一个可加载模块可以由一个简单、独立的步骤完成。所有真正依赖的仅仅是一些内核的头文件们,就是你想运行这个模块的那个内核的头文件。
如果是2.6内核,你依然需要这些头文件。除了这些,还有一些其他的,一个已配置的内核代码树和一套描述了modules是如何构建的makefile们。这是因为:
1. 新的内核模块加载器需要有额外的编译时定义的标签们。此外,它还要求有KBUILD_BASENAME 和 KBUILD_MODNAME 被定义。
2. 所有的可加载模块现在都需要走一个连接步骤 - 即使由一个不依赖其他任何东西的,可由一个单独文件构建的模块,也是一个样。和内核代码树中的init/vermagic.o连接,就可以在可加载的模块中产生一个特殊的部分,用来描述它构建时的目标内核环境的信息。它包含了编译器版本,是否内核支持SMP(译者注:对称多处理,一般指的是多内核和多CPU),是否启用了内核抢占机制,构建内核的目标硬件架构,当然还有内核版本。这些参数的任何不一致,将会导致模块与目标内核的不兼容;为了使这些不一致发生时不莫名其妙的死机,内核模块加载器就事先检查这些参数但不一致时就拒绝加载这个模块。
当写这篇文章的时候(2.5.59),“vermagic”机制很容易被触发,因为它需要任何一个模块与vermagic.o进行匹配检验。正常情况确实应该这样,但是当人们更换编译器版本或者进行一些编译欺骗技巧的操作时,这种策略会让你焦头烂额。
3. 新的标签版本机制(“modversions”)要求一个独立的编译完成后的步骤和额外的可连接文件来保持这些标签校验信息。
可能某些人正做着一些努力,写一个新的独立的makefile 来解决上面的问题。但是使用这种办法,不但会付出艰苦,而且这些makefile会相当脆弱。一旦模块构建过程发生了变化,这些makefile 就会报废。也许最终这个过程变得稳定下来,但是那只是一时的,将来对它的改进也几乎会是必然的。
那么现在你确信是如何使用内核构建系统构建外部模块的了吧?第一步就是学习如何在常规方式下使用makefile;推荐阅读一下Documentation/kbuild目录里的makefiles.txt。一个简单的模块至少需要一个makefile标记。实际上,对于一个单文件模块,使用一个单行的makefile就可以了:
obj-m := module.o
(当然这里的module要用实际的模块名称命名)。当内核构建系统碰到这句声明时,就会把module.c编译成module.o,之后把它与vermagic.o连接,然后最终结果生成module.ko,这就是可以被内核加载的模块了。对于一个多源文件文件组成的模块也很简单:
obj-m := module.o
module-objs := file1.o file2.o
在这种情况下,file1.c和file2.c将会被编译,然后连接成module.ko。
当然,所有的这些基于你可以使内核构建系统读和处理你的makefile。使用这个神奇的命令就可以做到这点:
make -C /path/to/source SUBDIRS=$PWD modules
/path/to/source就是(已配置的和已构建的)目标内核的源代码树的目录。这个命令会导致make去内核代码树目录去寻找makefile作为顶层的makefile;之后他回到原来的目录来构建我们的模块。
当然,敲那个命令可能会感到麻烦一些。一个由Gerd Knorr发明的技巧可使它简单点。那就是靠查找一个在内核构建过程中定义的标签,makefile可以判断是否它被直接读取,或者由内核构建系统读取。下面的makefile写法将会构建一个当前运行内核的模块:
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
这样一个简单的make命令就足够了。这个mafefile将会被读两次;第一次会简单地调用内核构建系统,而实际的工作将会在第二次做。这样写的makefile很简单,而且对于内核构建变化来说也是可以正确适应的(译者注:当内核构建变化时,运行这个makefile会首先调用内核构建机制构建好变化后的内核,完后再构建模块,这样前后就会保持一致)。