The Kbuild System
How does the Linux kernel know what to compile? Meet Kbuild — the brain behind every kernel build.
What you will learn in this part
- Why the kernel needs a special build system
- The four pillars of Kbuild: CONFIG symbols, Kconfig, Makefiles, and .config
- What CONFIG_FOO means and how y / m / n values work
- What Kconfig files are and why they matter
- How the recursive Makefile system works
- What the .config file actually contains
1. Why Does the Kernel Need Its Own Build System?
Think about this for a moment. The Linux kernel runs on everything — your laptop, a Raspberry Pi, a smart thermostat, a car dashboard, supercomputers. Each of these devices has a completely different processor, different memory, different peripherals. You obviously cannot include code for a WiFi driver when you are building a kernel for a temperature sensor with no WiFi.
At the same time, the kernel source tree is absolutely massive — we are talking about tens of millions of lines of code across thousands of files. You cannot just run gcc *.c and hope for the best.
This is exactly the problem that Kbuild solves. Kbuild is the Linux kernel’s own build infrastructure. It lets you describe what should be compiled, under what conditions, and for which hardware — all in a very structured and maintainable way. When you run make menuconfig and start ticking options, you are working through Kbuild.
💡 Think of it like a restaurant order
Imagine a restaurant with a menu of 3,000 dishes. You do not cook all 3,000 dishes for every customer. The customer picks what they want, the kitchen only prepares those specific dishes, and everything is made fresh for that order. Kbuild works the same way — you tell it which features you want (your “order”), and it compiles only those parts of the kernel. No waste, no bloat.
2. The Four Pillars of Kbuild
Kbuild works through four interconnected components. They all work together — change one and it affects the others. Let us understand each one.
| Component | Where It Lives | What It Does |
|---|---|---|
CONFIG_FOO symbols |
Everywhere in kernel source | Macros that represent each configurable feature (y/m/n) |
| Kconfig files | Every subsystem directory | Define what CONFIG_FOO means, its type, dependencies, menu text |
| Makefiles | Root + every subfolder | Tell the compiler which files to build based on CONFIG values |
.config file |
Kernel source root | The final kernel configuration — stores all your choices |
3. CONFIG_FOO — The Feature Toggle System
Every configurable feature in the Linux kernel has a name that starts with CONFIG_. For example:
CONFIG_BT— Bluetooth supportCONFIG_USB— USB supportCONFIG_EXT4_FS— EXT4 filesystem supportCONFIG_SMP— Symmetric Multi-Processing (multiple CPU cores)CONFIG_MODULES— Loadable module support
Each of these CONFIG symbols can take one of three values. This is the core concept — once you understand y/m/n, the whole system makes sense.
The feature is compiled directly into the kernel image. It is always available from boot. Cannot be removed at runtime.
Compiled as a separate .ko file. Loaded into the kernel when needed using modprobe. Can be loaded and unloaded at runtime.
The feature is not compiled at all. Zero impact on kernel size or boot time. As if the code does not exist.
Here is a concrete example. Suppose you want to understand whether Bluetooth is in the kernel. You check the .config file:
# In .config file — three possible states for Bluetooth:
CONFIG_BT=y # Built directly into the kernel image
CONFIG_BT=m # Built as a loadable module (bluetooth.ko)
# CONFIG_BT is not set # Not compiled at all
Now look at how the Bluetooth subsystem Makefile uses this to decide what to compile:
# net/bluetooth/Makefile (simplified concept)
obj-$(CONFIG_BT) += bluetooth.o
# When CONFIG_BT=y → this becomes: obj-y += bluetooth.o (built-in)
# When CONFIG_BT=m → this becomes: obj-m += bluetooth.o (module)
# When CONFIG_BT=n → this becomes: obj- += bluetooth.o (ignored)
obj-$(CONFIG_BT) pattern handles all three cases automatically. This is the elegance of the Kbuild system — the CONFIG symbol value gets substituted directly into the Makefile variable.
Not Every Config Is a Toggle
Some CONFIG symbols are not on/off — they can hold numeric or string values:
# Numeric config example
CONFIG_HZ=250 # Timer interrupt frequency (ticks per second)
CONFIG_LOG_BUF_SHIFT=17 # Kernel log buffer size (2^17 = 128KB)
# String config example
CONFIG_LOCALVERSION="-mykernel" # Appended to kernel version string
CONFIG_DEFAULT_HOSTNAME="(none)" # Default hostname
For toggle-type features, you will see either bool (only y or n) or tristate (y, m, or n) in the Kconfig definition. Numeric and string types have their own config types as well, which we will see in the Kconfig section.
4. Kconfig Files — Where Features Are Defined
You now know what a CONFIG symbol is. But where does the kernel define what CONFIG_BT actually means, what type it is, what it depends on, and what text appears in the menu when you run make menuconfig?
That is the job of Kconfig files. There is a Kconfig file in almost every directory of the kernel source tree. Each subsystem describes its own features in its local Kconfig file.
Let us look at a simplified Kconfig entry for Bluetooth to understand the syntax:
# net/bluetooth/Kconfig (simplified for learning)
config BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
select CRC16
help
This option enables the core Bluetooth support.
Bluetooth is a short-range wireless technology.
Say M to compile Bluetooth as a loadable module (bluetooth.ko)
Say Y to build it directly into the kernel.
Let us break down what each line means:
| Kconfig keyword | What it does |
|---|---|
config BT |
Declares a new config option named BT (which becomes CONFIG_BT in code) |
tristate |
This option can be y, m, or n. Use bool for y/n only features. |
depends on NET |
This option only appears if CONFIG_NET=y. Dependency chain. |
select CRC16 |
Automatically enables CONFIG_CRC16 when you enable BT. |
help |
The text shown when you press <Help> in menuconfig. |
The dependency system is particularly important. If you try to enable Bluetooth but you forgot to enable networking (CONFIG_NET), Kbuild will not even show you the Bluetooth option — because the dependency is not satisfied. This prevents impossible or broken configurations.
📁 Where Kconfig files live in the kernel source tree
linux-6.x/
├── Kconfig ← Top-level, ties everything together
├── init/Kconfig ← Core kernel options
├── kernel/Kconfig ← Scheduler, tracing, locking
├── mm/Kconfig ← Memory management
├── net/Kconfig ← Networking core
│ └── bluetooth/Kconfig ← Bluetooth-specific options
├── drivers/
│ ├── usb/Kconfig ← USB driver options
│ └── net/Kconfig ← Network driver options
└── arch/arm/Kconfig ← ARM architecture options
5. The Recursive Makefile System
Once you have decided your configuration (your .config file is ready), the Makefile system takes over and actually compiles the source code.
The kernel uses a recursive Makefile approach. There is one top-level Makefile at the root of the kernel source tree, and then every subdirectory has its own smaller Makefile. When you run make in the kernel root, it starts at the top-level Makefile, which then calls Makefiles in subdirectories, which in turn call their subdirectory Makefiles, and so on.
As of the Linux 6.x kernel, there are over 3,000 Makefiles spread across the source tree. That is a big number, but it makes perfect sense — each directory just manages its own little corner of the codebase.
Here is what a typical subsystem Makefile looks like:
# drivers/usb/core/Makefile (simplified concept)
obj-$(CONFIG_USB) += usbcore.o
usbcore-y += usb.o hub.o hcd.o urb.o
obj-$(CONFIG_USB_ANNOUNCE_NEW_DEVICES) += announce.o
# If CONFIG_USB=y → usbcore.o is compiled and linked into kernel
# If CONFIG_USB=m → usbcore.ko is created as a loadable module
# If CONFIG_USB=n → nothing happens, USB code is skipped entirely
6. The .config File — Your Kernel’s DNA
After you run make menuconfig (or any configuration tool) and save your choices, all those decisions are written to a single plain text file called .config in the kernel source root directory.
This file is the most important output of your configuration work. It captures exactly what you want in your kernel. Here is what a small portion of a real .config file looks like:
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 6.12.0 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 13.2.0) 13.2.0"
CONFIG_64BIT=y
CONFIG_X86=y
CONFIG_SMP=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# Networking support
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_BT=m
# CONFIG_WIRELESS is not set
# File systems
CONFIG_EXT4_FS=y
# CONFIG_BTRFS_FS is not set
CONFIG_TMPFS=y
CONFIG_HZ=250
CONFIG_HZ_250=y
CONFIG_LOCALVERSION=""
make menuconfig or similar tools to modify your configuration.
The .config file is also your kernel’s identity. If you build a product and need to reproduce that exact kernel later, you need this file. Treat it like source code — keep it in version control.
7. How All Four Components Work Together
Let us trace the complete flow from “I want USB support” to “USB driver is compiled”:
make menuconfig.configPart 1 — Quick Recap
- Kbuild is the Linux kernel’s build infrastructure — it controls what gets compiled and what does not
- Every feature is represented by a
CONFIG_FOOmacro that can be y (built-in), m (module), or n (excluded) - Kconfig files define features, their types, dependencies, and menu text for each subsystem
- Makefiles use
obj-$(CONFIG_FOO)patterns to decide what to compile - The
.configfile stores all your choices — it is the kernel’s DNA

2 Comments