We’ve seen how insmod resolves undefined symbols
against the table of
public kernel symbols. The table contains 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
.
When your module is loaded, any global symbol you declare 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 the ppp driver stacks on the header compression module.
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, a frame buffer video driver can export symbols to be used by a lower-level VGA driver. Each user loads the frame buffer module and the specific VGA module for his or her installed hardware.
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.
An alternative to exporting all the global symbols of your module is to use the function register_symtab, which is the official kernel interface to symbol-table management. The programming interface described here applies to the 1.2.13 and 2.0 kernels. See the section Section 17.1 in Chapter 17, for details about changes introduced in the 2.1 development kernels.
The function register_symtab,
as its name suggests, is used to register a whole
symbol table in the kernel’s main table. This technique is
somewhat cleaner than relying on static and global symbols, in that the
programmer centralizes the information about what is
made available to other modules and what isn’t. This is a better approach
than scattering static
declarations all over the source file.
If a module calls register_symtab from its initialization function, global symbols are no longer exported; only symbols that are listed in the explicit symbol table are exported to the kernel.
The advantage of declaring a centralized symbol table is most relevant when writing modules that span multiple source files. Many functions and variables can be left global so the source files can share the relevant information; the symbol table later selects what really needs to be exported for use by other modules.
Filling a symbol table structure is a tricky task, but kernel developers have written header files to simplify the task. The following lines of code show how a symbol table is declared and exported:
static struct symbol_table skull_syms = { #include <linux/symtab_begin.h> X(skull_fn1), X(skull_fn2), X(skull_variable), #include <linux/symtab_end.h> }; register_symtab(&skull_syms);
Interested readers can examine <linux/symtab_begin.h>
,
but it’s one of the most difficult headers in the kernel. It’s
not actually necessary to understand the X
macro to benefit
from its use.
register_symtab is able to override the static or global declaration of symbols because it is called after the module has been loaded into the running kernel. register_symtab replaces the public symbols exported by default for the current module with the explicit symbol table.
The override is possible because insmod hands the table of global symbols to the system call sys_init_module, which in turn registers the table before calling init_module. Any explicit call to register_symtab thus replaces the symbol table associated with the module.
If your module doesn’t need to export any symbols, and you don’t
want to declare everything as static
, just hide global symbols by
adding the following line to init_module. This call to
register_symtab simply
overwrites the module’s default symbol table with an empty one:
register_symtab(NULL);
If the source file does not offer hooks for additional modules to be stacked on it, it’s always a good idea to hide all the symbols by using the one-liner above.
When the module is unloaded from the kernel, any public symbol it declared is automatically discarded from the main symbol table. This applies to both global symbols and explicit symbol tables.
13.58.112.1