Kbuild: The Kernel Build System
Makefiles, Kconfig, out-of-tree modules, and cross-compilation
Overview
The kernel build system (Kbuild) is built on top of GNU Make. It uses a hierarchy of Makefile fragments across the source tree and a powerful configuration language (Kconfig):
make menuconfig → generates .config (symbol definitions)
make → compiles kernel using .config
make modules_install → installs modules to /lib/modules/$(uname -r)/
.config: kernel configuration
# Start fresh from a default config:
make defconfig # the arch's default config (arch/$SRCARCH/configs/)
make allmodconfig # enable everything as modules
make allyesconfig # enable everything built-in
make localmodconfig # minimal config matching currently loaded modules
# Interactive config tools:
make menuconfig # text-based menu (requires ncurses-dev)
make xconfig # Qt GUI
make gconfig # GTK GUI
make nconfig # newer ncurses UI
# Use existing config as base:
cp /boot/config-$(uname -r) .config
make olddefconfig # accept defaults for new symbols
# Randomized config, for build-testing wide swathes of the tree:
make randconfig # random config (for testing)
Config file format
# .config format: each CONFIG_SYMBOL has one of:
CONFIG_SMP=y # built-in (y = yes)
CONFIG_MODULES=y # required for loadable modules
CONFIG_EXT4_FS=m # built as module
# CONFIG_NFS_FS is not set # disabled
# Query a symbol:
./scripts/config --state CONFIG_SMP
# y
# Set a symbol (pick one that actually has a prompt — see the note under
# "Compiler options and build flags" about promptless symbols):
./scripts/config --enable CONFIG_DEBUG_KERNEL
./scripts/config --disable CONFIG_SWAP
./scripts/config --module CONFIG_EXT4_FS
./scripts/config --set-val CONFIG_LOG_BUF_SHIFT 18
Kconfig language
# drivers/net/ethernet/intel/Kconfig
config E1000
tristate "Intel(R) PRO/1000 Gigabit Ethernet support"
depends on PCI && HAS_IOPORT
help
This driver supports Intel(R) PRO/1000 gigabit ethernet family of
adapters. For more information on how to identify your adapter, go
to the Adapter & Driver ID Guide that can be located at:
<http://support.intel.com>
...
config E1000E
tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support"
depends on PCI && (!SPARC32 || BROKEN)
depends on PTP_1588_CLOCK_OPTIONAL
select CRC32
help
...
# Types:
# bool: y or n
# tristate: y, m (module), or n
# string: CONFIG_DEFAULT_HOSTNAME="(none)"
# int: CONFIG_LOG_BUF_SHIFT=18
# hex: CONFIG_PAGE_OFFSET=0xC0000000
# Dependencies:
# depends on A && B → both A and B must be y/m
# depends on A || B → at least one
# depends on !A → A must not be set
# select A → force-enable A (ignores depends)
# imply A → suggest enabling A (can be overridden)
Kbuild Makefiles
Each directory has a Makefile that tells Kbuild what to compile:
# drivers/net/ethernet/intel/e1000/Makefile
# obj-$(CONFIG_E1000): 'y' → built-in, 'm' → module, unset → skip
obj-$(CONFIG_E1000) += e1000.o
# e1000.o is built from multiple source files:
e1000-y := e1000_main.o e1000_hw.o e1000_ethtool.o e1000_param.o
# Subdirectories:
obj-y += subdirname/
# → recurse into subdirname/Makefile
Top-level build flow
make
├── scripts/Makefile.build: recurse into each subdirectory
├── Each Makefile: contributes obj-y, obj-m to the build
├── vmlinux: link all obj-y into the kernel binary
└── modules: compile all obj-m into .ko files
Building an out-of-tree module
# Makefile for external module (hello_module.c):
obj-m := hello_module.o
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
# Build the module:
make
# Install (copies to /lib/modules/$(uname -r)/updates/, the default
# INSTALL_MOD_DIR for external modules):
sudo make install
sudo depmod -a # update module dependency database
# Load:
modprobe hello_module
Multiple source files
obj-m := mydriver.o
mydriver-y := main.o helper.o init.o
# Builds main.o, helper.o, init.o and links into mydriver.ko
Cross-compilation
# Cross-compile for ARM64 on x86-64 host:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make defconfig # uses arch/arm64/configs/defconfig
make -j$(nproc)
# The CROSS_COMPILE prefix is prepended to:
# ${CROSS_COMPILE}gcc, ${CROSS_COMPILE}ld, etc.
# For 32-bit ARM (e.g. Raspberry Pi 2/3, armv7):
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make multi_v7_defconfig # arch/arm/configs/; includes CONFIG_ARCH_BCM2835
make -j$(nproc)
# Out-of-tree module cross-compilation (invoke kbuild directly: -C points at
# the prepared kernel build tree, M= at the module source directory):
make -C /path/to/arm64-kernel-build M=$PWD \
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules
Compiler options and build flags
# Add compilation flags:
ccflags-y += -DDEBUG_MODE
ccflags-y += -I$(src)/include # $(src) = directory of this Makefile
# Per-file flags:
CFLAGS_hello_module.o += -DTEST_BUILD
# Disable warnings:
CFLAGS_my_file.o += -w
# CONFIG_* symbols come from .config (via include/config/auto.conf), not
# from the make command line — setting them there is not how kbuild works.
# Edit .config, then re-resolve the new symbols:
# Build with debugging info (for KGDB, crash analysis). CONFIG_DEBUG_INFO
# itself has no prompt — it is selected by the "Debug information" choice,
# so enable a choice entry instead. That choice is itself
# `depends on DEBUG_KERNEL`, which has no default, so enable DEBUG_KERNEL
# first or olddefconfig will silently drop the DWARF selection too:
./scripts/config --enable CONFIG_DEBUG_KERNEL
./scripts/config --enable CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
make olddefconfig && make -j$(nproc)
# Build with address sanitizer:
./scripts/config --enable CONFIG_KASAN
make olddefconfig && make -j$(nproc)
# Enable compile-time warnings. The levels are independent, not cumulative:
# each is gated by its own findstring test on KBUILD_EXTRA_WARN, so W=3
# enables only the level-3 warnings, not levels 1 and 2 as well.
make W=1 # relevant warnings that do not occur too often
make W=2 # warnings that occur quite often but may still be relevant
make W=3 # more obscure warnings, can most likely be ignored
make W=123 # combine all three (W=12, W=13, ... work the same way)
make C=1 # sparse static analysis
make C=2 # sparse for all files
# Documentation:
make htmldocs # build kernel documentation
depmod and module dependencies
# modules.dep: dependency database for modprobe:
depmod -a # generate for current kernel
depmod -a 5.15.0 # for specific kernel version
# View module dependencies:
modinfo -F depends virtio_net
# virtio,net_failover
# modprobe automatically loads dependencies:
modprobe virtio_net
# Loads whichever of virtio_net's dependencies are themselves modules
# (here virtio, net_failover) before virtio_net. Which ones those are is
# config-dependent: CONFIG_VIRTIO_NET selects NET_FAILOVER, DIMLIB and
# PAGE_POOL and depends on VIRTIO. NET_FAILOVER and DIMLIB are tristate and
# so may be modules; PAGE_POOL is a plain bool and is always built in.
# Manual depmod output:
cat /lib/modules/$(uname -r)/modules.dep | grep virtio_net
# kernel/drivers/net/virtio_net.ko: kernel/drivers/virtio/virtio.ko ...
Build artifacts
# After make:
# Top-level build directory:
# vmlinux ← uncompressed ELF kernel (for debugging)
# System.map ← symbol table (addresses)
ls arch/x86/boot/
# bzImage ← bootable compressed kernel
# Module signature. Signing is not part of the build: `make` leaves the .ko
# in the build tree unsigned. scripts/Makefile.modinst signs modules during
# `make modules_install` (when CONFIG_MODULE_SIG_ALL=y) or `make modules_sign`,
# so inspect the *installed* copy:
modinfo /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/intel/e1000/e1000.ko | grep sig
# sig_id: PKCS#7
# sig_hashalgo: sha256
# Global text symbols defined by a module (not the same as EXPORT_SYMBOL exports):
nm --defined-only drivers/net/ethernet/intel/e1000/e1000.ko | grep " T "
# Only text (function) symbols defined in the module
# Check what a module provides and needs:
modinfo drivers/net/ethernet/intel/e1000/e1000.ko
# filename: ...
# license: GPL v2
# description: Intel(R) PRO/1000 Network Driver
# vermagic: 7.2.0-rc7 SMP preempt mod_unload modversions
# vermagic: must match the running kernel exactly — except that with
# CONFIG_MODVERSIONS the leading release string is skipped and only the
# trailing flags are compared (same_magic() in kernel/module/version.c)
Useful build targets
make -j$(nproc) # parallel build
make bzImage # kernel image only
make modules # modules only
make modules_install # install modules to /lib/modules/
make install # install kernel image + System.map
# Single file:
make drivers/net/ethernet/intel/e1000/e1000.o
# Single module:
make drivers/net/ethernet/intel/e1000/e1000.ko
# Export sanitised UAPI headers (the old `headers_check` target was
# removed in v5.17; CONFIG_UAPI_HEADER_TEST compile-tests them instead):
make headers_install
# Clean:
make clean # remove build artifacts (keep .config)
make mrproper # remove everything including .config
make distclean # mrproper + editor/tag leftovers: *.orig, *.rej, *~, *.bak,
# #*#, *%, core, tags/TAGS, cscope*, GPATH/GRTAGS/GSYMS/GTAGS
Further reading
Kernel source
- Makefile — the top-level build driver: the
W=1/W=2/W=3warning levels andC=1/C=2sparse checking, theARCH/CROSS_COMPILEhandling, and theclean/mrproper/distcleantargets listed above - scripts/Makefile.build — the per-directory recursion engine that reads each
Makefile'sobj-y/obj-mlists and descends intoobj-y += subdir/ - scripts/Makefile.lib — expands composite objects (the
<module>-y/<module>-objssuffix search) and assembles the per-file flagsccflags-y,asflags-y, andCFLAGS_<file>.o - scripts/Makefile.modinst — the
modules_installrules; noteINSTALL_MOD_DIR ?= updates, the default install subdirectory for external modules since commit b74d7bb7ca24 ("kbuild: Modify default INSTALL_MOD_DIR from extra to updates", v6.3) - scripts/kconfig/ — the Kconfig implementation:
lexer.landparser.yparse the language shown above,mconf.c/nconf.c/qconf.cc/gconf.care the programs behindmenuconfig/nconfig/xconfig/gconfig, andstreamline_config.plimplementslocalmodconfig - scripts/config — the shell script implementing the
--state/--enable/--disable/--module/--set-val.configedits used above
Man pages
modprobe(8)— dependency-aware module loading,-r, and module parameters on the command linedepmod(8)— generatesmodules.depand the map files;-a,-b basedir, and the optionalversionargumentmodules.dep(5)— the format ofmodules.dep/modules.dep.bin, the dependency databasemodprobeconsultsmodinfo(8)—-F fieldextraction of the documented.modinfofields (depends,license,alias,parm, …); kmod also printsvermagicand thesig*fields shown above, which the man page's own field list does not enumerate
Related pages
- Writing and Loading Kernel Modules — module init/exit, the source-to-
.kolifecycle these Makefiles drive - Module Loading Internals — what the kernel does with the
.koafterwards: ELF parsing, relocation, and thevermagic/modversions checks - Module Parameters, Symbols, and Kconfig —
module_param(), symbol export, and how Kconfig symbols reach module code - Kernel Module Signing —
CONFIG_MODULE_SIG_ALLand signing out-of-tree modules - KGDB: Kernel GDB Debugger — the consumer of the debug-info builds described above
LWN articles
- How many ways are there to configure the Linux kernel? — Daroc Alden, September 10, 2025: 32,468 Kconfig options on x86_64 in 6.16, and what
depends on/selectconstraints do to the space of valid configurations - A kbuild and kconfig maintainer change — August 6, 2025: Masahiro Yamada steps down after eight years; kbuild moves to "odd fixes" under Nathan Chancellor and Nicolas Schier, with Kconfig orphaned outright at the time — the same two have since picked up Kconfig as well, also under "odd fixes"
External
- Kernel Build System — index of the upstream
Documentation/kbuild/set - Linux Kernel Makefiles — the authoritative reference for
obj-y/obj-m, composite objects via<module>-y,ccflags-y/subdir-ccflags-y, and$(src)/$(obj) - Building External Modules — the
make -C $KDIR M=$PWD modulespattern,KBUILD_EXTRA_SYMBOLS, and wheremodules_installputs external modules - Kconfig Language —
bool/tristate/string/int/hextypes and the exact semantics ofdepends on,select, andimply - Configuration targets and editors — the
menuconfig/nconfig/xconfig/gconfigfront-ends and theKCONFIG_CONFIG,KCONFIG_ALLCONFIG, andKCONFIG_SEEDenvironment variables - Kbuild — build-time environment variables:
KBUILD_OUTPUT/O=,KBUILD_EXTRA_WARN/W=,CROSS_COMPILE,CFfor sparse, andINSTALL_PATH