vpnhide/kmod/BUILDING.md
okhsunrog 35b3dcdf50 build: align native cdylib on 16 KiB; unify kmod/zygisk build scripts
Two related changes that ship together because they touch the same
build-script + docs surface and were verified together on-device.

16 KiB alignment
  - zygisk/build.rs: pass `-Wl,-z,max-page-size=16384` to lld so the
    cdylib's LOAD segments line up on 16 KiB pages. NDK r28+ already
    does this by default, but the flag keeps r27 builds compatible.
  - lsposed/native/build.rs: new file, same flag, for libvpnhide_checks.so.
  - docs/development.md: bumped the NDK requirement to r28+ and noted
    the 16 KiB rationale.

Verified via `llvm-readelf -l`: both libvpnhide_zygisk.so and
libvpnhide_checks.so now show `Align 0x4000` on every LOAD segment.

Unified build entry points
  - kmod/build.py replaces kmod/build-zip.py. Single script that
    auto-detects whether to build natively (we're inside the DDK image
    or `--kdir` was passed) or to spawn `ghcr.io/ylarod/ddk-min` via
    podman/docker. CI uses the same script with `--inside-container`.
  - zygisk/build-zip.py renamed to zygisk/build.py for symmetry; logic
    unchanged.
  - kmod/BUILDING.md rewritten — local build is now one command:
    `./kmod/build.py --kmi android14-6.1` (or `--all`). The old
    hand-rolled podman/docker recipes are gone.
  - .github/workflows/ci.yml updated to call the new entry points.
    The DDK image tag in CI now has a comment pointing at
    `DDK_IMAGE_TAG` in kmod/build.py as the source of truth.
  - README.{md,en.md}, kmod/README.md, zygisk/README.md, docs/releasing.md,
    scripts/build_lib.py: reference updates.
  - README.en.md: also fixes a "bacame" typo and tightens the Windows
    zygisk-build note (the aux.rs / libgit2 issue is still real).

Verified end-to-end on Pixel 8 Pro (husky, android14-6.1, Android 16):
APK installs, kmod + zygisk modules load, all 26 self-checks PASS in
Enforcing, 22/26 PASS in Permissive (the same 4 by-design FAILs as
before — kmod doesn't cover those paths in Permissive).
2026-04-26 23:26:30 +03:00

3.3 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.

Quick build

One command — same script CI runs, no container invocation to memorize:

./kmod/build.py --kmi android14-6.1     # one variant
./kmod/build.py --all                   # every supported GKI

The script auto-detects whether to build natively (you're already inside the DDK image, or you've pointed --kdir at a kernel source tree) or to spawn a ghcr.io/ylarod/ddk-min:<kmi>-<TAG> container via podman/docker. On rootless podman (Fedora etc) it adds --userns=keep-id and :Z automatically. The output is vpnhide-kmod-<kmi>.zip at the repo root.

Requires podman or docker. The container image weighs ~1 GB per GKI variant on first pull.

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

Local build with kernel source

If you prefer building against a local kernel source tree (e.g. for development or debugging), point --kdir at it. The script then runs natively without spinning up a container:

./kmod/build.py --kdir ~/kernels/android14-6.1 --kmi android14-6.1

You can also drop a kmod/.env file with KDIR= / KERNEL_SRC= / CLANG_DIR= (see .env.example) and use direnv to load it automatically. The script picks those up via env, no flag needed.

Install and test

adb push vpnhide-kmod-<kmi>.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. Rebuild via the DDK container (./kmod/build.py --kmi <kmi>); the container image carries 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.

./kmod/build.py says "neither podman nor docker found" — install one (dnf install podman / apt install docker.io), or build natively against a local kernel source via --kdir.

Bumping the DDK image tag — single source of truth is DDK_IMAGE_TAG in kmod/build.py. Both this script and .github/workflows/ci.yml's kmod matrix pin to the same value, so update both together.