vpnhide/kmod/BUILDING.md
okhsunrog 876829d9ad docs: add CONTRIBUTING.md and docs/ with build/release/changelog guides
Split the contributor-facing knowledge that used to live in the local
CLAUDE.md into versioned, public docs:

- CONTRIBUTING.md — PR process, commit conventions, required changelog
  entry for user-visible changes, code-style checks.
- docs/development.md — prereqs, keystore setup, per-module build
  commands, device install, CI lints.
- docs/releasing.md — VERSION bump → update-version.py → tag → CI →
  update-json.sh flow, with the rationale for why update-json is a
  separate post-release commit.
- docs/changelog.md — changelog.json as source of truth, how the two
  generated markdowns are regenerated, when to add an entry.

Extended kmod/BUILDING.md with a Podman variant of the DDK command,
covering rootless + SELinux (Fedora) where --userns=keep-id and :Z are
required. Kept the kmod build docs next to the code since the GKI /
DDK complexity is kmod-specific.

Component READMEs untouched — they document each module's architecture
and belong next to the code.
2026-04-17 13:51:33 +03:00

4.1 KiB

Building vpnhide-kmod

Most users should download pre-built modules from Releases — builds are provided for all supported GKI generations. This guide is for contributors or users who need to build from source.

The easiest way to build is using the same DDK container images that CI uses. No kernel source clone, no toolchain setup.

Docker

# Pick your GKI generation (see "Identifying your GKI generation" below)
KMI=android14-6.1

# Build the kernel module (run from repo root)
docker run --rm -v $(pwd)/kmod:/work \
    ghcr.io/ylarod/ddk-min:${KMI}-20260313 sh -c "
    CLANG=\$(echo /opt/ddk/clang/clang-r*/bin) && \
    make -C /opt/ddk/kdir/${KMI} M=/work \
        ARCH=arm64 LLVM=1 LLVM_IAS=1 \
        CC=\$CLANG/clang LD=\$CLANG/ld.lld \
        AR=\$CLANG/llvm-ar NM=\$CLANG/llvm-nm \
        OBJCOPY=\$CLANG/llvm-objcopy \
        OBJDUMP=\$CLANG/llvm-objdump \
        STRIP=\$CLANG/llvm-strip \
        CROSS_COMPILE=aarch64-linux-gnu- \
        modules"

# Package as KSU module
cp kmod/vpnhide_kmod.ko kmod/module/
(cd kmod/module && zip -qr ../../vpnhide-kmod.zip .)

Podman (rootless + SELinux)

On Fedora / RHEL-family systems with rootless Podman, the docker run above needs two extra flags:

  • --userns=keep-id — maps the host UID into the container so the bind-mounted /work stays writable. Without it the build fails with mkdir: cannot create directory '/work/.tmp_*': Permission denied.
  • :Z on the -v mount — SELinux relabel for the bind mount.
KMI=android14-6.1

podman run --rm --userns=keep-id -v "$(pwd)/kmod:/work:Z" \
    ghcr.io/ylarod/ddk-min:${KMI}-20260313 sh -c '
    CLANG=$(echo /opt/ddk/clang/clang-r*/bin) && \
    make -C /opt/ddk/kdir/'${KMI}' M=/work \
        ARCH=arm64 LLVM=1 LLVM_IAS=1 \
        CC=$CLANG/clang LD=$CLANG/ld.lld \
        AR=$CLANG/llvm-ar NM=$CLANG/llvm-nm \
        OBJCOPY=$CLANG/llvm-objcopy \
        OBJDUMP=$CLANG/llvm-objdump \
        STRIP=$CLANG/llvm-strip \
        CROSS_COMPILE=aarch64-linux-gnu- \
        modules'

cp kmod/vpnhide_kmod.ko kmod/module/
(cd kmod/module && zip -qr ../../vpnhide-kmod.zip .)

On non-SELinux distros with rootful Podman, the plain docker run command above works with just s/docker/podman/.

Local build with kernel source

If you prefer building against a local kernel source tree (e.g. for development or debugging), use the Makefile with direnv:

cd kmod/
cp .env.example .env
# Edit .env with paths to your kernel source and clang toolchain
direnv allow
make
./build-zip.sh

See .env.example for the required variables. You need a prepared kernel source tree with headers and Module.symvers.

Identifying your GKI generation

adb shell uname -r

The output looks like 6.1.75-android14-11-g... — the generation is android14-6.1.

Note: the android14 part is NOT your Android version — it's the kernel generation. All Pixels from 6 to 9a share the same android14-6.1 kernel. Pixel 10 series moves to android16-6.12.

uname -r pattern GKI generation
5.10.xxx-android12-... android12-5.10
5.10.xxx-android13-... android13-5.10
5.15.xxx-android13-... android13-5.15
5.15.xxx-android14-... android14-5.15
6.1.xxx-android14-... android14-6.1
6.6.xxx-android15-... android15-6.6
6.12.xxx-android16-... android16-6.12

Install and test

adb push vpnhide-kmod.zip /sdcard/Download/
# Install via KernelSU-Next manager -> Modules -> Install from storage
# Reboot

Verify after reboot:

adb shell "su -c 'lsmod | grep vpnhide'"
adb shell "su -c 'dmesg | grep vpnhide'"
adb shell "su -c 'cat /proc/vpnhide_targets'"

Troubleshooting

insmod: Exec format error — symvers CRC mismatch. Use the DDK build (matched symvers).

insmod: File exists — module already loaded. rmmod vpnhide_kmod first.

kretprobe not firing — check dmesg | grep vpnhide for registration messages and /proc/vpnhide_targets for correct UIDs. Target app UIDs change on reinstall — re-resolve via the VPN Hide app.