Kernel Modules in the Source Tree-Free Linux kernel development course

Previous Lecture
Next Lecture

Linux Kernel Programming
Chapter 4 — Part 2: Kernel Modules in the Source Tree
📚 Free Course
💻 Kernel 6.x Updated
✅ Interview Q&A Included

Topics Covered:

Module types: =y, =m, =n
menuconfig M option
/lib/modules directory
lsmod command
modinfo command
Built-in vs Loadable
Module dependencies

Where Do All Those .ko Files Come From?

When you install Linux on a machine, thousands of kernel modules are already present on your system — long before you ever write a single line of driver code. These modules handle everything from USB keyboards to Wi-Fi adapters to GPU memory management. They are maintained as part of the official Linux kernel source tree itself.

In this tutorial, we explore how these in-tree modules differ from the out-of-tree modules you write yourself, how the kernel build system handles them, and the practical tools you use to inspect and manage modules on a live system.

Three Ways a Feature Can Exist in the Kernel

When you configure a Linux kernel using make menuconfig, every configurable feature presents you with three choices. Understanding this tristate is fundamental to kernel module development.

Kernel Configuration: The Three-State (Tristate) Option
Symbol In .config What It Means Result
[*] CONFIG_XXX=y Feature compiled into the kernel Always available, baked into vmlinuz. No .ko file.
[M] CONFIG_XXX=m Feature compiled as a loadable module Creates a .ko file. Loaded on demand.
[ ] # CONFIG_XXX is not set Feature disabled entirely Neither built-in nor module. Not available.

This choice happens when you run make menuconfig (or make xconfig) before building the kernel. The result is saved in a file called .config in the kernel source root. The kernel build system reads this file and decides what to compile into the image and what to build as a .ko.

# Example lines from a .config file
CONFIG_USB_SERIAL=m          # USB serial driver → built as module
CONFIG_EXT4_FS=y             # ext4 filesystem → built into kernel
# CONFIG_NFS_V4 is not set   # NFSv4 → disabled entirely

Where Do In-Tree Modules Live After Building?

After you build a kernel and run make modules_install, all the .ko files get placed in a directory structure under /lib/modules/. Each kernel version gets its own subdirectory named after the version string returned by uname -r.

# See all kernel versions that have modules installed
$ ls /lib/modules/
6.8.0-45-generic    6.8.0-47-generic

# Explore the directory structure for the running kernel
$ ls /lib/modules/$(uname -r)/
build    kernel    modules.alias    modules.builtin
modules.dep    modules.devname    modules.order    modules.symbols    source

# The actual .ko files are organized by subsystem inside the 'kernel' directory
$ ls /lib/modules/$(uname -r)/kernel/
arch    block    crypto    drivers    fs    lib    mm    net    sound

# Count all modules for the running kernel
$ find /lib/modules/$(uname -r)/ -name "*.ko*" | wc -l

On a modern Ubuntu 24.04 or Fedora 40 system with kernel 6.8 or 6.9, you will typically find between 5,000 and 7,000 kernel modules. This large count makes sense — Linux supports an enormous range of hardware across servers, desktops, embedded boards, and everything in between. Distributors configure the kernel to support as many devices as possible, but compile uncommon drivers as modules so they do not bloat the base image.

The modules.dep File — Tracking Dependencies

Modules often depend on other modules. For example, a USB Wi-Fi driver might depend on the USB core module and the wireless stack module. The kernel uses a dependency file called modules.dep to track these relationships. modprobe reads this file to know what to load first.

# modules.dep shows each module and what it depends on
# Format: module_path: dependency1_path dependency2_path ...
$ cat /lib/modules/$(uname -r)/modules.dep | grep "iwlwifi" | head -3

# After building your own kernel, always run this to regenerate the dep file:
$ sudo depmod -a

Practical Tool: lsmod — See What Is Currently Loaded

lsmod shows you all kernel modules that are currently loaded in memory. It reads from the kernel’s internal module list and formats it nicely.

$ lsmod
Module                  Size  Used by
nvidia_uvm           1413120  0
nvidia_drm             98304  4
nvidia              56893440  282 nvidia_uvm,nvidia_drm
drm_kms_helper        262144  1 nvidia_drm
drm                   671744  6 drm_kms_helper,nvidia_drm
e1000e                335872  0
usbhid                 73728  0
hid                   151552  1 usbhid

Let’s understand the three columns:

Understanding lsmod Output
Column What It Shows Example
Module Name of the loaded module e1000e
Size How many bytes of kernel memory this module occupies 335872 (≈ 328 KB)
Used by Reference count + which other modules depend on this one nvidia_uvm,nvidia_drm (means nvidia module is needed by both)

The Used by column is critical. A module with a non-zero use count cannot be removed with rmmod. You must first unload the modules that depend on it. For example, you cannot remove the nvidia module while nvidia_drm and nvidia_uvm are still loaded.

Practical Tool: modinfo — Inspect a Module’s Metadata

Every properly written kernel module embeds metadata inside itself — things like its author, a description, what license it is under, and what parameters it accepts. The modinfo command extracts and displays this metadata without actually loading the module.

# Get information about the e1000e Intel Ethernet driver module
$ modinfo e1000e

filename:       /lib/modules/6.8.0-45-generic/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko.zst
version:        3.2.6-k
license:        GPL v2
description:    Intel(R) PRO/1000 Network Driver
author:         Intel Corporation <linux.nics@intel.com>
srcversion:     4E4F0B85F33B53D4C4C7CE7
alias:          pci:v00008086d...
depends:        ptp
retpoline:      Y
intree:         Y
name:           e1000e
vermagic:       6.8.0-45-generic SMP preempt mod_unload modversions

Notice the vermagic field. This is very important — it tells you exactly which kernel version and configuration this module was compiled for. If you try to load a module compiled for kernel 6.7 into a 6.8 kernel, the kernel will refuse it unless version checking is disabled. We will cover this in detail when we discuss version magic.

Key Fields in modinfo Output — What Each Means
filename
Full path to the .ko file on disk. Helps you confirm which module file is being referred to.
license
The license under which the module is distributed. GPL modules get full access to the kernel API. Proprietary modules are restricted from using GPL-only exported symbols.
vermagic
The kernel version + config combination this module was compiled against. The kernel checks this before loading. A mismatch = load rejected (by default).
depends
Other modules that must be loaded before this one. modprobe uses this to automatically load them first.
parm
Module parameters — configurable values you can pass when loading the module. For example: sudo insmod mymod.ko debug=1 timeout=5000
intree
Y = this module is part of the official kernel source tree. N or absent = out-of-tree module. The kernel may taint itself when an out-of-tree module is loaded.

In-Tree vs Out-of-Tree Modules: What Is the Difference?

In-Tree vs Out-of-Tree Kernel Modules — Side by Side
Aspect In-Tree Module Out-of-Tree Module
Location Inside the Linux kernel source tree (under drivers/, fs/, net/, etc.) Separate directory, outside the kernel source. Your working directory.
Built by make modules during a full kernel build make in your module directory, referencing the kernel headers
Installed to /lib/modules/$(uname -r)/kernel/ Wherever you place it — or copied there manually
modinfo intree Y N (kernel marks itself as “tainted”)
Kernel taint No taint on loading Kernel taint flag O (Out-of-tree) gets set when loaded
Example e1000e Intel Ethernet driver, ext4 filesystem, USB HID driver Your custom device driver, a hello world module, an NVIDIA proprietary driver

Kernel Taint: What It Means and Why It Matters

When you load an out-of-tree module (or a module without a GPL-compatible license), the kernel sets a taint flag. This is the kernel’s way of saying “the state of this kernel is no longer pure — something unofficial has been loaded into it.”

Taint flags are visible in the kernel log (dmesg) and in /proc/sys/kernel/tainted. Each bit represents a different reason for taint. Some common ones:

Common Kernel Taint Flags
Flag Letter Meaning
P A proprietary (non-GPL) module was loaded
O An out-of-tree module was loaded
E An unsigned module was loaded (on kernels with module signing enforcement)
W A warning was issued during kernel operation

Why does this matter practically? When you report a kernel bug to a distro or to the kernel community, the first thing they check is whether the kernel is tainted. A tainted kernel means they cannot rule out that the unofficial module caused the bug. This is not a security concern per se — it is a quality-assurance mechanism. During your development work, you will routinely see taint flag O and that is perfectly normal.

# Check current taint status of your kernel
$ cat /proc/sys/kernel/tainted
0      # 0 = clean, untainted

# After loading your out-of-tree module:
$ sudo insmod ./mymodule.ko
$ cat /proc/sys/kernel/tainted
4096   # bit 12 set = out-of-tree module loaded

Module Signing in Kernel 6.x — A Security Feature

Modern Linux kernels (especially in kernel 6.x) support module signing. This means the kernel can require that every module be cryptographically signed before loading it. This is designed to prevent someone from loading a malicious kernel module (for example, a rootkit) on a secured system.

Module Signing Concept (Kernel 6.x Security Feature)
🔐 Developer’s
Private Key
+
📄 mymodule.ko
→ sign-file tool →
📄 mymodule.ko
(with embedded signature)
✅ Signature valid?
Kernel loads the module successfully
❌ No signature / Invalid?
Kernel may warn, taint itself (flag E), or refuse to load (if enforcement is on)

For your development work, module signing enforcement is typically disabled. But if you are writing drivers for a production embedded product running on a kernel with Secure Boot enabled, you will need to sign your modules. Kernel 6.x makes this easier with improved tooling in the scripts/ directory of the kernel source.

Essential Module Management Commands — Quick Reference

Module Management Commands Cheat Sheet
Command What It Does Handles Dependencies?
insmod module.ko Load a specific .ko file directly No — manual
rmmod module_name Unload a module by name No
modprobe module_name Load module + all dependencies automatically Yes — automatic
modprobe -r module_name Unload module + unused dependencies Yes — automatic
lsmod List all currently loaded modules N/A
modinfo module_name Show metadata of a module (without loading it) N/A
depmod -a Rebuild the modules.dep dependency file N/A
dmesg | tail -20 See kernel ring buffer — check module load messages N/A

Built-In Features vs Loadable Modules: A Practical Example

How do you know which features are compiled into the kernel versus loaded as modules? Two files in /lib/modules/$(uname -r)/ help you:

# modules.builtin lists features compiled directly into the kernel binary
$ cat /lib/modules/$(uname -r)/modules.builtin | grep "ext4"
kernel/fs/ext4/ext4.ko   # if ext4 is built-in, it appears here

# modules.builtin.modinfo gives parameter info for built-in features
$ cat /lib/modules/$(uname -r)/modules.builtin.modinfo | head -20

How Built-In Features and Modules Co-Exist in Kernel Memory
💻 Kernel Memory (VAS — Virtual Address Space)
vmlinuz — The Core Kernel Image

Scheduler (=y)
Memory Mgmt (=y)
ext4 FS (=y)
VFS (=y)
Interrupt handling (=y)
Always present. Cannot be removed while kernel is running.
e1000e.ko
Loaded when Intel NIC detected
usbhid.ko
Loaded when USB keyboard plugged in
mydriver.ko
Your out-of-tree module (loaded manually)
bt_module.ko
Not loaded (no BT hardware)

🎓 Interview Questions & Answers

Q1. What does it mean when a kernel config option is set to =m?

When a kernel configuration option is set to =m, the corresponding feature is compiled as a loadable kernel module (a .ko file) rather than being built into the kernel image. This means the feature is not available immediately at boot but can be loaded on demand using insmod or modprobe.

Q2. What is the difference between =y and =m in kernel configuration?

=y means the feature is compiled into the kernel image itself. It is always available from the moment the kernel boots and consumes memory permanently. =m means the feature is compiled as a separate .ko file and only loaded into memory when needed — saving RAM when the feature is not in use.

Q3. What is the lsmod command and what does each column mean?

lsmod displays all kernel modules currently loaded in memory. It reads from /proc/modules. The three columns are: Module (name of the module), Size (bytes of kernel memory it occupies), and Used by (reference count plus names of other modules that depend on this one). A non-zero use count prevents the module from being unloaded.

Q4. What is the vermagic field in modinfo?

The vermagic field contains the kernel version string and build configuration flags (such as SMP, preemption model) for which the module was compiled. When you try to load a module, the kernel compares the module’s vermagic with its own version. If they do not match, the kernel refuses to load the module to prevent incompatible code from running. This check can be disabled with insmod --force but doing so is dangerous.

Q5. What is a “tainted” kernel?

A tainted kernel is one where something outside the mainline, supported kernel code has been loaded or an unusual condition has occurred. Common reasons include: loading an out-of-tree module (flag O), loading a non-GPL module (flag P), loading an unsigned module (flag E), or a kernel warning being triggered (flag W). The taint status is stored in /proc/sys/kernel/tainted as a bitmask. Kernel developers use this to determine whether third-party code might have contributed to a reported bug.

Q6. Why can’t you remove a module that has a non-zero “Used by” count?

A non-zero “Used by” count means other loaded modules are depending on this module — they may be using functions or symbols it exports. If you forcibly removed the module while others depend on it, those dependent modules would have dangling pointers to code that no longer exists in memory, which would cause a kernel crash. The kernel prevents this by refusing rmmod on in-use modules. You must first unload all dependent modules.

Q7. What command do you run after installing new modules to make modprobe aware of them?

You run sudo depmod -a. This command scans all installed .ko files, analyzes their symbols and dependencies, and regenerates the modules.dep, modules.alias, and modules.symbols files in /lib/modules/$(uname -r)/. Without this step, modprobe will not be able to find or auto-load your newly installed module.

Q8. What is the significance of the GPL license for kernel modules?

The Linux kernel itself is licensed under GPL v2. Many internal kernel functions and symbols are exported with EXPORT_SYMBOL_GPL, which means they are only accessible to modules that declare a GPL-compatible license. If your module declares a proprietary license (for example, “Proprietary”), it cannot use these GPL-only symbols, which significantly limits what it can do. Declaring GPL in your module metadata with MODULE_LICENSE("GPL v2") is therefore important not just legally but technically.

Ready to Write Your First Kernel Module?

Next tutorial: We write a Hello World kernel module from scratch, understand the Makefile structure, and run it live on a kernel 6.x system.

Visit EmbeddedPathashala

Previous Lecture
Next Lecture

Leave a Reply

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