Day07 - Cross-Compile Kernel Module for Raspberry Pi 5¶
🎯 Goal¶
- Build a clean cross-compilation environment on Ubuntu 24.04 (WSL)
- Target device: Raspberry Pi 5
- Running kernel:
6.12.47+rpt-rpi-2712 - Cross-compile a kernel module on WSL
- Successfully load (
insmod) on Raspberry Pi
🧭 Overview¶
This lab focuses on external kernel module cross-compilation, not full kernel build.
Key idea:
Build module on host (WSL) → deploy to target (Pi) → load into running kernel
🏗️ Environment Setup¶
Install toolchain (WSL)¶
sudo apt update
sudo apt install -y
git bc bison flex libssl-dev make libc6-dev
libncurses-dev crossbuild-essential-arm64 dwarves
Check:
📥 Get Kernel Source (Optimized Clone)¶
Avoid full clone (too large). Use:
git clone --filter=blob:none
--branch stable_20250916
--single-branch
--depth=100
https://github.com/raspberrypi/linux.git
⚙️ Prepare Kernel Config¶
1. Copy running config from Pi¶
2. Apply config¶
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make bcm2712_defconfig
cp ../pi5.config .config
make olddefconfig
🔧 Fix Kernel Release (Critical)¶
Initial result:
Target:
Fix LOCALVERSION¶
scripts/config --set-str LOCALVERSION "+rpt-rpi-2712"
scripts/config --disable LOCALVERSION_AUTO
make olddefconfig
Remove trailing '+'¶
Kernel adds + if source tree is "dirty".
Fix by forcing:
🛠️ Prepare Build Tree¶
Verify host tools:
Expected:
❗ Common Issue #1: Exec format error¶
Error:
Cause¶
- Copied ARM binary tools from Pi
- Trying to execute on x86 WSL
Fix¶
👉 Always run:
This rebuilds host tools correctly.
🧩 Build External Module¶
hello.c¶
#include <linux/module.h>
#include <linux/init.h>
static int __init hello_init(void)
{
pr_info("Hello Kernel Module Loaded\n");
return 0;
}
static void __exit hello_exit(void)
{
pr_info("Hello Kernel Module Unloaded\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Makefile¶
KERNEL_DIR := /home/user/workspace/linux
PWD := $(shell pwd)
obj-m := hello.o
all:
make -C $(KERNEL_DIR)
ARCH=arm64
CROSS_COMPILE=aarch64-linux-gnu-
LOCALVERSION=
M=$(PWD)
modules
clean:
make -C $(KERNEL_DIR)
ARCH=arm64
CROSS_COMPILE=aarch64-linux-gnu-
LOCALVERSION=
M=$(PWD)
clean
❗ Common Issue #2: Missing Module.symvers¶
Error:
Cause¶
- Symbol version mismatch
- Kernel exports and module expectations differ
🔥 Critical Concept: Module.symvers¶
Matching kernel version is NOT enough Symbol version must also match
✅ Fix: Use Target Module.symvers¶
On Pi:
Copy to WSL:
Rebuild module:
🚀 Deploy and Test¶
Copy to Pi¶
Load module¶
Expected:
Verify¶
Unload¶
Expected:
❗ Common Issue #3: Invalid parameters¶
Error:
Real cause¶
Kernel returned -EINVAL
👉 Always check:
🧠 Key Learnings¶
1. Cross-compilation has TWO worlds¶
| Type | Runs on |
|---|---|
| Host tools (fixdep) | x86 (WSL) |
| Target code (.ko) | ARM (Pi) |
2. kernelrelease must match exactly¶
3. LOCALVERSION matters¶
Controls:
4. + means dirty tree¶
Fix with:
5. Module.symvers is critical¶
Without it:
- symbol mismatch
- module load fails
6. vermagic is not enough¶
Even if:
Still possible to fail due to symbol version mismatch
🧭 Final Workflow¶
🎯 Result¶
✅ Successfully cross-compiled kernel module ✅ Loaded module on Raspberry Pi ✅ Verified execution in kernel space
🚀 Next Steps¶
- Convert hello → character device driver
- Add module parameters
- Learn
modprobe/depmod - Debug with
dmesg -w