B.2 Module Implementation

Modules are stored in the filesystem as ELF object files and are linked to the kernel by executing the insmod program (see the later section, Section B.3). For each module, the kernel allocates a memory area containing the following data:

·         A module object

·         A null-terminated string that represents the name of the module (all modules should have unique names)

·         The code that implements the functions of the module

The module object describes a module; its fields are shown in Table B-1. A simply linked list collects all module objects, where the next field of each object points to the next element in the list. The first element of the list is addressed by the module_list variable. But actually, the first element of the list is always the same: it is named kernel_module and refers to a fictitious module representing the statically linked kernel code.

Table B-1. The module object

Type

Name

Description

unsigned long

size_of_struct

Size of module object

struct module *

next

Next list element

const char *

name

Pointer to module name

unsigned long

size

Module size

atomic_t

uc.usecount

Module usage counter

unsigned long

flags

Module flags

unsigned int

nsyms

Number of exported symbols

unsigned int

ndeps

Number of referenced modules

struct module_symbol *

syms

Table of exported symbols

struct module_ref *

deps

List of referenced modules

struct module_ref *

refs

List of referencing modules

int (*)(void)

init

Initialization method

void (*)(void)

cleanup

Cleanup method

struct exception_table_entry *

ex_table_start

Start of exception table

struct exception_table_entry *

ex_table_end

End of exception table

struct module_persist *

persist_start

Start of area containing module's persistent data

struct module_persist *

persist_end

End of area containing module's persistent data

int (*)(void)

can_unload

Return 1 if the module is currently unused

int

runsize

Not used

char *

kallsyms_start

Start of area storing kernel symbols for debugging

char *

kallsyms_end

End of area storing kernel symbols for debugging

char *

archdata_start

Start of architecture-dependent data area

char *

archdata_end

End of architecture-dependent data area

char *

kernel_data

Not used

The total size of the memory area allocated for the module (including the module object and the module name) is contained in the size field.

As already mentioned in Section 9.2.6 in Chapter 9, each module has its own exception table. The table includes the addresses of the fixup code of the module, if any. The table is copied into RAM when the module is linked, and its starting and ending addresses are stored in the ex_table_start and ex_table_end fields of the module object.

The fields below ex_table_end were introduced in Linux 2.4 and implement some advanced features of modules. For instance, it is now possible to record in a disk file data that should be preserved across loading and unloading of a module. New module support also offers a lot of debugging data to kernel debuggers, so catching a bug hidden in the code of a module is now a lot easier.

B.2.1 Module Usage Counter

Each module has a usage counter, stored in the uc.usecount field of the corresponding module object. The counter is incremented when an operation involving the module's functions is started and decremented when the operation terminates. A module can be unlinked only if its usage counter is 0.

For example, suppose that the MS-DOS filesystem layer is compiled as a module and the module is linked at runtime. Initially, the module usage counter is 0. If the user mounts an MS-DOS floppy disk, the module usage counter is incremented by 1. Conversely, when the user unmounts the floppy disk, the counter is decremented by 1.

Besides this simple mechanism, Linux 2.4's modules may also define a custom function whose address is stored in the can_unload field of the module object. The function is invoked when the module is being unlinked; it should check whether it is really safe to unload the module, and return 0 or 1 accordingly. If the function returns 0, the unloading operation is aborted, much as if the usage counter were not equal to 0.

B.2.2 Exporting Symbols

When linking a module, all references to global kernel symbols (variables and functions) in the module's object code must be replaced with suitable addresses. This operation, which is very similar to that performed by the linker while compiling a User Mode program (see Section 20.1.3 in Chapter 20), is delegated to the insmod external program (described later in the section, Section B.3).

A special table is used by the kernel to store the symbols that can be accessed by modules together with their corresponding addresses. This kernel symbol table is contained in the _ _ksymtab section of the kernel code segment, and its starting and ending addresses are identified by two symbols produced by the C compiler: _ _start_ _ _ksymtab and _ _stop_ _ _ksymtab. The EXPORT_SYMBOL macro, when used inside the statically linked kernel code, forces the C compiler to add a specified symbol to the table.

Only the kernel symbols actually used by some existing module are included in the table. Should a system programmer need, within some module, to access a kernel symbol that is not already exported, he can simply add the corresponding EXPORT_SYMBOL macro into the kernel/ksyms.c file of the Linux source code.

Linked modules can also export their own symbols so that other modules can access them. The module symbol table is contained in the _ _ksymtab section of the module code segment. If the module source code includes the EXPORT_NO_SYMBOLS macro, symbols from that module are not added to the table. To export a subset of symbols from the module, the programmer must define the EXPORT_SYMTAB macro before including the include/linux/module.h header file. Then he may use the EXPORT_SYMBOL macro to export a specific symbol. If neither EXPORT_NO_SYMBOLS nor EXPORT_SYMTAB appears in the module source code, all nonstatic global symbols of the modules are exported.

The symbol table in the _ _ksymtab section is copied into a memory area when the module is linked, and the address of the area is stored in the syms field of the module object. The symbols exported by the statically linked kernel and all linked-in modules can be retrieved by reading the /proc/ksyms file or using the query_module( ) system call (described in the later section, Section B.3).

Recently, a new EXPORT_SYMBOL_GPL macro was added. It is functionally equivalent to EXPORT_SYMBOL, but it marks the exported symbol as usable only in modules licensed through either the General Public License (GPL) license or a compatible one. This way, the author of a kernel component may forbid using his work in binary-only modules that do not comply with the standard requirements of the GPL. The license of a module is specified by a MODULE_LICENSE macro inserted into its source code, whose argument is usually "GPL" or "Proprietary."

B.2.3 Module Dependency

A module (B) can refer to the symbols exported by another module (A); in this case, we say that B is loaded on top of A, or equivalently that A is used by B. To link module B, module A must have already been linked; otherwise, the references to the symbols exported by A cannot be properly linked in B. In short, there is a dependency between modules.

The deps field of the module object of B points to a list describing all modules that are used by B; in our example, A's module object would appear in that list. The ndeps field stores the number of modules used by B. Conversely, the refs field of A points to a list describing all modules that are loaded on top of A (thus, B's module object is included when it is loaded). The refs list must be updated dynamically whenever a module is loaded on top of A. To ensure that module A is not removed before B, A's usage counter is incremented for each module loaded on top of it.

Beside A and B there could be, of course, another module (C) loaded on top of B, and so on. Stacking modules is an effective way to modularize the kernel source code to speed up its development and improve its portability.