Building and Booting a Custom Linux Kernel: A Real-World Journey
Building a custom Linux kernel from source is an exciting but challenging task. This post documents my step-by-step process of downloading, customizing, compiling, installing, and booting a custom Linux kernel on a Debian cloud server. It also covers the problems I encountered along the way and how I solved them.
Environment Setup
I started with a Debian 12 server hosted on DigitalOcean. The machine is headless with SSH-only access, running the stock kernel 6.1.0-26-amd64
. My goal was to:
- Download Linux kernel 6.1 source code
- Add a custom printk log message during boot
- Compile and install the custom kernel alongside existing ones
- Boot into my kernel and verify the changes
I installed the essential build tools:
sudo apt update
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev libelf-dev bc git
Downloading and Preparing Kernel Source
I cloned the stable Linux kernel 6.1 source with:
git clone --depth=1 --branch v6.1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git linux-6.1-custom
cd linux-6.1-custom
To uniquely identify my kernel build, I modified the Makefile
to append a custom suffix:
EXTRAVERSION = -custom
Adding a Custom Boot Log
I wanted a clear indication that my kernel was running. I added the following line inside the start_kernel()
function in init/main.c
:
pr_info("Custom Kernel Loaded Successfully\n");
Important: This line must be inside a function body. Placing it outside caused cryptic compiler errors because kernel logging macros rely on specific macro expansions that can’t be used at global scope.
Kernel Configuration
To preserve existing settings, I copied the current config and updated it:
cp /boot/config-$(uname -r) .config
yes "" | make olddefconfig
A warning about ANDROID_BINDER_IPC=m
appeared but was automatically handled by the build system.
Building the Kernel: Issues and Solutions
1. Kernel Image (bzImage
) Not Generated
Initially, running:
make -j$(nproc)
only built kernel modules (.ko
files). The actual kernel image arch/x86/boot/bzImage
was missing. This caused make install
to fail.
Solution: Explicitly build the kernel image with:
make bzImage -j$(nproc)
2. BTF Debug Info Build Failure
The build failed at the vmlinux
stage with:
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
The build system needs the external tool pahole
to generate BPF Type Format debug info.
Solution Options:
-
Disable BTF debug info:
scripts/config --disable CONFIG_DEBUG_INFO_BTF make olddefconfig
-
Or install
pahole
:sudo apt install dwarves
For a quicker build, I chose to disable BTF.
Installing and Configuring GRUB
After successfully building the kernel and modules:
sudo make modules_install
sudo make install
sudo update-grub
GRUB detected my kernel as 6.1.0-custom+
.
Booting the Custom Kernel
Despite installation, the system booted into the default kernel:
uname -r
6.1.0-37-amd64
I checked available GRUB entries:
grep "menuentry '" /boot/grub/grub.cfg | grep custom
Output showed:
Debian GNU/Linux, with Linux 6.1.0-custom+
To boot my kernel by default, I updated /etc/default/grub
:
sudo sed -i 's|^GRUB_DEFAULT=.*|GRUB_DEFAULT="Advanced options for Debian GNU/Linux>Debian GNU/Linux, with Linux 6.1.0-custom+"|' /etc/default/grub
sudo update-grub
Rebooting this time booted into my custom kernel.
Verification
After reboot:
uname -r
# Output: 6.1.0-custom+
Checking the kernel log:
dmesg | grep "Custom Kernel Loaded"
# Output: Custom Kernel Loaded Successfully
Lessons Learned
- Kernel logging macros (
pr_info
,printk
) must be inside functions. Placing them outside causes compilation errors. - Explicitly building
bzImage
is necessary ifmake
alone only compiles modules. - BTF debug info requires
pahole
. If missing, disableCONFIG_DEBUG_INFO_BTF
or installpahole
. - GRUB may default to older kernels. Update
GRUB_DEFAULT
to boot your custom kernel automatically. - On headless/cloud servers, configuring GRUB default is critical since interactive boot selection isn’t possible.
Conclusion
Building and booting a custom Linux kernel can be tricky but rewarding. Careful step-by-step work and troubleshooting will lead to success, deepening your understanding of Linux internals. This experience is invaluable for embedded development, kernel hacking, or custom OS work.
If you’d like to automate this process or have questions, feel free to reach out!
Happy hacking!