#!/usr/bin/env bash # build-stock-gki.sh — build cellguard.ko against the STOCK Google GKI kernel # that the target device is running, on an aarch64 build host (e.g. RPi5). # # Why this exists # --------------- # The device is a stock Pixel (rango / Tensor G5) running a stock GKI kernel: # 6.6.102-android15-8-g6eb5b2a8c46b-ab14739656-4k (RANDSTRUCT_NONE, CFI_CLANG, # MODVERSIONS, MODULE_SIG_PROTECT, LTO_NONE) # # For an out-of-tree module to LOAD there: # 1. It must match the kernel ABI: same vermagic *flags* (the release string is # ignored under MODVERSIONS) and matching symbol CRCs. => build against the # exact stock source @ the build's git SHA + the build's vmlinux.symvers. # 2. It must not import GKI *protected* symbols (kernel_read/kernel_write). The # module resolves those at runtime via a kprobe->kallsyms bootstrap instead # (see cellguard.c: cg_resolve_protected). Nothing to do here, just noting it. # # This does NOT require Google's signing key: MODULE_SIG_FORCE is off, so an # unsigned module loads as long as it imports no protected symbols. # # Usage # ----- # ./build-stock-gki.sh # auto-detect build info from adb device # BID=14739656 KREL=6.6.102-android15-8-g6eb5b2a8c46b-ab14739656-4k \ # KSHA=6eb5b2a8c46b6393ccde67a51f2e2f56277791c6 KBRANCH=android15-6.6-2025-10 \ # ./build-stock-gki.sh # or pin everything explicitly # # Requires (Debian/RPiOS): clang lld llvm bc bison flex libssl-dev libelf-dev git curl set -euo pipefail # ---- locations ------------------------------------------------------------- HERE="$(cd "$(dirname "$0")" && pwd)" # .../CellGuard/common/kmod WORK="${WORK:-$HERE/.gki-build}" # persistent build workspace SRC="$WORK/common" # kernel source tree ART="$WORK/artifacts" # downloaded GKI artifacts mkdir -p "$WORK" "$ART" # ---- 1. determine target build -------------------------------------------- # KREL = full kernel release; BID = ci.android.com build number; KSHA = kernel # common git sha; KBRANCH = the kernel/common branch to fetch the sha from. if [ -z "${KREL:-}" ] && command -v adb >/dev/null && adb get-state >/dev/null 2>&1; then KREL="$(adb shell 'uname -r' | tr -d '\r')" echo "[*] device kernel release: $KREL" fi : "${KREL:?set KREL (e.g. 6.6.102-android15-8-g-ab-4k) or connect adb}" # derive BID (ab#######) and short sha (g############) from the release string BID="${BID:-$(printf '%s' "$KREL" | sed -nE 's/.*-ab([0-9]+)-.*/\1/p')}" GSHORT="$(printf '%s' "$KREL" | sed -nE 's/.*-g([0-9a-f]{12,}).*/\1/p')" : "${BID:?could not derive BID from KREL; pass BID=...}" : "${KBRANCH:=android15-6.6}" # override if the build used a dated branch, e.g. android15-6.6-2025-10 echo "[*] BID=$BID gsha=$GSHORT branch=$KBRANCH" # ---- 2. download stock GKI artifacts for this exact build ------------------ # kernel-headers.tar.gz (source headers), vmlinux.symvers (symbol CRCs = our # Module.symvers), abi_symbollist.raw (KMI allow-list), manifest (pins KSHA). RAW="https://ci.android.com/builds/submitted/${BID}/kernel_aarch64/latest/raw" fetch() { # fetch -> $ART/ [ -s "$ART/$1" ] && { echo " cached $1"; return; } echo " download $1" curl -fsSL -o "$ART/$1" "$RAW/$1" } echo "[*] fetching GKI artifacts for ab$BID" fetch "vmlinux.symvers" fetch "abi_symbollist.raw" fetch "manifest_${BID}.xml" fetch "kernel-headers.tar.gz" # not strictly needed (we clone full source) but handy # exact kernel/common sha: prefer manifest, fall back to KSHA env if [ -z "${KSHA:-}" ]; then KSHA="$(sed -nE 's/.*path="common"[^>]*revision="([0-9a-f]{40})".*/\1/p' "$ART/manifest_${BID}.xml" | head -1)" fi : "${KSHA:?could not determine kernel sha; pass KSHA=<40hex>}" echo "[*] kernel/common sha: $KSHA" # ---- 3. get the exact stock source ---------------------------------------- if [ ! -d "$SRC/.git" ]; then echo "[*] cloning kernel/common (shallow) ..." git clone --depth 1 -b "$KBRANCH" \ https://android.googlesource.com/kernel/common "$SRC" fi if [ "$(git -C "$SRC" rev-parse HEAD)" != "$KSHA" ]; then echo "[*] fetching exact sha $KSHA ..." git -C "$SRC" fetch --depth 1 origin "$KSHA" git -C "$SRC" checkout -q "$KSHA" fi echo "[*] source at $(git -C "$SRC" rev-parse HEAD)" # ---- 4. configure with the DEVICE's real .config -------------------------- # Pull /proc/config.gz from the device for a byte-exact config (RANDSTRUCT off, # CFI on, MODVERSIONS on, etc). Fall back to a saved copy if adb is unavailable. if command -v adb >/dev/null && adb get-state >/dev/null 2>&1; then echo "[*] pulling device .config" adb exec-out 'su -c "cat /proc/config.gz"' 2>/dev/null | zcat > "$SRC/.config" \ || adb exec-out 'cat /proc/config.gz' | zcat > "$SRC/.config" elif [ -s "$HERE/device.config" ]; then cp "$HERE/device.config" "$SRC/.config" else echo "!! no .config: connect adb or place device.config next to this script" >&2 exit 1 fi cp "$ART/vmlinux.symvers" "$SRC/Module.symvers" # real CRCs for MODVERSIONS # ---- 5. prepare + build ---------------------------------------------------- # Native aarch64 clang (Debian) — kCFI type-IDs are ABI-stable across clang # versions, so this interoperates with the device's clang-18 GKI kernel. export ARCH=arm64 LLVM=1 JOBS="${JOBS:-$(nproc)}" echo "[*] olddefconfig + modules_prepare (-j$JOBS) ..." make -C "$SRC" -j"$JOBS" olddefconfig >/dev/null make -C "$SRC" -j"$JOBS" modules_prepare echo "[*] building module ..." make -C "$SRC" -j"$JOBS" M="$HERE" modules echo echo "[✓] built: $HERE/cellguard.ko" modinfo "$HERE/cellguard.ko" | grep -E 'vermagic|name' echo echo "Load it via the KernelSU module path (service.sh does this on boot):" echo " adb push $HERE/cellguard.ko /data/local/tmp/cellguard.ko" echo " adb shell su -c 'cp /data/local/tmp/cellguard.ko \\" echo " /data/adb/modules/cellguard/common/kmod/cellguard.ko && \\" echo " insmod /data/adb/modules/cellguard/common/kmod/cellguard.ko'"