What you will learn:
Cross-toolchain setup
ARM kernel build
Raspberry Pi kernel 6.x
ARCH and CROSS_COMPILE variables
Kernel config for embedded targets
dtb device tree blobs
Deploying kernel to target
Why This Topic Matters
In the previous tutorials of Chapter 3, you learned how to build the Linux kernel for your own x86_64 machine.
That was building for the same architecture you are sitting on. Now things get more interesting.
In the real embedded world — whether you are working on a Raspberry Pi, an industrial ARM board, or a custom SoC — the device you are targeting is almost never powerful enough to compile its own kernel quickly. You need to build on a fast host machine (your laptop or desktop) and produce binaries that run on a completely different CPU architecture. This is called cross-compilation, and it is a fundamental skill every embedded Linux engineer must have.
We use the Raspberry Pi as our example target because it is affordable, widely available, and has excellent community support. The concepts here apply equally to any ARM-based embedded target.
When you compile a C program on your laptop and run it on the same laptop, that is called native compilation. The compiler produces instructions for the same CPU it is running on.
Cross-compilation is when your host machine (where you build) and your target machine (where the program runs) have different CPU architectures. The compiler on your x86_64 host produces binary code that runs on the ARM processor of the Raspberry Pi — not on your host itself.
| Aspect | Native Compilation | Cross-Compilation |
|---|---|---|
| Host CPU | x86_64 | x86_64 |
| Target CPU | x86_64 (same) | ARM (different) |
| Compiler used | gcc (system gcc) | arm-linux-gnueabihf-gcc |
| Output binary runs on | Your machine | Raspberry Pi |
| Build speed | Fast | Fast on host (much faster than building on the Pi itself) |
Why not just build on the Raspberry Pi itself? You can, but the Pi is slow for compilation. Building the full kernel on a Pi 4 can take 30–60 minutes or more. The same build on a modern x86_64 machine takes 5–10 minutes. For professional embedded development, cross-compilation is always the preferred approach.
We will walk through each of these three steps in detail. All commands are shown for a modern Ubuntu/Debian host running on x86_64, updated for kernel 6.x.
The official Raspberry Pi kernel is maintained in its own GitHub repository. Unlike the vanilla mainline kernel from kernel.org, the Raspberry Pi foundation maintains a fork with Raspberry Pi-specific drivers, overlays, and device tree files. As of 2024–2025, the Raspberry Pi kernel 6.x is available and actively maintained.
3.1 Set Up Your Workspace
First, create a dedicated staging directory. Using an environment variable keeps your commands clean and avoids hard-coding paths everywhere.
# Create a staging directory for our RPi cross-build work
export RPI_STG=~/rpi_work
# Create subdirectories: one for kernel source, one for toolchain
mkdir -p ${RPI_STG}/kernel_rpi ${RPI_STG}/rpi_tools
# Verify
ls ${RPI_STG}
3.2 Clone the Raspberry Pi Kernel Source (Kernel 6.x)
The Raspberry Pi foundation maintains branches named by kernel version. For kernel 6.6 (the current LTS), the branch is rpi-6.6.y. We use --depth=1 to fetch only the latest commit, which dramatically reduces download size and time.
cd ${RPI_STG}/kernel_rpi
# Clone the RPi kernel 6.6 LTS branch (shallow clone for speed)
git clone --depth=1 \
--branch rpi-6.6.y \
https://github.com/raspberrypi/linux.git
# This creates: ${RPI_STG}/kernel_rpi/linux/
Once the clone completes, let us verify we have the right version:
cd ${RPI_STG}/kernel_rpi/linux
# Check the top 5 lines of the Makefile
head -n5 Makefile
# Expected output (similar to):
# SPDX-License-Identifier: GPL-2.0
# VERSION = 6
# PATCHLEVEL = 6
# SUBLEVEL = xx
# EXTRAVERSION =
| Branch Name | Kernel Version | LTS? | Status (2025) |
|---|---|---|---|
| rpi-6.6.y | Linux 6.6.x | ✅ Yes | Recommended |
| rpi-6.1.y | Linux 6.1.x | ✅ Yes | Active |
| rpi-5.15.y | Linux 5.15.x | ✅ Yes | Older LTS |
| rpi-5.4.y | Linux 5.4.x | ✅ Yes | EOL — avoid for new projects |
A cross-toolchain is a set of tools — compiler, linker, assembler, standard library — that run on your x86_64 host but produce output for your ARM target. Think of it as a translation kit: you speak x86, but it writes ARM.
4.1 Understanding the Toolchain Prefix
The toolchain prefix encodes a lot of information. Let us break it down:
hf = Hard Float (uses FPU hardware registers for floating point, faster than soft-float)
4.2 Installing the Toolchain (Ubuntu/Debian)
The simplest method is installing from your distribution’s package manager. This is sufficient for most kernel builds.
# Update package lists
sudo apt update
# Install the ARM 32-bit cross-compiler (for RPi 1, 2, 3, Zero)
sudo apt install -y gcc-arm-linux-gnueabihf
# If you also need 64-bit ARM (RPi 3, 4, 5 in 64-bit mode)
sudo apt install -y gcc-aarch64-linux-gnu
# Verify the compiler is working
arm-linux-gnueabihf-gcc --version
# arm-linux-gnueabihf-gcc (Ubuntu ...) 13.x.x
4.3 Which Toolchain for Which Raspberry Pi?
| RPi Model | CPU | Default Kernel Mode | Toolchain Prefix |
|---|---|---|---|
| Pi Zero, Pi 1 | ARM1176 (ARMv6) | 32-bit | arm-linux-gnueabihf- |
| Pi 2, Pi 3 | Cortex-A7/A53 (ARMv7/v8) | 32-bit (default), 64-bit available | arm-linux-gnueabihf- |
| Pi 4 | Cortex-A72 (ARMv8) | 64-bit (Raspberry Pi OS 64-bit default) | aarch64-linux-gnu- |
| Pi 5 | Cortex-A76 (ARMv8.2) | 64-bit | aarch64-linux-gnu- |
aarch64-linux-gnu-gcc as your cross-compiler and set ARCH=arm64 in the build command. For Pi Zero and older boards, stick with arm-linux-gnueabihf- and ARCH=arm.This is where everything comes together. The kernel build system uses two important environment variables for cross-compilation:
5.1 Configure the Kernel
The Raspberry Pi kernel ships with default configuration files for each board family. Using these as your starting point is the correct approach — you do not need to configure every kernel option from scratch.
cd ${RPI_STG}/kernel_rpi/linux
# --- For Pi 2, Pi 3, Pi Zero 2W (32-bit, ARMv7/v8 in 32-bit mode) ---
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
# --- For Pi 4 (32-bit mode) ---
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig
# --- For Pi 4, Pi 5 (64-bit mode) ---
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
# --- For Pi 5 (64-bit) ---
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2712_defconfig
After running defconfig, a .config file is created in the kernel source root. This is your kernel configuration. You can optionally fine-tune it:
# Launch the menu-based configuration interface
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
menuconfig and go with the defconfig directly. You can always refine the configuration later once you have a working base build.5.2 Build the Kernel
Now trigger the actual compilation. The -j$(nproc) flag uses all available CPU cores to parallelize the build — this is what makes cross-compilation on a host machine so much faster than building on the Pi itself.
# For 32-bit ARM (Pi 2, Pi 3, Pi Zero)
make -j$(nproc) \
ARCH=arm \
CROSS_COMPILE=arm-linux-gnueabihf- \
zImage modules dtbs
# For 64-bit ARM (Pi 4, Pi 5)
make -j$(nproc) \
ARCH=arm64 \
CROSS_COMPILE=aarch64-linux-gnu- \
Image modules dtbs
| Target | Output File | What It Is |
|---|---|---|
| zImage | arch/arm/boot/zImage | Compressed kernel image for 32-bit ARM |
| Image | arch/arm64/boot/Image | Uncompressed kernel image for 64-bit ARM |
| modules | Various .ko files | Loadable kernel modules (device drivers, etc.) |
| dtbs | arch/arm/boot/dts/*.dtb | Device Tree Blobs — hardware description for each RPi board variant |
5.3 Install Modules
After the build completes, install the compiled modules into a staging directory. This directory will later be copied to the Raspberry Pi.
# Create a staging output directory
mkdir -p /tmp/rpi_kernel_output
# Install modules into the staging directory
make ARCH=arm \
CROSS_COMPILE=arm-linux-gnueabihf- \
INSTALL_MOD_PATH=/tmp/rpi_kernel_output \
modules_install
This creates a directory structure under /tmp/rpi_kernel_output/lib/modules/ with all the .ko files organized by kernel version.
After the build is done on your host, you need to copy the output files to the Raspberry Pi’s SD card. The Raspberry Pi boots from a FAT32 partition (usually the first partition, mounted as /boot or /boot/firmware).
Kernel modules (.ko files) go to Partition 2 under /lib/modules/.
6.1 Copy Files to the SD Card
Insert the RPi SD card into your host machine (via a card reader). It typically mounts automatically. Adjust the mount path to match your system.
# Assume SD card boot partition is mounted at /media/$USER/bootfs
# and root partition is mounted at /media/$USER/rootfs
BOOT_PART=/media/$USER/bootfs
ROOT_PART=/media/$USER/rootfs
# Copy kernel image (32-bit example)
cp ${RPI_STG}/kernel_rpi/linux/arch/arm/boot/zImage ${BOOT_PART}/kernel7.img
# Copy device tree blobs
cp ${RPI_STG}/kernel_rpi/linux/arch/arm/boot/dts/broadcom/*.dtb ${BOOT_PART}/
# Copy device tree overlays
cp -r ${RPI_STG}/kernel_rpi/linux/arch/arm/boot/dts/overlays/*.dtb* ${BOOT_PART}/overlays/
# Copy kernel modules to root filesystem
sudo cp -r /tmp/rpi_kernel_output/lib/modules/ ${ROOT_PART}/lib/
📌 Kernel filename on the boot partition:
kernel.img— Pi Zero, Pi 1 (ARMv6)kernel7.img— Pi 2, Pi 3 (ARMv7)kernel7l.img— Pi 4 (ARMv8 in 32-bit LPAE mode)kernel8.img— Pi 3, Pi 4, Pi 5 (64-bit)
7.1 Export Variables Once, Use Everywhere
Instead of typing ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- before every command, export them as environment variables once in your session:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# Now you can type shorter make commands
make bcm2709_defconfig
make -j$(nproc) zImage modules dtbs
7.2 Save and Restore Your Kernel Config
If you spend time customizing menuconfig, always back up your .config:
# Save the current config as a defconfig for later reuse
make savedefconfig
# This creates a minimal defconfig file called 'defconfig'
# Store it safely
cp defconfig ~/my_rpi_defconfig.conf
7.3 Check the Toolchain Is Generating ARM Code
After building, you can verify the kernel image is really an ARM binary:
# Check ELF header of the vmlinux file
file vmlinux
# Expected output should say something like:
# vmlinux: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)...
# or for 64-bit:
# vmlinux: ELF 64-bit LSB pie executable, ARM aarch64...
7.4 Build Time Estimates
| Build Method | Machine | Approximate Build Time |
|---|---|---|
| Cross-compile on x86_64 host | Modern 8-core laptop | 5–10 minutes |
| Native build on the Pi itself | Raspberry Pi 4 (4 cores) | 45–90 minutes |
| Native build on the Pi itself | Raspberry Pi 3 (4 cores) | 2–4 hours |
| Cross-compile inside a VM | 4-core VM on the same laptop | 10–25 minutes (slower than native host) |
This is why cross-compilation is not optional in professional embedded development — it saves enormous amounts of time over the course of a project.
Here is a clean, end-to-end script summarizing everything covered in this tutorial. This is for a Pi 4 (32-bit mode) with kernel 6.6:
#!/bin/bash
# ======================================================
# Cross-compile Linux kernel 6.6 for Raspberry Pi 4
# Host: Ubuntu 22.04 or 24.04 x86_64
# ======================================================
# --- Setup ---
export RPI_STG=~/rpi_work
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
mkdir -p ${RPI_STG}/kernel_rpi /tmp/rpi_output
# --- Step 1: Get kernel source ---
cd ${RPI_STG}/kernel_rpi
git clone --depth=1 --branch rpi-6.6.y \
https://github.com/raspberrypi/linux.git
cd linux
# --- Step 2: Install toolchain ---
sudo apt install -y gcc-arm-linux-gnueabihf
# --- Step 3: Configure ---
make bcm2711_defconfig
# --- Step 4: Build ---
make -j$(nproc) zImage modules dtbs
# --- Step 5: Install modules ---
make INSTALL_MOD_PATH=/tmp/rpi_output modules_install
echo "Build complete! Output in /tmp/rpi_output"
echo "Kernel: arch/arm/boot/zImage"
echo "DTBs: arch/arm/boot/dts/broadcom/"
ARCH tells the kernel build system which architecture to target, causing it to include the correct architecture-specific source files and Kconfig options. CROSS_COMPILE specifies the prefix for all toolchain programs — so the Makefile calls arm-linux-gnueabihf-gcc instead of the host gcc, ensuring all compiled code targets the ARM architecture rather than x86_64.zImage is a self-decompressing compressed kernel image used in 32-bit ARM systems. It is smaller to store on the boot medium and decompresses itself into RAM at boot time. Image is a raw, uncompressed kernel binary used in 64-bit ARM (AArch64) systems. In AArch64, the bootloader (like U-Boot or the RPi firmware) typically handles any decompression externally, so the kernel itself is kept as a plain binary.-j$(nproc) flag. nproc is a shell command that returns the number of available processor cores. Passing this to make via -j enables parallel compilation — each core works on a different source file simultaneously. For a kernel with thousands of source files, this reduces build time dramatically, often by a factor of 4–8x compared to a single-threaded build.make modules_install installs modules to /lib/modules/<kernel-version>/ on the host. For cross-builds destined for a target device, you override this with INSTALL_MOD_PATH=/some/staging/dir. This places the modules under /some/staging/dir/lib/modules/, which you then copy to the target’s root filesystem — ensuring you do not accidentally overwrite your host’s own kernel modules.ARCH=arm builds a 32-bit ARMv7/ARMv8 kernel in 32-bit compatibility mode, producing code that runs on all Raspberry Pi models starting from Pi 2. ARCH=arm64 builds a true 64-bit AArch64 kernel, which can only run on Pi 3 and newer (which have 64-bit capable Cortex-A53/A72/A76 CPUs). The 64-bit kernel can address more RAM, has a larger virtual address space, and can use 64-bit CPU registers natively — but will not boot on a Pi Zero or Pi 1.Keep Learning — Free Embedded Linux Courses at EmbeddedPathashala
This is part of our completely free Linux Kernel Programming series. No fees, no sign-up walls — just solid technical content for engineering students and working engineers.
