11#! /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`).
2+ # Localize the bundled mimalloc symbols in an Apple static library so they do
3+ # not coalesce with Unity 6.5's built-in mimalloc when the library is statically
4+ # linked into UnityFramework (iOS `__Internal`).
5+ #
6+ # Only mimalloc's own symbols are localized. Rust runtime symbols (including
7+ # personality routines) are intentionally left global: localizing them would
8+ # give every linked static lib its own personality routine, and compact-unwind
9+ # can encode at most 3 per image ("ld: Too many personality routines ...").
510#
611# Requires the library to have been compiled with `-fno-common` (see the CI
712# workflow) so that mimalloc's tentative definitions (e.g. `_mi_page_map`) are
@@ -15,9 +20,9 @@ LIB="$1" # path to the .a (e.g. libunienc_c.a)
1520ARCH=" $2 " # ld arch name (e.g. arm64)
1621SDK=" ${3:- iphoneos} " # xcrun SDK (e.g. iphoneos)
1722PLATFORM=" ${4:- ios} " # -platform_version platform name (e.g. ios)
18- MIN_OS=" ${5:- 13 .0} " # -platform_version minimum OS version
23+ MIN_OS=" ${5:- 10 .0} " # -platform_version minimum OS version
1924SCRIPT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
20- EXPORTS =" $SCRIPT_DIR /../../InstantReplay.Externals/unienc/crates/unienc_c/apple-exports .txt"
25+ UNEXPORTS =" $SCRIPT_DIR /../../InstantReplay.Externals/unienc/crates/unienc_c/apple-mimalloc-unexports .txt"
2126
2227if [ ! -f " $LIB " ]; then
2328 echo " ERROR: static library not found: $LIB " >&2
@@ -32,7 +37,7 @@ trap 'rm -rf "$WORK"' EXIT
3237# Extract every archive member as a loose object. We deliberately do NOT use
3338# `ld -r -force_load <archive>`: the modern linker (ld-prime) localizes *all*
3439# symbols of a force-loaded archive, which would strip the `_unienc*` exports
35- # too. Passing loose objects makes `-exported_symbols_list` behave as intended .
40+ # and the Rust runtime symbols too. Loose objects make the symbol lists behave .
3641( cd " $WORK " && ar x " $LIB_ABS " )
3742member_count=" $( ar t " $LIB_ABS " | grep -Ec ' \.o$' || true) "
3843extracted_count=" $( find " $WORK " -maxdepth 1 -name ' *.o' | wc -l | tr -d ' ' ) "
@@ -42,35 +47,36 @@ if [ "$member_count" != "$extracted_count" ]; then
4247fi
4348
4449# Partial link into a single object.
45- # -exported_symbols_list : demote every global NOT matching `_unienc*` to a
46- # private-extern symbol .
50+ # -unexported_symbols_list : demote ONLY the listed mimalloc symbols to
51+ # private-extern (leaves everything else global) .
4752# -r (without -keep_private_externs) : convert those private-externs (and the
48- # ones already produced by `-fvisibility=hidden`) into
49- # true local symbols.
53+ # ones already produced by `-fvisibility=hidden`)
54+ # into true local symbols.
5055# -platform_version : required by ld-prime even for a `-r` link.
5156( cd " $WORK " && xcrun --sdk " $SDK " ld -r -arch " $ARCH " \
5257 -platform_version " $PLATFORM " " $MIN_OS " " $SDK_VER " \
53- -exported_symbols_list " $EXPORTS " \
58+ -unexported_symbols_list " $UNEXPORTS " \
5459 * .o -o merged.o )
5560
5661rm -f " $LIB_ABS "
5762xcrun libtool -static -o " $LIB_ABS " " $WORK /merged.o"
5863
5964# --- 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-
6365# (a) No mimalloc symbol may remain external or private-external. `nm -gU` lists
6466# 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
67+ # The `_mi_` substring also matches `__mi_*` / `___mi_*`.
68+ if xcrun nm -gU " $LIB_ABS " | grep -iE ' _mi_|_heap_main|_theap' ; then
6669 echo " FAIL: mimalloc symbols are still (private-)external -> would coalesce with Unity" >&2
70+ echo " (if _mi_page_map is listed, the build is missing -fno-common)" >&2
6771 exit 1
6872fi
6973
70- # (b) FFI entry points must remain global.
71- if ! nm -gU " $LIB_ABS " | grep -q ' _unienc_UnityPluginLoad' ; then
74+ # (b) FFI entry points must remain global. Use `grep -c` (not `grep -q`): under
75+ # `set -o pipefail`, `grep -q` exits on first match and SIGPIPEs `nm`, whose
76+ # non-zero status would then be reported as a (spurious) failure.
77+ if [ " $( xcrun nm -gU " $LIB_ABS " | grep -c ' _unienc_UnityPluginLoad' ) " -eq 0 ]; then
7278 echo " FAIL: _unienc_UnityPluginLoad is no longer exported" >&2
7379 exit 1
7480fi
7581
76- echo " Localization OK: $( nm -gU " $LIB_ABS " | grep -c ' _unienc' ) unienc symbols exported, 0 mimalloc symbols external"
82+ echo " Localization OK: $( xcrun nm -gU " $LIB_ABS " | grep -c ' _unienc' ) unienc symbols exported, 0 mimalloc symbols external"
0 commit comments