#!/bin/bash set -euo pipefail # Builds shadowhook as a static library for Android arm64. # Produces: libs/libshadowhook.a and libs/libshadowhook_nothing.so # # Skips the build entirely if both outputs already exist. Set FORCE_REBUILD=1 # to rebuild anyway (e.g. after changing CFLAGS, the toolchain, or the # shadowhook sources themselves). SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" BUILD_DIR="$PROJECT_DIR/build/deps" OUT_DIR="$SCRIPT_DIR" SH="$SCRIPT_DIR/shadowhook/shadowhook/src/main/cpp" if [ "${FORCE_REBUILD:-0}" != "1" ] \ && [ -f "$OUT_DIR/libshadowhook.a" ] \ && [ -f "$OUT_DIR/libshadowhook_nothing.so" ]; then echo "=== shadowhook already built, skipping (FORCE_REBUILD=1 to rebuild) ===" exit 0 fi mkdir -p "$BUILD_DIR" # --- Auto-detect Android NDK (same logic as build.sh) --- find_ndk() { if [ -n "${ANDROID_NDK_HOME:-}" ] && [ -d "$ANDROID_NDK_HOME" ]; then echo "$ANDROID_NDK_HOME"; return fi local brew_ndk brew_ndk=$(find /opt/homebrew /usr/local -path "*/android-ndk/*/toolchains/llvm/prebuilt" -type d 2>/dev/null | head -1) if [ -n "$brew_ndk" ]; then echo "$(dirname "$(dirname "$(dirname "$brew_ndk")")")"; return fi local sdk_ndk sdk_ndk=$(find "$HOME/Library/Android/sdk/ndk" -maxdepth 1 -type d 2>/dev/null | sort -V | tail -1) if [ -n "$sdk_ndk" ] && [ -d "$sdk_ndk" ]; then echo "$sdk_ndk"; return fi for p in /opt/android-ndk /usr/local/android-ndk "$HOME/android-ndk"; do if [ -d "$p" ]; then echo "$p"; return; fi done echo "" } NDK_HOME="$(find_ndk)" API_LEVEL="${ANDROID_API_LEVEL:-21}" CC="" if [ -n "$NDK_HOME" ]; then CC=$(find "$NDK_HOME/toolchains/llvm/prebuilt" -type f \ -name "aarch64-linux-android${API_LEVEL}-clang" -print -quit 2>/dev/null) fi if [ -z "$CC" ]; then echo "ERROR: aarch64-linux-android clang not found." echo " Set ANDROID_NDK_HOME to your NDK path." exit 1 fi TOOLCHAIN_DIR="$(dirname "$CC")" AR="$TOOLCHAIN_DIR/llvm-ar" CFLAGS="-O2 -ffunction-sections -fdata-sections -fPIC -DANDROID -Wall -Wextra -Wno-unused-parameter -Wno-unused-function" SH_SRCS=( "$SH/sh_elf.c" "$SH/sh_enter.c" "$SH/sh_hub.c" "$SH/sh_island.c" "$SH/sh_linker.c" "$SH/sh_recorder.c" "$SH/sh_safe.c" "$SH/sh_switch.c" "$SH/sh_task.c" "$SH/sh_xdl.c" "$SH/shadowhook.c" "$SH/arch/arm64/sh_a64.c" "$SH/arch/arm64/sh_inst.c" "$SH/arch/arm64/sh_glue.S" "$SH/common/bytesig.c" "$SH/common/sh_errno.c" "$SH/common/sh_log.c" "$SH/common/sh_ref.c" "$SH/common/sh_trampo.c" "$SH/common/sh_util.c" "$SH/third_party/xdl/xdl.c" "$SH/third_party/xdl/xdl_iterate.c" "$SH/third_party/xdl/xdl_linker.c" "$SH/third_party/xdl/xdl_lzma.c" "$SH/third_party/xdl/xdl_util.c" ) SH_INCLUDES=( "-I$SH" "-I$SH/include" "-I$SH/arch/arm64" "-I$SH/common" "-I$SH/third_party/xdl" "-I$SH/third_party/bsd" "-I$SH/third_party/lss" ) NOTHING_SRC="$SH/nothing/sh_nothing.c" echo "=== Building shadowhook (arm64-v8a) ===" SH_OBJS=() for src in "${SH_SRCS[@]}"; do if [ ! -f "$src" ]; then echo "ERROR: shadowhook source not found: $src" exit 1 fi rel="${src#"$SH"/}" flat="$(echo "$rel" | tr '/' '_')" obj="$BUILD_DIR/sh_${flat%.*}.o" "$CC" $CFLAGS "${SH_INCLUDES[@]}" -c "$src" -o "$obj" SH_OBJS+=("$obj") done "$AR" rcs "$OUT_DIR/libshadowhook.a" "${SH_OBJS[@]}" SH_SIZE=$(wc -c < "$OUT_DIR/libshadowhook.a" 2>/dev/null || echo "unknown") echo " Built $OUT_DIR/libshadowhook.a ($SH_SIZE bytes)" if [ ! -f "$NOTHING_SRC" ]; then echo "ERROR: ShadowHook companion source not found: $NOTHING_SRC" exit 1 fi "$CC" -shared -Oz -fPIC -fno-ident -fno-unwind-tables -fno-asynchronous-unwind-tables \ -nostdlib -nostartfiles -nodefaultlibs \ -Wl,-z,max-page-size=16384 \ -Wl,--strip-all -Wl,--as-needed -Wl,--hash-style=sysv \ -Wl,--build-id=none \ -o "$OUT_DIR/libshadowhook_nothing.so" "$NOTHING_SRC" echo " Built $OUT_DIR/libshadowhook_nothing.so"