L27: Linux 6.x Kernel Modules — Installation and Management

 

Linux 6.x Kernel Modules — Installation and Management

Lecture 27  |  Step 5: make modules_install, depmod and the Module System  |  Free Linux Kernel Development Course

🐧 Linux 6.x
⚙ Step 5 of 7
👍 Free Course
🎯 Intermediate

What You Will Learn

make modules_install
/lib/modules layout
depmod
modules.dep
modprobe vs insmod
lsmod and rmmod
modinfo
INSTALL_MOD_PATH
LOCALVERSION safety
zstd .ko.zst

Where Are We?

In Lecture 26 you ran make -j$(nproc) and produced bzImage plus hundreds of .ko.zst module files scattered throughout the kernel source tree. Those files are in the wrong place — the kernel and tools like modprobe cannot find them there. Step 5 installs them to the correct system location and builds the dependency database that makes module loading work.

Step 5 — Installing Kernel Modules

After a successful build, one command installs all modules and runs depmod. It must be run as root because it writes to a system directory.

sudo make modules_install

What make modules_install Does — Two Automatic Steps

Step A — Copy All .ko.zst Files to /lib/modules/

Every compiled module file is copied from the scattered locations inside the kernel source tree to a single organised directory: /lib/modules/<kernel-version>/kernel/. The directory structure mirrors the source tree — drivers/, fs/, net/, sound/ etc.

Copies .ko.zst files Preserves subsystem structure Requires root

Step B — Runs depmod -a Automatically

After copying, depmod -a is called. It reads every module file, analyses symbol dependencies, and writes the dependency database (modules.dep and related files). This is what allows modprobe to load dependencies in the correct order automatically.

Builds dependency graph Creates modules.dep Runs automatically

The /lib/modules Directory Layout

Before running make modules_install, check your exact kernel version string so you know what directory name to expect:

make kernelrelease

/lib/modules/6.6.30-mykernel/ — What Gets Created

kernel/ — All Module Files

Mirrors the kernel source structure. Examples of what you find inside:

kernel/drivers/net/ethernet/intel/igb/igb.ko.zst
kernel/drivers/net/wireless/intel/iwlwifi/iwlwifi.ko.zst
kernel/fs/ext4/ext4.ko.zst
kernel/sound/pci/hda/snd-hda-intel.ko.zst

Dependency and Alias Database Files

Generated by depmod. These are what modprobe reads to find and load modules correctly.

modules.dep — text dependency graph
modules.dep.bin — binary version, faster lookup
modules.alias — hardware ID to module name
modules.builtin — modules compiled into kernel
modules.symbols — symbol to module mapping

💡 Linux 6.x Change — .ko.zst Extension

All installed module files in Linux 6.x carry the .ko.zst extension because they are compressed with zstd. The kernel, modprobe, insmod, and depmod all handle decompression transparently. You never need to decompress them manually before loading. If you see .ko.xz files, those are from kernels using xz compression (some distros). The default in mainline 6.x is zstd.

Understanding depmod — The Dependency Builder

When you install modules, make modules_install calls depmod -a automatically. You can also run it manually any time you add new module files:

sudo depmod -a

depmod reads every .ko.zst file, checks what ELF symbols each module exports and what symbols it requires from others, and builds a complete dependency graph saved as modules.dep.

How depmod Resolves Module Dependencies — Wi-Fi Example

iwlwifi.ko.zst — Intel Wi-Fi Driver

Requires symbols from: cfg80211 and mac80211. Cannot load without both of those being in the kernel first.

Depends on mac80211 Depends on cfg80211

mac80211.ko.zst — 802.11 MAC Layer

Requires symbols from: cfg80211. Must be loaded after cfg80211 but before iwlwifi.

Depends on cfg80211 Provides symbols for iwlwifi

cfg80211.ko.zst — 802.11 Configuration Framework

The base module. Only depends on built-in kernel symbols. Must be loaded first of the three.

No module dependencies Must load first

Result Written to modules.dep

depmod records: iwlwifi.ko.zst: mac80211.ko.zst cfg80211.ko.zst. When you now run modprobe iwlwifi, it reads this entry and loads cfg80211, then mac80211, then iwlwifi — automatically, in the correct order, with no intervention from you.

modprobe reads this Automatic ordering

modprobe vs insmod — Know the Difference

modprobe vs insmod — When to Use Each

modprobe — Use This in Practice ✅

Reads modules.dep and loads all required dependencies first, then loads the requested module. You specify the module by name. Works from any directory. This is the right tool for almost every situation.

sudo modprobe iwlwifi
# Automatically loads: cfg80211 -> mac80211 -> iwlwifi
Resolves dependencies Module name only Standard practice

insmod — Low Level, No Dependency Resolution ⚠

Loads a single .ko.zst file from a full path you provide. No dependency checking. If required symbols are not already loaded, insmod fails with “Unknown symbol” errors. Useful in driver development when you know exactly what you are doing.

sudo insmod /lib/modules/6.6.30-mykernel/kernel/net/wireless/cfg80211.ko.zst
No dependency resolution Full path required Development use

The Full Module Command Toolkit

# Load a module with automatic dependency resolution
sudo modprobe module_name

# Load with a parameter
sudo modprobe igb max_vfs=4

# List all currently loaded modules
lsmod

# Show detailed information about a module
modinfo module_name

# Unload a module (fails if something is using it)
sudo modprobe -r module_name
# Same as:
sudo rmmod module_name

Sample output of lsmod:

Module                  Size  Used by
iwlwifi               487424  1 iwlmvm
mac80211              892928  3 iwlmvm,iwlwifi,ath9k
cfg80211              983040  4 mac80211,iwlwifi,ath9k,ath

The columns mean: module name, bytes used in RAM, number of users (processes or modules), and the list of other modules that are using (depending on) this one. A module with Used by count of 0 can be safely unloaded.

Sample output of modinfo igb:

filename:       /lib/modules/6.6.30/kernel/drivers/net/ethernet/intel/igb/igb.ko.zst
version:        5.15.2-k
description:    Intel(R) Gigabit Ethernet Network Driver
author:         Intel Corporation
license:        GPL v2
depends:        i2c-core,ptp,i2c-algo-bit
parm:           max_vfs:Maximum number of virtual functions (uint)

Cross-Compilation — Installing Modules to a Custom Path

When building a kernel for an embedded target, you must not install modules into your host machine’s /lib/modules/. Use the INSTALL_MOD_PATH variable to redirect installation to a staging directory:

sudo make INSTALL_MOD_PATH=/path/to/staging/rootfs modules_install

Cross-Compilation Module Install Workflow — ARM Target

Step 1 — Cross-compile on your x86_64 development PC

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

Produces arch/arm64/boot/Image and *.ko.zst module files for ARM64.

Step 2 — Install modules to a staging directory (NOT your host /lib/modules/)

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  INSTALL_MOD_PATH=./staging modules_install

Creates ./staging/lib/modules/6.6.30-mykernel/ with ARM64 module files.

Step 3 — Deploy to the target board

Copy staging/lib/modules/ to the target board’s root filesystem via SD card, SCP, TFTP, or NFS. Also copy arch/arm64/boot/Image to the board’s boot partition.

SD card SCP over network NFS mount

LOCALVERSION — Protecting Your Running System

If you build a kernel with the same version string as your currently running kernel, make modules_install will silently overwrite the existing modules in /lib/modules/. The old kernel running in RAM may then fail to load modules because the new files do not match the running kernel’s expected ABI.

The fix is simple — always set CONFIG_LOCALVERSION before building:

# In .config:
CONFIG_LOCALVERSION="-mykernel"

# Your kernel version becomes: 6.6.30-mykernel
# Modules install to: /lib/modules/6.6.30-mykernel/   <-- safe, new directory
# Existing modules stay in: /lib/modules/6.6.30/       <-- untouched

LOCALVERSION Safety — Without vs With

❌ Without LOCALVERSION — Dangerous

Your build version string: 6.6.30. Running system kernel: 6.6.30. Running make modules_install overwrites /lib/modules/6.6.30/ with your new modules. The currently running kernel may then fail to load any module — drivers stop working without a reboot, and if a critical module fails, the system may become unstable.

✓ With CONFIG_LOCALVERSION=”-mykernel” — Safe

Your build version string: 6.6.30-mykernel. Modules install to /lib/modules/6.6.30-mykernel/ — a completely separate directory. The running system’s /lib/modules/6.6.30/ is untouched. No risk of breaking the currently booted system.

⚠ Golden Rule for Custom Kernel Development

Always set a unique LOCALVERSION string before compiling and installing a custom kernel. This single habit prevents a large class of hard-to-debug module mismatch problems and protects your production system from accidental breakage.

Verifying the Module Installation

# Check the new module directory exists
ls /lib/modules/

# Count the installed modules
find /lib/modules/6.6.30-mykernel/kernel/ -name "*.ko*" | wc -l

# Check the first few lines of modules.dep
head -5 /lib/modules/6.6.30-mykernel/modules.dep

# Query a specific module without it being loaded
modinfo -k 6.6.30-mykernel igb

📷 Suggested Featured Image

A terminal screenshot showing the output of ls /lib/modules/ with your new custom kernel version directory visible alongside the system kernel directory is ideal. It shows that the modules installed to a separate location and did not overwrite the existing system modules — proving the LOCALVERSION safety point made in this lecture.

Frequently Asked Questions

Q: Why does make modules_install need sudo?

Writing to /lib/modules/ requires root privileges because it is a system directory that affects every user and the boot process. Kernel modules run in kernel space with full hardware access — allowing any user to install kernel modules would be a serious security vulnerability.

Q: What happens if I skip depmod after installing new modules?

Without an up-to-date modules.dep, modprobe cannot find new modules or resolve their dependencies. Trying to load a new module with modprobe fails with “Module not found”. make modules_install runs depmod automatically so you do not have to think about it. If you ever manually copy a .ko file to the modules directory, you must run sudo depmod -a afterwards to update the database.

Q: Can I load a module built for one kernel into a different running kernel?

No. Every module contains a version magic string encoded at build time. This string includes the exact kernel version and key configuration options. When you try to load a module, the kernel compares this string against its own version magic. If they do not match exactly, the load fails with “version magic mismatch”. This is a deliberate safety check — modules compiled for a different kernel version may have incompatible data structures and would crash the kernel if loaded.

Interview Questions

Q1. What does make modules_install do and why is it a separate step from make?

The main make step compiles .ko.zst files into the kernel source tree. make modules_install is separate because it copies those files to a system location (/lib/modules/<version>/) and runs depmod to build the dependency database. Separating the steps allows you to cross-compile once and then install to different destinations: use INSTALL_MOD_PATH to redirect installation to a staging directory for an embedded target instead of the host’s /lib/modules/.

Q2. Explain the purpose of depmod and the files it generates.

depmod analyses all .ko.zst files in /lib/modules/<version>/, determines their ELF symbol dependencies, and builds a dependency database. Key output files: modules.dep (text dependency graph that modprobe reads), modules.dep.bin (binary version for fast lookup), modules.alias (maps hardware IDs like USB vendor:product codes to module names, enabling udev to auto-load the correct driver), and modules.symbols (maps exported kernel symbols to the module that provides them).

Q3. What is the difference between modprobe and insmod?

insmod loads a single kernel module from a direct file path you provide. It performs zero dependency resolution — if the module requires symbols from another module that is not yet loaded, insmod fails with “Unknown symbol” errors. modprobe is a higher-level tool that reads modules.dep, determines all required dependencies, and loads them in the correct order before loading the requested module. In everyday use always prefer modprobe. Use insmod only in driver development when you need explicit low-level control over exactly which file gets loaded.

Q4. How do you install modules for a cross-compiled kernel and why is INSTALL_MOD_PATH important?

Use make ARCH=<target> CROSS_COMPILE=<prefix>- INSTALL_MOD_PATH=/path/to/staging modules_install. Without INSTALL_MOD_PATH, the command installs to the host’s /lib/modules/. That is wrong for two reasons: the host kernel version is different, and ARM64 .ko.zst files would be copied onto an x86_64 host where they are unusable. INSTALL_MOD_PATH redirects installation to a staging root filesystem that you then deploy to the embedded target via SD card, TFTP, or SCP.

Q5. What is LOCALVERSION and why must you always set it during kernel development?

CONFIG_LOCALVERSION appends a suffix to the kernel version string. Setting it to -mykernel changes the version from 6.6.30 to 6.6.30-mykernel, so modules install to a new directory /lib/modules/6.6.30-mykernel/ that does not conflict with the system kernel’s /lib/modules/6.6.30/. Without it, make modules_install overwrites the system kernel’s modules while the old kernel is still running in RAM — the modules no longer match the running kernel and driver loading breaks.

Q6. What does lsmod output tell you and how do you interpret the Used by column?

lsmod reads /proc/modules and lists all currently loaded kernel modules. Each row shows: module name, current memory size in bytes, use count, and the names of modules that depend on this one. The use count has two components: the number of external references (other modules) plus the number of open file handles to devices provided by this module. A total use count of 0 means the module is safe to unload with modprobe -r. A non-zero count means something is actively using the module and unloading will fail with “Module is in use”.

Q7. What is version magic in kernel modules and what does it contain?

Version magic is a string embedded in every compiled kernel module at build time. It encodes: the exact kernel version string, the compiler version used, key configuration options (such as whether SMP is enabled, the preemption model, and kernel page table isolation settings). When you load a module, the kernel’s module loader compares the module’s version magic against its own. Even a single character difference causes the load to be rejected. This prevents loading modules compiled for a different kernel configuration, which could corrupt internal data structures and crash the system.

Continue the Free Linux Kernel Development Course

Next we generate the initramfs image and configure GRUB to boot into your custom kernel for the very first time.

← PREV_LEC
NEXT_LEC →

 

Leave a Reply

Your email address will not be published. Required fields are marked *