Cross compiling Linux ARM kernel modules

This guide will allow you to cross-compile a loadable kernel module (LKM; a.k.a. device driver) for a ARM Linux system.

1. Target system

I will use this configuration as an example, but you can apply the same method for other environments.

  • ARMv7 (32-bit)
  • ARM qemu emulating vexpress-a9 board
  • Linux is running in qemu.

2. Download linux kernel source

Download the kernel source from

You must download the exact version which is running in the qemu.

Note that source for 3.2.0 is named linux-3.2.tar.gz, not linux-3.2.0.tar.gz.

3. Download cross compiler toolchain

Linaro’s prebuilt toolchain generally works well. Download one from

Pick a version, and choose the appropriate architecture. In our case, it would be arm-linux-gnueabihf (ARM 32-bit, linux, little endian, hard float).

There are three kinds of files: gcc-linaro-, runtime-gcc-linaro-, and sysroot-eglibc-linaro-. You only need the first one. For more info, refer to this Linaro wiki page.

For instance, go to 4.9-2017.01/arm-linux-gnueabihf/ directory and download gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz.

4. Take out kernel build config

We need to build the kernel first, and then build a kernel module. But to compile a kernel, we must have the exact build configuration of the currently running Linux system. Fortunately, you can get a copy from a running system. Look at these locations:

  • /proc/config.gz
  • /boot/config
  • /boot/config-*

Copy the file out of the qemu using scp or something.

5. Build the kernel

You need auto-generated files in order to build a kernel module. Otherwise you may encounter an error message like this:

/home/ubuntu/linux-3.2/include/linux/kconfig.h:4:32: fatal error: generated/autoconf.h: No such file or directory
    #include <generated/autoconf.h>

To build a kernel with given config file,

cp <CONFIG_FILE> .config
make ARCH=arm CROSS_COMPILE=<TOOLCHAIN_DIR>/bin/arm-linux-gnueabihf- oldconfig
make ARCH=arm CROSS_COMPILE=<TOOLCHAIN_DIR>/bin/arm-linux-gnueabihf-

Complete kernel build may not be necessary because what you need is generated header files.

6. Build the module

Write a Makefile as follows:

PWD := $(shell pwd)
obj-m += hello.o

        make ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KERNEL) SUBDIRS=$(PWD) modules
        make -C $(KERNEL) SUBDIRS=$(PWD) clean

And create a hello world module.

// hello.c
#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void) {
    printk(KERN_INFO "Hello world.\n");
    return 0;

void cleanup_module(void) {
    printk(KERN_INFO "Goodbye world.\n");

Finally run this command.

make KERNEL=<LINUX_SOURCE_DIR> CROSS=<TOOLCHAIN_DIR>/bin/arm-linux-gnueabihf-

Then you will get hello.ko compatible with the running ARM Linux.

Other posts (list)

HITCON CTF Quals 2017 - Impeccable Artifact
HITCON CTF Quals 2017 - Seccomp
Cross compiling Linux ARM kernel modules
CCE 2018 Quals - VNC
Golang-like Defer in C++