Zygote
Builder tracknerd

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.

6 minAPI 30–37 · 17verified 2026-10-27

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_defconfig is 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:

  1. The module was built against the right kernel and toolchain.
  2. It references only KMI symbols.
  3. It lives in a partition that is allowed to carry it.

The contract, concretely#

RuleWhere it comes fromWhat it means for you
Same LTS and Android versionStable KMI pageA module built for one kernel generation will not load on another
Symbol list onlyStable KMI pageEvery external symbol you call must be on the list
Load-time enforcementStable KMI pageThe failure is at insmod, not at compile time
gki_defconfig onlyStable KMI pageYou cannot rely on a config option you enabled yourself
Modules early in boot go in vendor_bootKernel modules pageAnything 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#

  1. 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.
  2. 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.

    2
    You should seeA kernel branch whose LTS and Android version match your device.
  3. Write the module

    Keep it deliberately small: one module_init, one module_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.
  4. 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.

    3
    You should seeA build target that produces a .ko.
  5. 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 tools/bazel run invocations of a _dist target. The kernel binary, the modules and the images land in a distribution directory.

    4

    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.
  6. 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 gki/aarch64/abi.stg) and says the tree also ships the tools to create and compare the description.

    2

    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.
  7. Package it into the right partition

    Vendor modules can be delivered in the vendor or vendor_boot image, and modules required early in the boot process must be in vendor_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.
  8. Load it

    On a userdebug build you can load a module by hand before wiring it into boot. Pushing the file and calling insmod is 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.
  9. 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.

    34
    You should seeThe module loads without manual intervention.
Inspect a module on the device
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 -20
Example 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#

  • insmod fails 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.2
  • insmod fails with a version or CRC mismatch. The module was built against a different kernel or a different toolchain. CONFIG_MODVERSIONS compares symbol CRCs at load time.
  • It loads by hand but not at boot. The module is in vendor but 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: denied rather 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_defconfig builds 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.

  1. docsGeneric Kernel Image (GKI)source.android.comaccessed 2026-10-27↩
  2. docsStable KMIsource.android.comaccessed 2026-10-27↩
  3. docsKernel modules overviewsource.android.comaccessed 2026-10-27↩
  4. docsLoadable kernel modulessource.android.comaccessed 2026-10-27↩
  5. docsSELinuxsource.android.comaccessed 2026-10-27↩