Skip to content

Commit e8e5e08

Browse files
authored
Merge pull request #152 from CyberAgentGameEntertainment/feature/unity_6.5_mimalloc_conflict
Fix iOS player crash due to mimalloc symbol conflict in Unity 6.5+
2 parents da8c172 + 8b5d5e1 commit e8e5e08

20 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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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)