Module Init and Initcalls
How kernel modules and built-in subsystems initialize
module_init() and module_exit()
Every kernel module or built-in subsystem declares its initialization and cleanup functions using two macros:
#include <linux/module.h>
#include <linux/init.h>
static int __init mydriver_init(void)
{
pr_info("mydriver: initializing\n");
/* ... register device, allocate resources ... */
return 0; /* 0 = success; negative errno = failure */
}
static void __exit mydriver_exit(void)
{
pr_info("mydriver: exiting\n");
/* ... release resources ... */
}
module_init(mydriver_init);
module_exit(mydriver_exit);
What these macros expand to
For a loadable module (.ko file), module_init() creates an alias:
/* include/linux/module.h (loadable module case) */
#define module_init(initfn) \
static inline initcall_t __maybe_unused \
__inittest(void) { return initfn; } \
int init_module(void) __copy(initfn) \
__attribute__((alias(#initfn)));
The resulting init_module symbol is what the kernel's module loader calls after loading the .ko.
For a built-in module (y in Kconfig), module_init() becomes:
which places the function pointer in the .initcall6.init ELF section, called by do_initcalls() during boot. See Early Boot and start_kernel() for the full initcall level table.
module_exit() for built-in code expands to nothing — built-in code can never be unloaded, so the exit function is not needed (and is discarded by the linker from .exit.text).
struct module
Every loaded module is represented by a struct module allocated in module memory:
/* include/linux/module.h (simplified) */
struct module {
enum module_state state; /* COMING, LIVE, GOING, UNFORMED */
struct list_head list; /* linked into modules list */
char name[MODULE_NAME_LEN]; /* module name (e.g., "e1000e") */
/* Exported symbols (GPL and non-GPL share one unified table;
* each entry's license is carried in flagstab, not a separate array) */
const struct kernel_symbol *syms;
const u32 *crcs;
const u8 *flagstab;
unsigned int num_syms;
/* Init and exit functions */
int (*init)(void);
void (*exit)(void);
/* Reference counting */
atomic_t refcnt;
/* Module dependencies */
struct list_head source_list; /* modules that use this one (dependents) */
struct list_head target_list; /* modules this one depends on */
/* Source section information (for /sys/module/<name>/sections/) */
struct module_sect_attrs *sect_attrs;
/* Parameters (for /sys/module/<name>/parameters/) */
struct kernel_param *kp;
unsigned int num_kp;
/* Build ID */
unsigned char build_id[BUILD_ID_SIZE_MAX];
};
Module states (enum module_state):
| State | Meaning |
|---|---|
MODULE_STATE_LIVE |
Fully loaded and operational |
MODULE_STATE_COMING |
Being loaded, init() not yet called |
MODULE_STATE_GOING |
Being unloaded, exit() not yet called |
MODULE_STATE_UNFORMED |
Partially constructed (early allocations done) |
Module loading flow
The kernel exposes two syscalls for loading modules:
init_module(buf, len, params)— loads a module image from a buffer in userspace memoryfinit_module(fd, params, flags)— loads a module from an already-open file descriptor rather than a userspace buffer (preferred since 3.8, especially by container runtimes and systemd); both syscalls funnel into the sameload_module(), somodule_sig_check()runs identically either way —finit_moduledoesn't change signature handling, it changes how the module image gets to the kernel
insmod uses init_module; modprobe uses finit_module.
load_module(): step by step
load_module() in kernel/module/main.c does the heavy lifting:
load_module()
1. copy_module_from_user() / copy_module_from_fd()
- Read the ELF image from userspace
2. elf_validity_check()
- Verify ELF magic, architecture, section headers
3. module_sig_check() [if CONFIG_MODULE_SIG]
- Verify PKCS#7 signature against built-in public key
- Fail if CONFIG_MODULE_SIG_FORCE and signature invalid
4. Allocate struct module
- layout_and_allocate(): compute section sizes
- module_alloc(): allocate memory in module space
(vmalloc region, or close to kernel text for 32-bit)
5. Apply ELF relocations
- apply_relocations(): resolve symbol references
- Symbols resolved against:
a. __ksymtab (unified table; each entry carries its own
GPL/non-GPL license string, not a separate table)
b. Other loaded modules' exported symbols
6. module_finalize()
- Set up alternatives (CPU feature patching)
- Set up jump labels
- Set up ORC unwind info
7. do_init_module()
- Set state = MODULE_STATE_COMING
- call module->init() <-- driver's __init function runs
- If init() returns 0: state = MODULE_STATE_LIVE
- If init() returns non-zero: module loading fails,
module freed, error returned to userspace
8. add_unformed_module() / add_module_to_list()
- Module appears in /proc/modules
- Module directory created in /sys/module/
Module memory layout
Module code is loaded into a special virtual address range distinct from the main kernel text. On x86-64 this is in the range 0xffffffffc0000000 and above (the module space). The separation ensures that a module cannot be confused with core kernel code in stack traces and makes it easier to deallocate module memory on unload.
Module unloading
rmmod mydriver
→ delete_module("mydriver", flags) syscall
→ kernel/module/main.c: free_module()
1. Check refcnt: module_refcount() must be 0
- If non-zero: EWOULDBLOCK
- --force flag bypasses this (dangerous)
2. set state = MODULE_STATE_GOING
3. Call module->exit() if it exists
4. Remove from modules list and /proc/modules
5. Remove from /sys/module/
6. Synchronize with RCU (rcu_barrier())
- Ensures all in-flight RCU callbacks that reference
the module's code have completed
7. module_free(): return memory to vmalloc allocator
Modules cannot be unloaded while any code is executing in them (tracked by refcnt) or while any other module depends on them (tracked by the source_list).
Symbol export
Only explicitly exported symbols are accessible to loadable modules. Both macros funnel into the same underlying __EXPORT_SYMBOL(), differing only in the license string each entry carries:
/* include/linux/export.h */
#define _EXPORT_SYMBOL(sym, license) __EXPORT_SYMBOL(sym, license, DEFAULT_SYMBOL_NAMESPACE)
#define EXPORT_SYMBOL(sym) _EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "GPL")
Each export creates a struct kernel_symbol in a single unified __ksymtab section (kernel/module/internal.h):
struct kernel_symbol {
int value_offset; /* offset from this struct to the symbol */
int name_offset; /* offset from this struct to the name string */
int namespace_offset;
};
The license string itself lives in the module's flagstab array (see struct module above), not a separate symbol table — there is no __ksymtab_gpl. During module loading, the relocation step resolves undefined symbols by searching __ksymtab (and the __ksymtab of already-loaded modules). If a resolved symbol's license is GPL-only and the loading module's MODULE_LICENSE is not GPL-compatible, the load fails:
From userspace:
# List all exported kernel symbols:
cat /proc/kallsyms | grep ' T ' # T = global text symbol
# List symbols exported by a specific module:
cat /proc/kallsyms | grep '\[mydriver\]'
# List what symbols a .ko exports:
nm mydriver.ko | grep '__ksymtab'
# List what symbols a .ko imports (needs from other modules):
modinfo mydriver.ko
# or
nm mydriver.ko | grep ' U ' # U = undefined (imported)
Module parameters during load
/* In the module source: */
static int timeout_ms = 100;
module_param(timeout_ms, int, 0644);
MODULE_PARM_DESC(timeout_ms, "Timeout in milliseconds (default 100)");
static char *device_name = "default";
module_param(device_name, charp, 0444);
MODULE_PARM_DESC(device_name, "Target device name");
# Pass parameters at load time:
modprobe mydriver timeout_ms=500 device_name=eth0
insmod mydriver.ko timeout_ms=500
# Read/write parameters at runtime (if permissions allow):
cat /sys/module/mydriver/parameters/timeout_ms
echo 200 > /sys/module/mydriver/parameters/timeout_ms
The module_param() macro registers a struct kernel_param in the .data..param section. load_module() calls parse_args() on the parameter string passed to finit_module() before calling init(), so parameters are set before the driver initializes.
Module dependencies
modprobe (from the kmod package) handles dependency resolution. The dependency database is built by depmod:
# Rebuild module dependency database after installing new modules:
depmod -a
# The result:
cat /lib/modules/$(uname -r)/modules.dep
# drivers/net/ethernet/intel/e1000e/e1000e.ko.xz: \
# kernel/drivers/pci/pci.ko.xz
depmod reads each .ko file's undefined symbols and cross-references them against the exported symbols of other modules, producing a directed dependency graph. modprobe traverses this graph to ensure prerequisites are loaded first.
# Show dependencies of a module:
modinfo e1000e | grep depends
# Load a module and all its dependencies:
modprobe e1000e
# Remove a module and modules that depend on it (if not in use):
modprobe -r e1000e
/sys/module/ and /proc/modules
# List all loaded modules with size and reference count:
cat /proc/modules
# e1000e 290816 0 - Live 0xffffffffc0234000
# Format: name size refcount dependencies state memaddr
# Or with lsmod (prettier):
lsmod
# Detailed module information:
ls /sys/module/e1000e/
# coresize initsize initstate notes parameters refcnt sections srcversion taint
cat /sys/module/e1000e/initstate
# live
# Module parameters:
ls /sys/module/e1000e/parameters/
cat /sys/module/e1000e/parameters/debug
# Section addresses (useful for debugging):
cat /sys/module/e1000e/sections/.text
Module signing
CONFIG_MODULE_SIG enables cryptographic signature verification on module load. The kernel embeds a public key; modules are signed with the corresponding private key during the build.
# Sign a module manually (key must match kernel's built-in public key):
scripts/sign-file sha256 signing_key.pem signing_key.x509 mydriver.ko
# Check if a module is signed:
modinfo mydriver.ko | grep sig
# Verify signature:
openssl cms -verify -inform DER -in mydriver.ko ...
With CONFIG_MODULE_SIG_FORCE, any unsigned or incorrectly signed module is rejected. With CONFIG_MODULE_SIG but not _FORCE, unsigned modules are accepted with a taint flag:
The module signing infrastructure uses PKCS#7 signatures appended to the .ko file (after the ELF data).
Initcall debugging
# Boot with initcall_debug to see every initcall and its duration:
# Add to kernel command line:
initcall_debug
# In dmesg:
# calling e1000e_init_module+0x0/0x3c [e1000e] @ 3
# initcall e1000e_init_module+0x0/0x3c [e1000e] returned 0 after 1243 usecs
# The "@ N" is the CPU number; this helps identify parallelism issues.
# Find which initcall level a function is at:
grep '__initcall_' /proc/kallsyms | grep 'mydriver'
# Trace the module load path:
bpftrace -e '
kprobe:do_init_module {
printf("loading module: %s\n",
str(((struct module *)arg0)->name));
}'
Further reading
Kernel source
- include/linux/module.h —
module_init()/module_exit()macros and thestruct moduledefinition - include/linux/init.h — the initcall level macros (
early_initcall()throughlate_initcall()) and__define_initcall() - kernel/module/main.c —
load_module(),do_init_module(),free_module() - include/linux/export.h —
EXPORT_SYMBOL()andEXPORT_SYMBOL_GPL() - kernel/module/internal.h — the current
struct kernel_symboldefinition and the__ksymtabboundary symbols
Man pages
init_module(2)— loads a module from a userspace bufferfinit_module(2)— loads a module from a file descriptor; added in Linux 3.8delete_module(2)— the syscall behindrmmodmodprobe(8)— dependency-aware module loading/removaldepmod(8)— buildsmodules.depfrom each module's undefined symbols
Related pages
- Early Boot and start_kernel() — the initcall levels and
do_initcalls() - Kernel Modules — writing and building out-of-tree modules
- Module Signing — key generation and signing workflow
- Parameters, Symbols, and Kconfig —
module_param()in depth
LWN articles
- LWN: Enforcement (or not) for module-specific exported symbols — the debate over restricting exports to a named set of in-tree modules; the macro discussed was proposed as
EXPORT_SYMBOL_GPL_FOR_MODULES()and merged asEXPORT_SYMBOL_FOR_MODULES(), dropping theGPL_(see the Kernel source entry above) - LWN: The proper use of EXPORT_SYMBOL_GPL() — how maintainers decide when an exported symbol should be GPL-only
External
- Kernel module signing facility —
CONFIG_MODULE_SIG, key generation, andscripts/sign-file - Building External Modules — kbuild mechanics for out-of-tree modules
- Symbol Namespaces —
EXPORT_SYMBOL_NS()for namespaced exports