diff --git a/.github/scripts/localize-apple-staticlib.sh b/.github/scripts/localize-apple-staticlib.sh new file mode 100755 index 00000000..d78aeda7 --- /dev/null +++ b/.github/scripts/localize-apple-staticlib.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Localize all non-exported symbols in an Apple static library so that its +# bundled mimalloc does not coalesce with Unity 6.5's built-in mimalloc when the +# library is statically linked into UnityFramework (iOS `__Internal`). +# +# Requires the library to have been compiled with `-fno-common` (see the CI +# workflow) so that mimalloc's tentative definitions (e.g. `_mi_page_map`) are +# real defined symbols rather than common symbols, which cannot be localized. +# +# Usage: localize-apple-staticlib.sh [sdk] [platform] [min-os] +# e.g. localize-apple-staticlib.sh libunienc_c.a arm64 iphoneos ios 13.0 +set -euo pipefail + +LIB="$1" # path to the .a (e.g. libunienc_c.a) +ARCH="$2" # ld arch name (e.g. arm64) +SDK="${3:-iphoneos}" # xcrun SDK (e.g. iphoneos) +PLATFORM="${4:-ios}" # -platform_version platform name (e.g. ios) +MIN_OS="${5:-13.0}" # -platform_version minimum OS version +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +EXPORTS="$SCRIPT_DIR/../../InstantReplay.Externals/unienc/crates/unienc_c/apple-exports.txt" + +if [ ! -f "$LIB" ]; then + echo "ERROR: static library not found: $LIB" >&2 + exit 1 +fi + +LIB_ABS="$(cd "$(dirname "$LIB")" && pwd)/$(basename "$LIB")" +SDK_VER="$(xcrun --sdk "$SDK" --show-sdk-version)" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +# Extract every archive member as a loose object. We deliberately do NOT use +# `ld -r -force_load `: the modern linker (ld-prime) localizes *all* +# symbols of a force-loaded archive, which would strip the `_unienc*` exports +# too. Passing loose objects makes `-exported_symbols_list` behave as intended. +( cd "$WORK" && ar x "$LIB_ABS" ) +member_count="$(ar t "$LIB_ABS" | grep -Ec '\.o$' || true)" +extracted_count="$(find "$WORK" -maxdepth 1 -name '*.o' | wc -l | tr -d ' ')" +if [ "$member_count" != "$extracted_count" ]; then + echo "ERROR: extracted $extracted_count objects but archive lists $member_count (duplicate member names?)" >&2 + exit 1 +fi + +# Partial link into a single object. +# -exported_symbols_list : demote every global NOT matching `_unienc*` to a +# private-extern symbol. +# -r (without -keep_private_externs) : convert those private-externs (and the +# ones already produced by `-fvisibility=hidden`) into +# true local symbols. +# -platform_version : required by ld-prime even for a `-r` link. +( cd "$WORK" && xcrun --sdk "$SDK" ld -r -arch "$ARCH" \ + -platform_version "$PLATFORM" "$MIN_OS" "$SDK_VER" \ + -exported_symbols_list "$EXPORTS" \ + *.o -o merged.o ) + +rm -f "$LIB_ABS" +xcrun libtool -static -o "$LIB_ABS" "$WORK/merged.o" + +# --- Verify (CI gate; also guards against future linker behaviour changes) --- +echo "=== exported (global) symbols after localization ===" +nm -gU "$LIB_ABS" | sort -u + +# (a) No mimalloc symbol may remain external or private-external. `nm -gU` lists +# both (private-externs still coalesce), so it must contain zero of them. +if nm -gU "$LIB_ABS" | grep -iE '_mi_|_heap_main|_theap_main|mi_unity_'; then + echo "FAIL: mimalloc symbols are still (private-)external -> would coalesce with Unity" >&2 + exit 1 +fi + +# (b) FFI entry points must remain global. +if ! nm -gU "$LIB_ABS" | grep -q '_unienc_UnityPluginLoad'; then + echo "FAIL: _unienc_UnityPluginLoad is no longer exported" >&2 + exit 1 +fi + +echo "Localization OK: $(nm -gU "$LIB_ABS" | grep -c '_unienc') unienc symbols exported, 0 mimalloc symbols external" diff --git a/.github/workflows/build-unienc.yml b/.github/workflows/build-unienc.yml index ebead11b..d6a79576 100644 --- a/.github/workflows/build-unienc.yml +++ b/.github/workflows/build-unienc.yml @@ -128,6 +128,21 @@ jobs: - run: rustup default stable - run: rustup target add ${{ matrix.arch }}-apple-ios - run: cargo build --target ${{ matrix.arch }}-apple-ios --profile ${{ env._RUST_BUILD_CONFIG == 'debug' && 'dev' || env._RUST_BUILD_CONFIG }} ${{ matrix.variant == 'unity' && '-F unity,mimalloc' || '' }} + env: + # Turn mimalloc's tentative definitions (e.g. _mi_page_map) into real + # BSS definitions. Common symbols cannot be localized, so without this + # `_mi_page_map` would remain external and still coalesce with Unity's + # mimalloc. Injected via cc-rs's target CFLAGS so no submodule change + # is needed. Only aarch64 is built for iOS (see matrix). + CFLAGS_aarch64-apple-ios: -fno-common + # Localize the bundled mimalloc so it does not coalesce with Unity 6.5's + # built-in mimalloc when statically linked into UnityFramework. + - name: Localize non-exported symbols (Unity variant only) + if: matrix.variant == 'unity' + working-directory: InstantReplay.Externals/unienc/target/${{ matrix.arch }}-apple-ios/${{ env._RUST_BUILD_CONFIG }} + run: | + chmod +x "$GITHUB_WORKSPACE/.github/scripts/localize-apple-staticlib.sh" + "$GITHUB_WORKSPACE/.github/scripts/localize-apple-staticlib.sh" libunienc_c.a arm64 iphoneos - uses: actions/upload-artifact@v4 with: name: ${{ matrix.variant }}-${{ matrix.arch }}-apple-ios diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib index 1267c8ff..5b0e3f68 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib and b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a index f68a0877..a2007234 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a and b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so index 2df9d350..ea32da3c 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so and b/InstantReplay.Externals/src/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib index 06cdea5d..65016223 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib and b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so index e182b65c..ba6f12a0 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so and b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll index 89575eeb..1b680bec 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll and b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll differ diff --git a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so index 117e0956..e87451cc 100644 Binary files a/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so and b/InstantReplay.Externals/src/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so differ diff --git a/InstantReplay.Externals/unienc/crates/unienc_c/apple-exports.txt b/InstantReplay.Externals/unienc/crates/unienc_c/apple-exports.txt new file mode 100644 index 00000000..71b7fd2c --- /dev/null +++ b/InstantReplay.Externals/unienc/crates/unienc_c/apple-exports.txt @@ -0,0 +1,11 @@ +# Symbols that must stay globally visible when libunienc_c.a is statically +# linked into UnityFramework (iOS). Consumed via `ld -r -exported_symbols_list`. +# +# Everything NOT listed here (all mimalloc `mi_*`/`_mi_*`/`heap_main` symbols and +# Rust internals) is demoted to a local symbol, so the bundled mimalloc no longer +# coalesces with Unity 6.5's own built-in mimalloc. +# +# All FFI entry points (csbindgen `unienc_*` + the iOS `unienc_UnityPluginLoad` / +# `unienc_UnityPluginUnload` registered by RegisterPlugin.mm) share the `_unienc` +# Mach-O prefix, so a single wildcard covers them and any future additions. +_unienc* diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib index 36393ea5..9cdaf3d7 100755 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-darwin/libunienc_c.dylib differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a index d0c05cbe..f71c13cb 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-apple-ios/libunienc_c.a differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so index 6e590371..9b4ef40f 100755 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/aarch64-linux-android/libunienc_c.so differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/apple-darwin/libunienc_c.bundle b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/apple-darwin/libunienc_c.bundle index a497dca9..424d8412 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/apple-darwin/libunienc_c.bundle and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/apple-darwin/libunienc_c.bundle differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/wasm32-unknown-emscripten/libunienc_c.a b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/wasm32-unknown-emscripten/libunienc_c.a index b68e1394..a19b8898 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/wasm32-unknown-emscripten/libunienc_c.a and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/wasm32-unknown-emscripten/libunienc_c.a differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib index 123b9577..1f002bb3 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-apple-darwin/libunienc_c.dylib differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so index 8df8d95b..aa952f8b 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-linux-android/libunienc_c.so differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll index da3c58c5..41b201da 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/libunienc_c.dll differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/unienc_c.pdb b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/unienc_c.pdb index 6446b92c..9cc98964 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/unienc_c.pdb and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-pc-windows/unienc_c.pdb differ diff --git a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so index e9239529..5e5b8a6a 100644 Binary files a/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so and b/Packages/jp.co.cyberagent.instant-replay/UniEnc/Plugins/x86_64-unknown-linux/libunienc_c.so differ