Kbuild and Modules

On this page

Even after generating a .ko, loading may still fail; having a valid signature does not guarantee ABI compatibility. To explain whether a module truly fits, you must check along the lines of configuration, build information, loading checks, and resource cleanup.

Inspection TargetReading Location
Feature selection and artifact compositionKconfig, Kbuild
Reproducible builds and target matchingExternal module project, build information
Runtime and trust boundariesLoading, exported symbols, signatures
Configuration, build, and loading are three different inspection boundaries Kconfig and .config Select features, satisfy configuration dependencies 1 Kbuild and modpost Compile and link, check module symbols, etc. 2 Kernel Module Loader Check target compatibility, symbols, signatures, and call initialization 3 Compiling a .ko does not mean it can be loaded on any kernel; successful loading does not mean the driver behaves correctly.

Kconfig: Dependencies Are Not Textual Menu Decorations

bool represents y/n, while tristate also allows m, with prerequisites including module support configurations. depends on constrains the availability of options; select forcibly enables another symbol, potentially bypassing the latter's original dependency checks, so select should not be treated as a general dependency declaration. Kconfig

.config is the result of selections. It can be updated using olddefconfig etc. after changing source code or kernel versions, but changes in default values should be reviewed. defconfig is architecture-dependent and does not guarantee suitability for all hardware on the current machine.

Why consumers cannot remain 'y' when dependencies are 'm'

For standard tristate dependencies, you can understand the upper bound using n < m < y. If CONSUMER directly calls the functionality of PROVIDER and the dependency is written as depends on PROVIDER, when PROVIDER=m, CONSUMER can only reach up to m; built-in code cannot depend on modules that are yet to be loaded at runtime.

Save the following Kconfig in an independent experiment directory. It includes an intentionally incorrect select relationship to observe how forced selection breaks dependencies.

mainmenu "Dependency example"
config MODULES
    bool "Enable modules"
    modules
    default y
config PROVIDER
    tristate "Provider"
config CONSUMER
    tristate "Consumer"
    depends on PROVIDER
config HARDWARE
    bool "Required hardware support"
config DRIVER
    tristate "Driver"
    depends on HARDWARE
config FORCE_DRIVER
    bool "Force driver"
    select DRIVER
Request in seedEvaluated key resultReason
PROVIDER=m, CONSUMER=yCONSUMER=mForward dependency pushes the upper bound to m
PROVIDER=n, CONSUMER=yCONSUMER=nProvider is off, consumer cannot be enabled
PROVIDER=y, CONSUMER=yCONSUMER=yDependency allows built-in
HARDWARE=n, FORCE_DRIVER=yDRIVER=y, reports unmet dependenciesSelect forces lower bound, no dependency satisfied for DRIVER

For example, save the following as seed.config, and use scripts/kconfig/conf from the built kernel source tree to evaluate it; the output directory and KCONFIG_CONFIG should remain in this experiment directory to avoid modifying the kernel configuration currently in use.

CONFIG_MODULES=y
CONFIG_PROVIDER=m
CONFIG_CONSUMER=y
KCONF=/path/to/kernel/scripts/kconfig/conf
KCONFIG_CONFIG=out.config "$KCONF" --defconfig=seed.config Kconfig

The fourth line is not a recommended configuration, but demonstrates why incorrect relationships are dangerous. When fixing, let the configuration express real dependencies, or model dependency-free internal helpers suitable for selection by multiple callers separately. Replacing select with imply cannot replace the design of built-in/module linking relationships. Kconfig Dependencies and Three-Valued Expressions

Directly change dependencies and the requested tristate values. Below, treat n, m, y as ordered upper bounds, showing the build result after taking the smaller value between requested and dependency upper bounds; it does not treat dependencies as automatically selected menus.

Preparing the visual
Requested y, why is the final result only m or n?

depends on establishes an upper bound for symbols. When DRIVER depends on BUS && NET, it cannot reference dependencies provided only as modules (m) as built-in (y). select has different semantics and cannot replace standard dependencies.

Kbuild: Object Sets and Composite Modules

obj-$(CONFIG_MYDRV) += mydrv.o
mydrv-y := main.o helper.o

When configured as y, add the built-in object path; when m, build the module; when disabled, do not add to the corresponding set. mydrv-y specifies the files composing this composite object; do not treat mydrv.o as two different targets unrelated to the same-named mydrv.c.

Configuring as y does not automatically guarantee final linking, and configuring as m does not guarantee the dependency can call it in built-in mode. Kconfig relationships and link dependencies must be designed together.

A Complete External Module Project

The directory contains the following three files. This example only records loading and unloading, not device takeover.

hello.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>

static int greeting_id = 42;
module_param(greeting_id, int, 0444);
MODULE_PARM_DESC(greeting_id, "Identifier printed on initialization");

static int __init hello_init(void)
{
    pr_info("kb_hello: id=%d\n", greeting_id);
    return 0;
}
static void __exit hello_exit(void)
{
    pr_info("kb_hello: removed\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Knowledge base build example");

Kbuild:

obj-m := kb_hello.o
kb_hello-y := hello.o

Makefile (recipe lines start with a Tab):

KDIR ?= /lib/modules/$(shell uname -r)/build
MODULE_DIR := $(CURDIR)

.PHONY: all clean
all:
	$(MAKE) -C $(KDIR) M=$(MODULE_DIR) modules
clean:
	$(MAKE) -C $(KDIR) M=$(MODULE_DIR) clean

Run make in this directory to get kb_hello.ko, or specify the target build directory via make KDIR=/path/to/prepared/kernel. Use $(MAKE) to preserve recursive build parameters; Kbuild lists objects separately to avoid confusion between standard wrapper targets and kernel rules. External Modules

Headers Are Not Enough: Must Match Target Build Information

External modules require the target kernel's configuration, generated headers, and necessary build artifacts. modules_prepare does not generate the complete Module.symvers required when MODVERSIONS is enabled; the corresponding build must provide it. Do not assume that preparing headers includes all symbol version information.

modpost handles exported symbols and module metadata. Unresolved symbols, namespace, and version issues should be investigated at this stage and the loading stage respectively. Mainline Linux does not promise a stable internal module ABI across kernel versions. Stable API Explanation

Two External Modules: Known Symbols Do Not Mean Linked Into Consumer

The following second project only demonstrates dependencies between modules; place it in a different directory from the hello project. provider exports a function, which consumer calls during initialization. Build both modules uniformly first, so modpost can see their export information simultaneously.

kb_api.h:

#ifndef KB_API_H
#define KB_API_H
unsigned int kb_step(unsigned int value);
#endif

kb_provider.c:

#include <linux/module.h>
#include "kb_api.h"

unsigned int kb_step(unsigned int value)
{
    return value + 1U;
}
EXPORT_SYMBOL_GPL(kb_step);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Knowledge base symbol provider");

kb_consumer.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include "kb_api.h"

static int __init kb_consumer_init(void)
{
    return kb_step(41U) == 42U ? 0 : -EINVAL;
}
static void __exit kb_consumer_exit(void) {}
module_init(kb_consumer_init);
module_exit(kb_consumer_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Knowledge base symbol consumer");

Kbuild:

obj-m := kb_provider.o kb_consumer.o

You can reuse the wrapper rules from the previous Makefile, specifying the prepared KDIR to build. After completion, you can check without loading the modules:

modinfo -F depends ./kb_consumer.ko
nm -u ./kb_consumer.ko

depends should include kb_provider, and you can still see kb_step in the list of undefined symbols. This is not contradictory: consumer's .ko does not statically copy provider's implementation; symbols are resolved when the kernel loads them; modpost knows which module they come from and generates dependency metadata.

Declarations, export records, and runtime implementations provide different evidence C Compilation Header declares kb_step modpost Export table indicates symbol provider Runtime Loading Resolve symbols from loaded provider Undefined references in consumer.ko can remain; dependency information is recorded separately KBUILD_EXTRA_SYMBOLS provides build information; it does not embed provider's implementation into consumer.

If you split the two modules into independent build directories and provide only the declaration header to consumer, C compilation may still succeed, but modpost will refuse to proceed due to missing export records for kb_step. Build provider first, then pass KBUILD_EXTRA_SYMBOLS=/absolute/path/to/provider/Module.symvers to the consumer build to supplement the symbol information required at the build stage; both projects must be built against compatible target kernels.

This parameter does not install or load provider, nor does it make inconsistent headers, outdated symvers, or ABI mismatches into correct dependencies. At runtime, provider must still exist and be resolved first; only when the module index is installed correctly can modprobe find and load dependencies according to them. If you only use insmod to specify the consumer file, it will not automatically find provider just because symvers were provided at build time. Symbols and Module.symvers for External Modules

Loading Tools and Lifecycle

ToolDependency Handling
insmodLoads the specified file, does not automatically find all dependencies
modprobeHandles dependencies and aliases based on module index and configuration
modprobe -rCan attempt to remove unused dependencies, does not guarantee all are unloaded

Distinguish between "loading this file" and "loading according to installed module relationships" before choosing a tool.

Module loading involves relocation, symbol resolution, checks, and init calls; if init fails, resources already acquired must be cleaned up. The exit path must stop new requests, cancel work, wait for references and callbacks to finish, and cannot simply write a pr_info as complete unloading logic for a real driver.

You can first use modinfo ./kb_hello.ko to check names, parameters, and vermagic. Installation and loading affect the system, so it is usually best to do this in a test VM matching the kernel. The boot entries and initramfs behavior of make install depend on architecture and distribution hooks; these are not all steps guaranteed to be completed by Kbuild in one go.

Exported Symbols Are Not Permanently Stable Public APIs

EXPORT_SYMBOL exposes symbols to applicable modules, EXPORT_SYMBOL_GPL imposes restrictions on module license declarations; symbol namespaces may also require corresponding imports. Exporting means it can be resolved, but does not imply that calling it in any context is safe, nor does it promise it will remain unchanged in future versions.

Module Signing: Verification, Automatic Signing, and Mandatory Policies Are Separate

Configuration or PolicyResponsible For
CONFIG_MODULE_SIGProvides signature verification mechanism
CONFIG_MODULE_SIG_ALLInvolves automatic signing during build and installation
CONFIG_MODULE_SIG_FORCE or mandatory policyDetermines if an acceptable signature is required

A SIG=y does not mean "automatically sign all modules and forcibly reject all unmatched modules". Signing Documentation

Runtime policies, trusted keys, and Secure Boot integration are affected by kernel and distribution configurations. Successful signature verification does not guarantee that the module matches the current kernel ABI or behaves correctly. Signatures are appended after the module content; modifying or stripping after signing may invalidate them; arrange signing at the correct product processing stage.

For further policy testing, see Debugging and Testing.