Skip to content

Commit b046cc4

Browse files
committed
fix Unity 6.5 built-in mimalloc conflict
1 parent da8c172 commit b046cc4

3 files changed

Lines changed: 102 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env bash
2+
# Localize all non-exported symbols in an Apple static library so that its
3+
# bundled mimalloc does not coalesce with Unity 6.5's built-in mimalloc when the
4+
# library is statically linked into UnityFramework (iOS `__Internal`).
5+
#
6+
# Requires the library to have been compiled with `-fno-common` (see the CI
7+
# workflow) so that mimalloc's tentative definitions (e.g. `_mi_page_map`) are
8+
# real defined symbols rather than common symbols, which cannot be localized.
9+
#
10+
# Usage: localize-apple-staticlib.sh <lib.a> <ld-arch> [sdk] [platform] [min-os]
11+
# e.g. localize-apple-staticlib.sh libunienc_c.a arm64 iphoneos ios 13.0
12+
set -euo pipefail
13+
14+
LIB="$1" # path to the .a (e.g. libunienc_c.a)
15+
ARCH="$2" # ld arch name (e.g. arm64)
16+
SDK="${3:-iphoneos}" # xcrun SDK (e.g. iphoneos)
17+
PLATFORM="${4:-ios}" # -platform_version platform name (e.g. ios)
18+
MIN_OS="${5:-13.0}" # -platform_version minimum OS version
19+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20+
EXPORTS="$SCRIPT_DIR/../../InstantReplay.Externals/unienc/crates/unienc_c/apple-exports.txt"
21+
22+
if [ ! -f "$LIB" ]; then
23+
echo "ERROR: static library not found: $LIB" >&2
24+
exit 1
25+
fi
26+
27+
LIB_ABS="$(cd "$(dirname "$LIB")" && pwd)/$(basename "$LIB")"
28+
SDK_VER="$(xcrun --sdk "$SDK" --show-sdk-version)"
29+
WORK="$(mktemp -d)"
30+
trap 'rm -rf "$WORK"' EXIT
31+
32+
# Extract every archive member as a loose object. We deliberately do NOT use
33+
# `ld -r -force_load <archive>`: the modern linker (ld-prime) localizes *all*
34+
# symbols of a force-loaded archive, which would strip the `_unienc*` exports
35+
# too. Passing loose objects makes `-exported_symbols_list` behave as intended.
36+
( cd "$WORK" && ar x "$LIB_ABS" )
37+
member_count="$(ar t "$LIB_ABS" | grep -Ec '\.o$' || true)"
38+
extracted_count="$(find "$WORK" -maxdepth 1 -name '*.o' | wc -l | tr -d ' ')"
39+
if [ "$member_count" != "$extracted_count" ]; then
40+
echo "ERROR: extracted $extracted_count objects but archive lists $member_count (duplicate member names?)" >&2
41+
exit 1
42+
fi
43+
44+
# Partial link into a single object.
45+
# -exported_symbols_list : demote every global NOT matching `_unienc*` to a
46+
# private-extern symbol.
47+
# -r (without -keep_private_externs) : convert those private-externs (and the
48+
# ones already produced by `-fvisibility=hidden`) into
49+
# true local symbols.
50+
# -platform_version : required by ld-prime even for a `-r` link.
51+
( cd "$WORK" && xcrun --sdk "$SDK" ld -r -arch "$ARCH" \
52+
-platform_version "$PLATFORM" "$MIN_OS" "$SDK_VER" \
53+
-exported_symbols_list "$EXPORTS" \
54+
*.o -o merged.o )
55+
56+
rm -f "$LIB_ABS"
57+
xcrun libtool -static -o "$LIB_ABS" "$WORK/merged.o"
58+
59+
# --- Verify (CI gate; also guards against future linker behaviour changes) ---
60+
echo "=== exported (global) symbols after localization ==="
61+
nm -gU "$LIB_ABS" | sort -u
62+
63+
# (a) No mimalloc symbol may remain external or private-external. `nm -gU` lists
64+
# both (private-externs still coalesce), so it must contain zero of them.
65+
if nm -gU "$LIB_ABS" | grep -iE '_mi_|_heap_main|_theap_main|mi_unity_'; then
66+
echo "FAIL: mimalloc symbols are still (private-)external -> would coalesce with Unity" >&2
67+
exit 1
68+
fi
69+
70+
# (b) FFI entry points must remain global.
71+
if ! nm -gU "$LIB_ABS" | grep -q '_unienc_UnityPluginLoad'; then
72+
echo "FAIL: _unienc_UnityPluginLoad is no longer exported" >&2
73+
exit 1
74+
fi
75+
76+
echo "Localization OK: $(nm -gU "$LIB_ABS" | grep -c '_unienc') unienc symbols exported, 0 mimalloc symbols external"

.github/workflows/build-unienc.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,21 @@ jobs:
128128
- run: rustup default stable
129129
- run: rustup target add ${{ matrix.arch }}-apple-ios
130130
- 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' || '' }}
131+
env:
132+
# Turn mimalloc's tentative definitions (e.g. _mi_page_map) into real
133+
# BSS definitions. Common symbols cannot be localized, so without this
134+
# `_mi_page_map` would remain external and still coalesce with Unity's
135+
# mimalloc. Injected via cc-rs's target CFLAGS so no submodule change
136+
# is needed. Only aarch64 is built for iOS (see matrix).
137+
CFLAGS_aarch64-apple-ios: -fno-common
138+
# Localize the bundled mimalloc so it does not coalesce with Unity 6.5's
139+
# built-in mimalloc when statically linked into UnityFramework.
140+
- name: Localize non-exported symbols (Unity variant only)
141+
if: matrix.variant == 'unity'
142+
working-directory: InstantReplay.Externals/unienc/target/${{ matrix.arch }}-apple-ios/${{ env._RUST_BUILD_CONFIG }}
143+
run: |
144+
chmod +x "$GITHUB_WORKSPACE/.github/scripts/localize-apple-staticlib.sh"
145+
"$GITHUB_WORKSPACE/.github/scripts/localize-apple-staticlib.sh" libunienc_c.a arm64 iphoneos
131146
- uses: actions/upload-artifact@v4
132147
with:
133148
name: ${{ matrix.variant }}-${{ matrix.arch }}-apple-ios
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Symbols that must stay globally visible when libunienc_c.a is statically
2+
# linked into UnityFramework (iOS). Consumed via `ld -r -exported_symbols_list`.
3+
#
4+
# Everything NOT listed here (all mimalloc `mi_*`/`_mi_*`/`heap_main` symbols and
5+
# Rust internals) is demoted to a local symbol, so the bundled mimalloc no longer
6+
# coalesces with Unity 6.5's own built-in mimalloc.
7+
#
8+
# All FFI entry points (csbindgen `unienc_*` + the iOS `unienc_UnityPluginLoad` /
9+
# `unienc_UnityPluginUnload` registered by RegisterPlugin.mm) share the `_unienc`
10+
# Mach-O prefix, so a single wildcard covers them and any future additions.
11+
_unienc*

0 commit comments

Comments
 (0)