How Linux Handles Module Dependencies with modules.dep

The Linux operating system maintains a modular architecture that allows device drivers and kernel extensions to load and unload on demand without rebooting the system. When modules rely on symbols and functions exported by other modules, the system prevents runtime errors by resolving these interdependencies automatically. This article examines how Linux maps, organizes, and resolves these relationships using the modules.dep file and related user-space tools like depmod and modprobe.

The Purpose of modules.dep

The Linux kernel does not contain native, high-level graph-traversal logic to hunt down missing dependencies on disk; that responsibility belongs to user space. To make dependency resolution fast and efficient, the operating system precomputes a static mapping of all module relationships and stores it in a file called modules.dep.

Located in /lib/modules/$(uname -r)/modules.dep, this plain-text file lists every compiled kernel module for the current kernel release along with the exact paths of the modules it requires. Modern Linux distributions also generate an optimized binary equivalent, modules.dep.bin, which allows tools to perform fast lookups without parsing large text files.

How modules.dep Is Generated with depmod

The generation of modules.dep is handled by the depmod utility, typically executed during system installation, kernel updates, or manual driver compilation.

  1. Symbol Inspection: Kernel modules are ELF (Executable and Linkable Format) binaries. When depmod runs, it scans all .ko (kernel object) files under /lib/modules/$(uname -r)/. It inspects the ELF symbol tables of each module, identifying:

    • Provided symbols: Functions and variables exposed via macros like EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL().
    • Required symbols: External functions and variables referenced by the module but not defined internally.
  2. Graph Construction: depmod cross-references the required symbols of each module with the provided symbols of all other available modules.

  3. Output Generation: Once the dependency graph is established, depmod writes the output to modules.dep.

The format of modules.dep follows a straightforward layout:

kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko: kernel/drivers/net/mdio.ko kernel/net/core/ptp.ko

In this example, the e1000e.ko driver requires both mdio.ko and ptp.ko. The target module appears first, followed by a colon and a space-separated list of its direct and indirect dependencies.

Dependency Resolution at Runtime: modprobe vs. insmod

Linux provides two primary tools for inserting modules into the kernel: insmod and modprobe. Understanding the difference highlights why modules.dep is essential.

insmod

The insmod command is a low-level utility that instructs the kernel directly to load a specific module file into memory using the init_module or finit_module system calls. It does not read configuration files or dependency lists. If you attempt to load a module with unmet dependencies using insmod, the kernel will reject the request with an unresolved symbol error (such as Unknown symbol in module).

modprobe

The standard, intelligent utility for module management is modprobe. When requested to load a module (for example, modprobe e1000e), modprobe performs the following steps:

  1. Consults modules.dep.bin (or modules.dep): It searches for the requested module name to locate its file path and its declared dependencies.
  2. Builds an Execution Order: It constructs a topological sort of the required modules, ensuring that base dependencies load before the modules that rely on them.
  3. Loads Modules in Sequence: modprobe invokes the kernel module loading system calls for each dependency in order, ending with the requested module.
  4. Applies Configurations: It accounts for module aliases, blacklists, and custom parameters defined in /etc/modprobe.d/.

Unloading Dependent Modules

modules.dep also assists when removing modules. When you execute modprobe -r <module_name>, the utility unloads the specified module and evaluates the dependency chain in reverse. If any supporting modules were loaded exclusively to satisfy the removed module and are no longer in use by any other running component, modprobe safely unloads them as well, reclaiming system memory.