Part 1: The Kbuild System — How the Kernel Builds Itself-Linux Kernel Development Course Online

📚 Linux Kernel Programming Series  |  Part 1: The Kbuild System  →  Part 2: Kernel Configuration →  Part 3: Config Approaches →
Linux Kernel Programming | Part 1

The Kbuild System

How does the Linux kernel know what to compile? Meet Kbuild — the brain behind every kernel build.

⏱ ~20 min read
🎯 Beginner Friendly
🐧 Kernel 6.x Updated

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.

Kbuild — Four Pillars Overview
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 support
  • CONFIG_USB — USB support
  • CONFIG_EXT4_FS — EXT4 filesystem support
  • CONFIG_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.

y
yes / built-in

The feature is compiled directly into the kernel image. It is always available from boot. Cannot be removed at runtime.

m
module

Compiled as a separate .ko file. Loaded into the kernel when needed using modprobe. Can be loaded and unloaded at runtime.

n
no / excluded

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)
Key insight: The Makefile itself does not have any if/else logic. The 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.

Recursive Makefile Structure
linux-6.x/Makefile (Top-Level)
│
│
net/Makefile
│
bluetooth/Makefile
│
drivers/Makefile
│
usb/Makefile
│
arch/arm/Makefile
│
kernel/Makefile
Each subsystem manages its own compilation independently

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=""
⚠️ Important: Notice the comment at the top — “DO NOT EDIT”. The .config file is generated by the configuration tools. If you manually edit it, you can accidentally create an inconsistent configuration where a dependent option is enabled but its dependency is not. Always use 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”:

Kbuild Complete Flow
👤
You run
make menuconfig
→
📋
Kconfig reads
Shows options with dependencies
→
💾
Saves choices
Writes your selections to .config
→
🔨
make builds
Makefiles use CONFIG values to compile the right files

Part 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_FOO macro 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 .config file stores all your choices — it is the kernel’s DNA

Interview Questions

Q1. What is the difference between CONFIG_FOO=y and CONFIG_FOO=m?
When set to y, the feature is compiled directly into the kernel image (vmlinux). It is always present from boot and cannot be removed without rebooting. When set to m, the feature is compiled as a separate kernel module (.ko file). It can be loaded with modprobe when needed and unloaded with rmmod when not needed. Modules offer flexibility but add a small overhead compared to built-in features.
Q2. What happens if you manually edit the .config file?
Technically you can manually edit it, but it is risky. The .config file has dependencies — enabling one option may require another to be enabled first. Manual edits can create inconsistent states where a feature is enabled but its dependency is not, leading to compile errors or unexpected behavior. You should always use make menuconfig, make xconfig, or make oldconfig to maintain a consistent configuration.
Q3. What is the purpose of the “select” keyword in a Kconfig entry?
The select keyword automatically enables another CONFIG option when the current one is enabled. For example, if Bluetooth needs CRC16 checksum support, the Kconfig entry for CONFIG_BT can say select CRC16. This means whenever a user enables CONFIG_BT, CONFIG_CRC16 is automatically set to y — even if the user never saw that option in the menu. This prevents missing dependency errors.
Q4. How does Kbuild know which source files to compile for a given feature?
Each directory in the kernel source tree has its own Makefile. These Makefiles use the obj-$(CONFIG_FOO) pattern. When the build system processes this, it substitutes the value of CONFIG_FOO (y, m, or blank for n) into the variable name. obj-y means build and link into kernel, obj-m means build as a module, and obj- (empty) means skip this file entirely. This is how the Makefile decides what to compile without any explicit if/else branching.
Q5. What is the difference between a tristate and a bool config option?
A bool config option can only be y (yes) or n (no) — it cannot be a module. This is used for features that are core to the kernel and cannot be separated out into a loadable module, such as support for SMP (multiple CPUs) or specific CPU architecture features. A tristate can be y, m, or n — it supports the module option. This is used for drivers and optional features that make sense as loadable modules.
Linux Kernel Programming Series | EmbeddedPathashala
Next: Kernel Configuration →

2 Comments

Leave a Reply

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