Kernel Build Troubleshooting-Free Linux kernel development course

 

Previous Lecture
Next Lecture

Kernel Build Troubleshooting
Chapter 4 · LKMs Part 1 · Lesson 3 of 3
Fix Common Build Errors, Cross-Compilation, Yocto & Buildroot
🔧 Practical Fixes
⏱ 20 min read
🐧 Kernel 6.x
💡 Interview Q&A

📌 Keywords — Rank for Free Linux Kernel Course
linux kernel build errors fix
openssl headers missing kernel
PIE PIC kernel build
cross compile linux kernel ARM
Yocto vs Buildroot embedded linux
free linux kernel development course
free embedded systems course
kernel 6.x compile ubuntu fedora

Anyone who has compiled the Linux kernel from source has hit at least one confusing error message. This lesson is your troubleshooting reference. We cover the most common errors that appear when building the kernel — including the OpenSSL header issue and the PIE/PIC compiler mismatch — explain what causes them at a deeper level, and show you exactly how to fix them on modern distributions.

We also introduce Yocto and Buildroot, the two major frameworks used in embedded Linux product development, and finish with practical advice on what to do when you hit an error you cannot immediately solve.

1. Installing All Required Dependencies First

Most kernel build failures come down to one thing: a missing package. The kernel’s own documentation lists its minimum requirements in Documentation/process/changes.rst inside the kernel source tree. Always check this file for the kernel version you are building, because the requirements change between major versions.

For kernel 6.x on the major distributions, here is what you need before you even start:

# Ubuntu / Debian (kernel 6.x)
sudo apt update
sudo apt install build-essential flex bison \
    libssl-dev libelf-dev libncurses-dev \
    bc dwarves pahole git

# Fedora / RHEL / Rocky Linux (kernel 6.x)
sudo dnf install gcc make flex bison \
    openssl-devel elfutils-libelf-devel ncurses-devel \
    bc dwarves git

# Arch Linux
sudo pacman -S base-devel flex bison openssl \
    libelf ncurses bc pahole git
📝 New in Kernel 5.16+: pahole / dwarves
Starting from kernel 5.16, the BPF CO-RE (Compile Once, Run Everywhere) feature requires pahole (part of the dwarves package) to generate BTF (BPF Type Format) information during the build. If you see an error about BTF generation or pahole not found, install the dwarves package. This is a new requirement compared to older kernel build guides.

2. Error: openssl/opensslv.h — No Such File or Directory

This is one of the most commonly encountered kernel build errors, especially on fresh installations. Here is what it looks like:

❌ Error Message
HOSTCC scripts/sign-file
scripts/sign-file.c:25:10: fatal error: openssl/opensslv.h: No such file or directory
#include <openssl/opensslv.h>
^~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.host: scripts/sign-file] Error 1
make: *** [Makefile: scripts] Error 2
✅ Cause
Starting from kernel 4.3, the kernel needs OpenSSL development headers to compile the sign-file utility, which is used to digitally sign kernel modules. The OpenSSL runtime library (libssl) is usually already installed on most systems, but the development headers (the .h files that compiler needs) are a separate package that is not installed by default.
✅ Fix
# Ubuntu / Debian:
sudo apt install libssl-dev

# Fedora / RHEL:
sudo dnf install openssl-devel

# Raspberry Pi OS:
sudo apt install libssl-dev

# After installing, simply re-run your make command
make -j$(nproc)

The reason the development headers are separate from the runtime is a deliberate packaging decision in Linux distributions: end users only need the runtime library to run programs that use SSL. Developers who compile software against OpenSSL need the headers additionally. The kernel build system is a developer tool, so you need both.

3. Error: Kernel Does Not Support PIC Mode / -fPIC

This error appeared more commonly when building older kernels with newer GCC versions, but understanding it is important for any embedded Linux developer.

❌ Error Message (Older Kernels, e.g. 4.x)
Error: Kernel compile with -fPIC is not supported
… or …
gcc: error: unrecognized command line option ‘-fno-PIE’
✅ What Are PIC and PIE?

PIC = Position Independent Code. Code that can run at any memory address without modification. Required for shared libraries (.so files) in user space.

PIE = Position Independent Executable. A regular executable compiled with PIC, enabling ASLR (Address Space Layout Randomisation) for better security.

Some distributions configure GCC to generate PIE by default for all code. The Linux kernel cannot be compiled as a PIC/PIE binary — it has its own position-dependent linking and load-time relocation mechanism. Modern kernels (5.x, 6.x) explicitly pass -fno-PIE and -fno-pic compiler flags to disable PIE/PIC mode. This is why you will see -fno-PIE in the kernel build output on kernel 6.x — it is working correctly.

✅ Fix for Kernel 6.x
On kernel 6.x, this is handled automatically. If you are building a very old kernel (3.x or early 4.x) on a modern distribution with PIE-enabled GCC, the workaround is to pass CFLAGS=-fno-PIE to make — but for any current project, just use a kernel version from the 5.x or 6.x series and the build system handles this for you.

4. Other Common Build Errors — Quick Reference

❌ Error: flex / bison not found
scripts/kconfig/zconf.tab.c:2579:15: fatal error: zconf.lex.c: No such file or directory
… or …
make: flex: No such file or directory
✅ Fix
# Ubuntu:
sudo apt install flex bison
# Fedora:
sudo dnf install flex bison
❌ Error: BTF generation / pahole not found (Kernel 5.16+)
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
✅ Fix — Option A: Install pahole
# Ubuntu:
sudo apt install dwarves
# Fedora:
sudo dnf install dwarves
✅ Fix — Option B: Disable BTF in config (not recommended for production)
scripts/config --disable CONFIG_DEBUG_INFO_BTF
make -j$(nproc)
❌ Error: No rule to make target ‘debian/canonical-certs.pem’
make[1]: *** No rule to make target ‘debian/canonical-certs.pem’,
needed by ‘certs/x509_certificate_list’
✅ Cause & Fix
This happens when you use a kernel .config file copied from a distribution kernel (like Ubuntu’s). The distribution has its own code-signing certificate. Fix by clearing the certificate config options:
scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS
make -j$(nproc)

Kernel Build Troubleshooting Decision Flow
Build fails with an error
Read the FULL error message — look for the first Error line
Contains “No such file”
→ Missing header/tool
→ Install the package
Contains “certificate”
→ Signing key issue
→ Clear cert config
Contains “BTF/pahole”
→ Install dwarves
→ or disable BTF
Unknown error
→ Copy exact msg
→ Search: “linux kernel 6.x ” + error
Apply fix → make -j$(nproc) again

5. The Most Reliable Troubleshooting Technique

When you hit a kernel build error that you cannot immediately recognise, there is one technique that works more often than you might expect: copy the exact error message, open a search engine, and search for something like linux kernel 6.x build fails openssl/opensslv.h. Include the kernel version and the key part of the error.

The Linux kernel community is vast and well-documented. Chances are very high that someone else has hit the same error on the same distribution and posted a working fix. The kernel’s own mailing list archives, Stack Overflow, the Arch Linux wiki, and the Ubuntu Ask community are especially good sources.

💡 Pro Tip: Use nproc for Parallel Builds
Always use make -j$(nproc) instead of plain make. The nproc command returns the number of available CPU cores on your system. Building in parallel dramatically reduces build time — from potentially 45 minutes to under 10 minutes on a modern multi-core machine. On a 4-core machine, make -j4 builds roughly 4 times faster than a single-threaded build.

6. Embedded Linux: Yocto and Buildroot

In professional embedded Linux product development, you rarely build the kernel by hand using the steps described in this series. Instead, you use a Linux builder framework. These are complete build systems that take a hardware description, a list of packages, and a kernel configuration, and produce an entire bootable Linux image — kernel, root filesystem, bootloader, and all.

Yocto vs Buildroot — What Goes In, What Comes Out
YOCTO PROJECT
Inputs (Recipes / Layers)
BSP layer + distro layer + app layer → bitbake
Output
Complete Linux image, SDK, package feed
Best For
Complex products, large teams, reproducible releases, automotive / industrial
BUILDROOT
Inputs (Kconfig + package list)
make menuconfig → make
Output
Minimal Linux image (kernel + rootfs)
Best For
Simple embedded systems, individual developers, quick prototyping, IoT devices

6.1 What a BSP Team Does

In larger companies, a BSP (Board Support Package) team is responsible for porting Linux to new hardware. Their job includes writing or adapting the bootloader (usually U-Boot), writing the device tree source (DTS) files that describe the hardware to the kernel, and configuring and building the kernel for the specific SoC. If you join an embedded Linux team, understanding the kernel build process deeply — as covered in this series — means you can understand and contribute to BSP work even if a framework like Yocto automates the mechanics.

6.2 Why You Still Need to Understand Manual Builds

Even when Yocto or Buildroot is used in a project, there are situations where manual kernel knowledge is essential:

  • Debugging a kernel issue requires reading and understanding kernel config options directly.
  • Writing a new device driver means testing it by manually building and loading a kernel module — not running a full Yocto build for every change.
  • Kernel configuration tuning for performance or security is done at the make menuconfig level regardless of which framework is used on top.
  • Cross-compilation concepts — target architecture, cross-compiler prefix, sysroot — are the same whether you use Buildroot or do it manually.

7. Cross-Compilation Basics

When your development machine is x86-64 but you are building a kernel for an ARM device (like a Raspberry Pi or an i.MX board), you need a cross-compiler — a compiler that runs on x86-64 but produces ARM binaries.

# Install an ARM64 cross-compiler on Ubuntu
sudo apt install gcc-aarch64-linux-gnu

# Set environment variables before building the kernel
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# Now run make as normal — it will use the cross-compiler
make -j$(nproc)

# For 32-bit ARM (e.g. older Raspberry Pi models):
sudo apt install gcc-arm-linux-gnueabihf
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make -j$(nproc)
📝 ARCH and CROSS_COMPILE in Kernel 6.x
ARCH tells the kernel’s build system which CPU architecture to build for. Valid values include x86, x86_64, arm, arm64, riscv, mips, and others. CROSS_COMPILE is the prefix for the cross-compiler toolchain binaries — so aarch64-linux-gnu- means the build system will call aarch64-linux-gnu-gcc, aarch64-linux-gnu-ld, etc. These same variables work identically in kernel 4.x, 5.x, and 6.x.

8. Verifying a Successful Build

After a successful kernel build, here is how to confirm everything is in order before installing:

# Check the main kernel image was produced
ls -lh arch/x86/boot/bzImage       # x86-64
ls -lh arch/arm64/boot/Image.gz    # ARM64

# Check kernel modules were built
find . -name "*.ko" | wc -l   # Should show hundreds of modules

# Check the kernel version string embedded in the build
strings arch/x86/boot/bzImage | grep "Linux version"

# Check module signing configuration
grep CONFIG_MODULE_SIG .config

# View build configuration summary
make listnewconfig   # Shows new Kconfig options not yet set in .config

🎯 Interview Questions & Answers

Q1. Why does the Linux kernel build fail with “openssl/opensslv.h: No such file or directory”?
From kernel version 4.3 onwards, the build system compiles a utility called sign-file that digitally signs kernel modules. This tool uses the OpenSSL cryptographic library. The build system needs the OpenSSL development headers (the .h files) — not just the runtime library. On Ubuntu/Debian, installing libssl-dev fixes this. On Fedora/RHEL, the package is called openssl-devel.
Q2. What is PIE/PIC and why can’t the Linux kernel be compiled with -fPIC?
PIC (Position Independent Code) is code that works regardless of where it is loaded in memory — required for shared libraries and for ASLR security. PIE (Position Independent Executable) applies the same concept to full executables. The Linux kernel cannot use PIC/PIE because it uses fixed absolute addresses in its own linking and relocation mechanism. It has its own boot-time relocation scheme (KASLR — Kernel Address Space Layout Randomisation) that is fundamentally different from userspace PIE. Modern kernels (5.x, 6.x) explicitly pass -fno-PIE to the compiler to enforce this.
Q3. What is the purpose of the ARCH and CROSS_COMPILE variables when building the kernel?
ARCH tells the kernel’s kbuild system which CPU architecture to target (e.g. arm64, x86_64, riscv). It selects the right architecture-specific source directories and Makefiles. CROSS_COMPILE provides the prefix of the cross-compiler toolchain binaries so that kbuild uses aarch64-linux-gnu-gcc instead of the host’s native gcc. Together these two variables completely control the target architecture of the build.
Q4. What is Yocto and why is it widely used in embedded Linux products?
Yocto is an open-source project and build framework for creating custom embedded Linux distributions. It uses a recipe-based system (BitBake) where developers write layers describing hardware (BSP layers), software packages, and distribution policies. Yocto can produce a complete, reproducible Linux image — bootloader, kernel, root filesystem, and SDK — for almost any hardware target. It is the industry-standard choice for large embedded projects (automotive, industrial, medical) because it supports long-term maintenance, license compliance tracking, and integration with commercial toolchains.
Q5. What is a BSP and what does a BSP engineer do?
BSP stands for Board Support Package. It is the collection of code that allows Linux to run on a specific hardware platform — typically including a bootloader port, device tree source files (DTS) describing the hardware topology, SoC-specific drivers, and a kernel configuration tuned for the board. A BSP engineer ports and validates Linux on new hardware, writes or adapts device tree files, works with silicon vendors, and produces the base platform on which application developers build their products.
Q6. What is the difference between Yocto and Buildroot?
Both are embedded Linux build frameworks, but they differ in scope and complexity. Yocto is a full distribution-building framework with a powerful but steep learning curve — it supports binary package feeds, SDK generation, reproducible builds, and scales well to large teams. Buildroot is simpler and faster to learn — it uses a Kconfig-based menu system and produces a minimal, non-updateable root filesystem image. For small IoT devices or individual projects, Buildroot gets you running quickly. For product lines that need long-term updates and package management, Yocto is the right choice.
Q7. What does “tainted kernel” mean?
A tainted kernel is a running Linux kernel that has been modified in ways the kernel developers consider potentially unreliable or unsupported. Common taint reasons include: loading a non-GPL kernel module, loading a module that was not built for this exact kernel version, a kernel oops occurring (soft error), or proprietary hardware firmware loading. The taint status is shown in kernel oops messages and can be read from /proc/sys/kernel/tainted. Kernel developers may decline to debug issues on a tainted kernel since the cause could be the untrusted code.
Q8. Why should you always use make -j$(nproc) when building the kernel?
The Linux kernel build involves compiling thousands of C source files. By default, plain make builds files sequentially — one at a time. make -j$(nproc) tells make to run as many parallel jobs as there are CPU cores. On a 4-core machine this reduces build time from 30-45 minutes to under 10 minutes. The $(nproc) subcommand automatically returns the correct number of cores, making the command portable across different machines.

✅ Chapter 4 LKM Part 1 — What You Have Learned

  • How the Linux virtual address space is split into user space (Ring 3) and kernel space (Ring 0)
  • Why Linux is classified as a monolithic kernel and how system calls bridge the two worlds
  • How LKMs let you extend the running kernel without rebooting
  • The complete LKM lifecycle: compile → load → init → run → exit → unload
  • How to write a minimal Hello World kernel module with module_init, module_exit, printk
  • The kbuild Makefile pattern used to build any kernel module
  • How to fix the most common kernel build errors including OpenSSL headers and PIE issues
  • What Yocto and Buildroot are and when each is used in embedded Linux product development

📚 EmbeddedPathashala — Free Linux Kernel Development Course

You have completed the first part of the LKM series! The next chapter dives into kernel module parameters, symbol exporting, kernel data structures, and writing a real character device driver.

Previous Lecture
Next Lecture

Leave a Reply

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