Skip to content

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:

aarch64-linux-gnu-gcc --version

📥 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

scp pi:/boot/config-6.12.47+rpt-rpi-2712 ../pi5.config

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:

6.12.47-v8-16k+

Target:

6.12.47+rpt-rpi-2712

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:

make LOCALVERSION= kernelrelease

🛠️ Prepare Build Tree

make prepare
make modules_prepare

Verify host tools:

file scripts/basic/fixdep

Expected:

x86-64 executable

❗ Common Issue #1: Exec format error

Error:

fixdep: Exec format error

Cause

  • Copied ARM binary tools from Pi
  • Trying to execute on x86 WSL

Fix

👉 Always run:

make prepare
make modules_prepare

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:

WARNING: Module.symvers is missing
Unknown symbol _printk

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:

ls /usr/src/linux-headers-$(uname -r)/Module.symvers

Copy to WSL:

scp pi:/usr/src/linux-headers-6.12.47+rpt-rpi-2712/Module.symvers 
~/workspace/linux/

Rebuild module:

make clean
make

🚀 Deploy and Test

Copy to Pi

scp hello.ko pi:~

Load module

sudo insmod hello.ko
dmesg | tail

Expected:

Hello Kernel Module Loaded

Verify

lsmod | grep hello

Unload

sudo rmmod hello
dmesg | tail

Expected:

Hello Kernel Module Unloaded

❗ Common Issue #3: Invalid parameters

Error:

insmod: Invalid parameters

Real cause

Kernel returned -EINVAL

👉 Always check:

dmesg | tail

🧠 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

uname -r
vermagic

3. LOCALVERSION matters

Controls:

6.12.47+rpt-rpi-2712

4. + means dirty tree

Fix with:

LOCALVERSION=

5. Module.symvers is critical

Without it:

  • symbol mismatch
  • module load fails

6. vermagic is not enough

Even if:

vermagic matches

Still possible to fail due to symbol version mismatch


🧭 Final Workflow

WSL:
prepare kernel tree
build module (.ko)
scp to Pi

Pi:
insmod
dmesg
rmmod

🎯 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