1. Overview
A kernel module provides a way to extend kernel functionality without rebuilding the entire kernel. Using the Buildroot toolchain ensures the module is ABI‑compatible with the kernel generated during your Buildroot build. QEMU then offers a convenient emulation environment to test modules without hardware.
Linux kernel modules are dynamically loadable pieces of code that extend the functionality of the kernel without requiring a reboot or recompilation. They are widely used for device drivers, filesystems, and various kernel extensions.
Modules can be loaded into the kernel using insmod or modprobe, and removed using rmmod.
The .ko file is the loadable kernel object file, which is an enhanced version of .o with kernel metadata.
2. Prepare the Workspace
Create a folder to store the module sources.
mkdir -p ~/project/kernel-study/0.hello-world/hello
cd ~/project/kernel-study/0.hello-world
3. Writing a Minimal Kernel Module
Create hello.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rishav");
MODULE_DESCRIPTION("Simple Hello World Module");
static int hello_init(void)
{
pr_info("Hello World from Buildroot module!");
return 0;
}
static void hello_exit(void)
{
pr_info("Goodbye from Buildroot module!");
}
module_init(hello_init);
module_exit(hello_exit);
Initialization function: called when the module is loaded, and will not be stored in kernel’s memory. Cleanup function: called when the module is removed.
4. Create the Makefile
Update the paths to match your Buildroot output directory.
obj-m := hello.o
BUILDROOT_SRC_DIR=~/project/Platform/buildroot
KDIR := $(BUILDROOT_SRC_DIR)/output/build/linux-*/
CROSS_COMPILE := $(BUILDROOT_SRC_DIR)/output/host/bin/aarch64-linux-
all:
$(MAKE) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) clean
KDIRmust point to the kernel build directory created by Buildroot.
5. Build the Module
Run:
make
The output should include:
hello.ko
This is the loadable kernel module.
6. Start QEMU
Use the Buildroot-generated QEMU launch script with an added port forwarding option:
-netdev user,id=eth0,hostfwd=tcp::2222-:22
-device virtio-net-device,netdev=eth0
Using this, the port 22 from target/guest machine which is used for ssh can be accessed at port 2222 in host machine.
Now, transfer .ko file inside target machine
scp -P 2222 hello.ko root@localhost:/root/
7. Insert the Kernel Module
Load the module:
insmod /mnt/host/hello.ko
Check the logs to see expected output:
$ dmesg | tail
Hello World from Buildroot module!
8. Remove the Module
Unload it:
rmmod hello
dmesg | tail
Expected output:
Goodbye from Buildroot module!

9. Summary: Understanding __init and __exit Macros
The Linux kernel provides special section markers:
__init
- Marks functions that are only used at initialization (e.g.,
my_init). - Memory used by these functions is freed after initialization completes.
__exit
- Marks functions used only at module removal (e.g.,
my_cleanup). - If the module is built into the kernel (not loadable), these functions are discarded because they are never used.
These macros are defined in <linux/init.h>
This setup is ideal for iterative development of kernel drivers, subsystems, and platform code without needing hardware.