N06 — Kernel module on GKI
Build an out-of-tree module against a GKI kernel, load it, and watch the KMI decide whether it is allowed. The vendor side of the kernel seam, and why a module that compiles can still refuse to load.
GKI is the arrangement where the core kernel is a Google-built binary and the device-specific drivers are loadable modules built separately, by someone else, on a different machine. The two have to work together without being compiled together, and the KMI is the contract that makes that possible. Building one module is the cheapest way to feel the contract.
Hardware note: beyond a normal build host, this needs a GKI kernel tree, the matching toolchain, and a device or virtual target whose vendor image you can change.
TL;DR#
- GKI moves SoC and board support out of the core kernel into loadable vendor modules, behind a stable Kernel Module Interface.1
- The KMI is only stable within the same LTS and Android version of a kernel. No stability is
promised for
android-mainline.2 - Only symbols on the symbol list are KMI symbols, and vendor modules must use only those.
- The check happens at load time. A module that needs a non-KMI symbol fails to load.
gki_defconfigis the single configuration used to build the GKI kernel.
What you are building#
A trivial module — something like a character device that logs on load — built out of tree against a GKI kernel, packaged into a vendor partition, and loaded. The module itself is not the point. The point is the three things that must line up:
- The module was built against the right kernel and toolchain.
- It references only KMI symbols.
- It lives in a partition that is allowed to carry it.
The contract, concretely#
| Rule | Where it comes from | What it means for you |
|---|---|---|
| Same LTS and Android version | Stable KMI page | A module built for one kernel generation will not load on another |
| Symbol list only | Stable KMI page | Every external symbol you call must be on the list |
| Load-time enforcement | Stable KMI page | The failure is at insmod, not at compile time |
gki_defconfig only | Stable KMI page | You cannot rely on a config option you enabled yourself |
Modules early in boot go in vendor_boot | Kernel modules page | Anything needed before the vendor partition is mounted must be there |
That last row is the one that surprises people: a module that is correct in every other respect is still useless if it is in the wrong partition at the wrong time.
Steps#
- Get the kernel tree
Follow the AOSP build-kernels instructions to download the kernel sources and their build tooling. The kernel tree is separate from the platform tree and is synced with its own manifest; do not assume a platform checkout contains it.
You should seeA kernel source tree with the Kleaf build tooling. - Match the generation
The KMI is stable only within the same LTS and Android version of a kernel. Pick the branch that matches the device you will load the module on, not the newest branch available.
2You should seeA kernel branch whose LTS and Android version match your device. - Write the module
Keep it deliberately small: one
module_init, onemodule_exit, one printk. The first build should prove the toolchain and the header plumbing before you add a single feature.You should seeA single .c file with an init and exit function. - Declare it for the build
Kleaf and the DDK are the modern way to declare an out-of-tree module; the declaration names the source files, the output module, and the kernel build it targets. The kernel build tooling documents the exact rule name and its properties, and they have changed between releases — take the declaration from the kernel build documentation for your branch rather than from a snippet here.
3You should seeA build target that produces a .ko. - Build it
The kernel build page gives the distribution commands for the GKI kernel and for the virtual-device vendor modules, both of which are
4tools/bazel runinvocations of a_disttarget. The kernel binary, the modules and the images land in a distribution directory.Those targets build the kernel's own module set. For a module you wrote, the command is the one the DDK documentation gives for your module's target; the exact target name depends on where you put the
BUILD.bazel.You should seeA .ko file in the output directory. - Check the symbol usage
Inspect the module's undefined symbols and compare them with the symbol list for your kernel generation. The stable KMI page names the ABI description files (for example
2gki/aarch64/abi.stg) and says the tree also ships the tools to create and compare the description.This is the step to do before you flash anything. A non-KMI symbol is a guaranteed load failure on the device.
You should seeEvery external symbol is on the KMI list. - Package it into the right partition
Vendor modules can be delivered in the
vendororvendor_bootimage, and modules required early in the boot process must be invendor_boot.3 The board variables that list them are documented on the loadable-kernel-modules page; the exact variable names belong to the make-based integration and should be copied from there.You should seeThe module is present in vendor or vendor_boot. - Load it
On a userdebug build you can load a module by hand before wiring it into boot. Pushing the file and calling
insmodis the fastest way to see whether the contract holds; the error message on failure names the symbol or the version mismatch.You should seeThe module is listed by lsmod and the printk appears in dmesg. - Wire it into boot, if it belongs there
A module that must be present at boot is loaded by the system from the partition it lives in, not by a shell command. That is the difference between a test and a deployment. The exact list file and loader depend on the partition and the release; the kernel modules page describes the placement rules, and the loadable-kernel-modules page covers loading.
34You should seeThe module loads without manual intervention.
adb push toy_module.ko /data/local/tmp/
adb shell su -c insmod /data/local/tmp/toy_module.ko
adb shell lsmod | grep toy
adb shell dmesg | tail -20Example output
Illustrative — module names and output differ.insmod from /data/local/tmp is a development shortcut, not a supported deployment. It is useful
precisely because the failure is immediate and specific.
The files you touched#
- the kernel source tree, synced separately from the platform
- the Kleaf build tooling and its documentation
- the GKI configuration and ABI description files such as aarch64/abi.stg
- README.mdhow init loads modules from a partition at boot
Verification#
A module is loaded when lsmod lists it and dmesg shows its init message. A module is deployable
when it also loads without a manual push, from the partition it belongs in, at the point in boot
where it is needed. Those are different bars, and only the second one matters.
What can go wrong#
insmodfails with an unknown symbol. The module uses a symbol that is not on the KMI list for this kernel. The KMI check is enforced by failing the load.2insmodfails with a version or CRC mismatch. The module was built against a different kernel or a different toolchain.CONFIG_MODVERSIONScompares symbol CRCs at load time.- It loads by hand but not at boot. The module is in
vendorbut is needed before that partition is mounted, or it is missing from the load list. - Module signing confusion. The loadable-kernel-modules page states that module signing is not supported for GKI vendor modules, and that unsigned modules may load as long as they use only allowlisted symbols. Do not add signing in the hope it fixes a symbol failure.
- SELinux denies the operation. Loading and probing a device node both need policy; a denial
appears as
avc: deniedrather than as a kernel error.5 - It works on one device and not another. The two devices have different kernel generations. KMI stability does not cross LTS versions.
- You built against a config you enabled. Only
gki_defconfigbuilds the GKI kernel, so a feature you turned on locally is not present in the kernel you are loading into.
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsGeneric Kernel Image (GKI)source.android.comaccessed 2026-10-27↩
- docsStable KMIsource.android.comaccessed 2026-10-27↩
- docsKernel modules overviewsource.android.comaccessed 2026-10-27↩
- docsLoadable kernel modulessource.android.comaccessed 2026-10-27↩
- docsSELinuxsource.android.comaccessed 2026-10-27↩