The Kernel Symbol Table

We’ve seen how insmod resolves undefined symbols against the table of public kernel symbols. The table contains the addresses of global kernel items—functions and variables—that are needed to implement modularized drivers. The public symbol table can be read in text form from the file /proc/ksyms (assuming, of course, that your kernel has support for the /proc filesystem—which it really should).

When a module is loaded, any symbol exported by the module becomes part of the kernel symbol table, and you can see it appear in /proc/ksyms or in the output of the ksyms command.

New modules can use symbols exported by your module, and you can stack new modules on top of other modules. Module stacking is implemented in the mainstream kernel sources as well: the msdos filesystem relies on symbols exported by the fat module, and each input USB device module stacks on the usbcore and input modules.

Module stacking is useful in complex projects. If a new abstraction is implemented in the form of a device driver, it might offer a plug for hardware-specific implementations. For example, the video-for-linux set of drivers is split into a generic module that exports symbols used by lower-level device drivers for specific hardware. According to your setup, you load the generic video module and the specific module for your installed hardware. Support for parallel ports and the wide variety of attachable devices is handled in the same way, as is the USB kernel subsystem. Stacking in the parallel port subsystem is shown in Figure 2-2; the arrows show the communications between the modules (with some example functions and data structures) and with the kernel programming interface.

Stacking of parallel port driver modules

Figure 2-2. Stacking of parallel port driver modules

When using stacked modules, it is helpful to be aware of the modprobe utility. modprobe functions in much the same way as insmod, but it also loads any other modules that are required by the module you want to load. Thus, one modprobe command can sometimes replace several invocations of insmod (although you’ll still need insmod when loading your own modules from the current directory, because modprobe only looks in the tree of installed modules).

Layered modularization can help reduce development time by simplifying each layer. This is similar to the separation between mechanism and policy that we discussed in Chapter 1.

In the usual case, a module implements its own functionality without the need to export any symbols at all. You will need to export symbols, however, whenever other modules may benefit from using them. You may also need to include specific instructions to avoid exporting all non-static symbols, as most versions (but not all) of modutils export all of them by default.

The Linux kernel header files provide a convenient way to manage the visibility of your symbols, thus reducing namespace pollution and promoting proper information hiding. The mechanism described in this section works with kernels 2.1.18 and later; the 2.0 kernel had a completely different mechanism, which is described at the end of the chapter.

If your module exports no symbols at all, you might want to make that explicit by placing a line with this macro call in your source file:

 EXPORT_NO_SYMBOLS;

The macro expands to an assembler directive and may appear anywhere within the module. Portable code, however, should place it within the module initialization function (init_module), because the version of this macro defined in sysdep.h for older kernels will work only there.

If, on the other hand, you need to export a subset of symbols from your module, the first step is defining the preprocessor macro EXPORT_SYMTAB. This macro must be defined before including module.h. It is common to define it at compile time with the -D compiler flag in Makefile.

If EXPORT_SYMTAB is defined, individual symbols are exported with a couple of macros:

 EXPORT_SYMBOL (name);
 EXPORT_SYMBOL_NOVERS (name);

Either version of the macro will make the given symbol available outside the module; the second version (EXPORT_SYMBOL_NOVERS) exports the symbol with no versioning information (described in Chapter 11). Symbols must be exported outside of any function because the macros expand to the declaration of a variable. (Interested readers can look at <linux/module.h> for the details, even though the details are not needed to make things work.)

Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.