Repository navigation
macOS: Doorstop cannot hook Unity 6000.3 Mono games — chained-fixups header parses as all zeros #108
Description
Activity
Confirming this affects a second title, same failure mode.
Environment
OS macOS 26.5.2 (25F84), Darwin 25.5.0 CPU Apple M-series (arm64), Mac16,11 Game Global Rescue (Steam appid 2873660) Unity 6000.3.12f1, Mono backend ( libmonobdwgc-2.0.dylib,MonoBleedingEdge/present)Doorstop 4.5.0 (release build) and the citag'sdoorstop_macos_verbose_4.5.0.zip, via BepInEx 5.4.23.5 macos_universalSymptom
Identical to the report above: Doorstop loads and its debug memory-map dump prints for
UnityPlayer.dylibandlibdoorstop.dylib, the game boots and runs completely normally, butBepInEx/config,BepInEx/pluginscontents, andLogOutput.logare never created — Preloader never gets control.Swapping in the verbose CI build (
doorstop_macos_verbose_4.5.0.zip) surfaced the same failure explicitly:[Doorstop] Failed to open current process PLT! Cannot run Doorstop! Error: unknown imports format 0which matches the all-zero
dyld_chained_fixups_header(imports_count 0,imports_format 0) described above —UnityPlayer.dylibin this build has ~thousands of symbols, so this is clearly the same parse failure, not a genuinely-empty import table.Ruled out (same as above, independently reproduced)
- Native arm64 vs. forced x86_64/Rosetta (
ARCHPREFERENCE) — identical failure both ways. com.apple.quarantine— stripped from all extracted BepInEx files before testing.- Steam relaunch — suppressed with a
steam_appid.txtcontaining the App ID; game reaches full init regardless. - Install layout — BepInEx files placed at game root alongside
GR.app, not inside the bundle.
No workaround found on our end either. +1 that this looks like it'll affect any Unity 6.3+ Mono game on macOS going forward, given both affected titles are otherwise unrelated (different devs/publishers) and hit the exact same zeroed chained-fixups header.
- Native arm64 vs. forced x86_64/Rosetta (
- addedbugSomething isn't workingSomething isn't workinghelp wantedExtra attention is neededExtra attention is needed
on Aug 3, 2026 I encountered the same problem in macOS 26.6 with Hearthstone(Unity 6000.3.11f1). Doorstop (4.5.0 CI build) reports :
[Doorstop] Failed to open current process PLT! Cannot run Doorstop! Error: unknown imports format 0Oh I already got it working on macOS again, you have to pull github.com/NeighTools/UnityDoorstop and patch src/nix/plthook/plthook_osx.c (Fix chained-fixups parsing (linkedit-relative address) + map imports to real GOT slots by reading chains from the file) and src/nix/entrypoint.c (Hook dlsym via DYLD interpose instead of (only) GOT patching) . Build it then yoink libdoorstop.dylib and you're golden.
Attached a working lib you can test that fix with
Reacted by Pik-4Have you considered making a PR?
``Application paused: False
=================================================================
Native Crash ReportingGot a segv while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.Obtained 23 stack frames.
=================================================================
Native stacktrace:0x11a7b1292 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_breakpoint_clean_code 0x11a7573c6 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_unity_backtrace_from_context 0x11a6d3585 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_jit_set_domain 0x7ff81b473dfd - /usr/lib/system/libsystem_platform.dylib : _sigtramp 0x6000003d3400 - Unknown 0x112614814 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib : 0x11261441a - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib : 0x1125af5c8 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib : 0x7ff81b45e4e1 - /usr/lib/system/libsystem_pthread.dylib : _pthread_start 0x7ff81b459f6b - /usr/lib/system/libsystem_pthread.dylib : thread_start=================================================================
Telemetry Dumper:#0 0x00000111d58320 in (Unknown)
Thread 0x700010a99000 may have been prematurely finalized* Assertion at mono-threads.c:702, condition `info' not met, function:mono_thread_info_current,An error has occured in the native fault reporting. Some diagnostic information will be unavailable.
=================================================================
Native stacktrace:#1 0x00000111cd1d0d in (Unknown)
0x11a7b1292 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_breakpoint_clean_code
0x11a90d1d3 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : monoeg_assertion_message
0x11a901a38 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_thread_info_current_unchecked
0x11a902fd9 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_thread_info_set_flags
0x11a8ab35d - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_threads_detach_coop
0x11a7b1549 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_breakpoint_clean_code
0x11a7573c6 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_unity_backtrace_from_context
0x11a6d3585 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/libmonobdwgc-2.0.dylib : mono_jit_set_domain
0x7ff81b473dfd - /usr/lib/system/libsystem_platform.dylib : _sigtramp
0x6000003d3400 - Unknown
0x112614814 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib :
0x11261441a - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib :
0x1125af5c8 - /Volumes/Game/Hearthstone/Hearthstone.app/Contents/Frameworks/UnityPlayer.dylib :
0x7ff81b45e4e1 - /usr/lib/system/libsystem_pthread.dylib : _pthread_start
0x7ff81b459f6b - /usr/lib/system/libsystem_pthread.dylib : thread_start
#2 0x000001128dd3de in unity_operator_delete_dealloc
#3 0x000001128e053a in unity_operator_delete_dealloc
#4 0x007ff81e6166d3 in -[_NSOpenGLViewBackingLayer display]
#5 0x007ff8225f762b in CA::Layer::display_if_needed(CA::Transaction*)=================================================================
External Debugger Dump:#6 0x007ff82274ef86 in CA::Context::commit_transaction(CA::Transaction*, double, double*)
#7 0x007ff8225d8a89 in CA::Transaction::commit()
#8 0x007ff81e0cdd71 in __62+[CATransaction(NSCATransaction) NS_setFlushesWithDisplayLink]_block_invokeExiting early due to double fault.
- added 2 commits that reference this issue
on Sep 13, 2026 Root cause found.
read_chained_fixupsreads the header at(mach_header + dataoff), butdataoffis a file offset and__LINKEDITis mapped at a different delta, so the read lands in the string table. Hence the zeros.Two fixes, either works:
- Fix chained-fixups reads on universal macOS binaries metacall/plthook#26 fixes it upstream, in the vendored file's own repo.
- osx: fix the chained-fixups reader so Unity 6.3 games hook #117 fixes it in the vendored copy here, and also corrects the import-index-to-
__got-slot mapping, which is wrong on this image (dlsymis import 546, slot 556).
With either, Ages of Conflict 6000.3.8f1 reaches
Chainloader startup completeon arm64.Confirming on a second title: Dressmaker (Unity 6000.3.4f1), macOS 26, Apple M3.
Stock BepInEx 5.4.23.5 (Doorstop 4.5.0): libdoorstop.dylib maps into the process and the game runs normally, but the hook never installs — no LogOutput.log, no config/. dyld_chained_fixups_header parses as all zeros, imports_count 0.
Swapping in the CI build of master (PR #117) fixes it: Chainloader startup complete, plugin loaded.
It also works when running the x86_64 slice under Rosetta (ARCHPREFERENCE=x86_64), not just native arm64 — possibly useful to know since BepInEx 5.4.23.5 separately loads no plugins on native arm64 (PR BepInEx/BepInEx#1402 isn't in a release yet)... so Rosetta is currently the practical configuration on Apple Silicon.
Should be fixed once doorstop's next release, my code was merged. Bepinex will need a small change to update the doorstop version and it's own release.
- added a commit that references this issue
on Sep 26, 2026
Summary
On macOS/Apple Silicon, Doorstop loads successfully into a Unity 6000.3.8f1 Mono game but never installs its
dlsymhook, somono_jit_init_versionis never intercepted and the target assembly never runs. BepInEx produces noLogOutput.logand noconfig/— it fails completely silently.I traced this to
read_chained_fixups()inplthook_osx.creturning an all-zerodyld_chained_fixups_headerforUnityPlayer.dylib. I also tested Doorstop 4.3.0, which fails at a different point for a different reason — details below, since the pair of failures narrows things down.Environment
libmonobdwgc-2.0.dylib)Both the game executable and
UnityPlayer.dylibare universal (x86_64 arm64).libdoorstop.dylibfrom the be.785 build is also universal, so it loads natively on arm64.Symptom
Doorstop injects fine — its
PLTHOOK_DEBUG_ADDRoutput is emitted from inside the game process, and the game itself starts normally (Metal device created, PhysX initialised, Steam API connected). But no hook is ever installed, and no BepInEx artifacts are created:Diagnosis — Doorstop 4.5.0
plthook_osx.ctakes theLC_DYLD_CHAINED_FIXUPSpath for this binary (it has noLC_DYLD_INFO/LC_DYLD_INFO_ONLY). ThePLTHOOK_DEBUG_FIXUPSdump shows the header parsing as effectively all zeros:With
imports_count == 0no imports are enumerated, sodlsymis never located in the GOT and no hook is installed. Notably__gotitself is found — the"__got section is not found in %s"path is never hit, so this is specifically the fixups parse, not section lookup.For reference, the same dump for
libdoorstop.dylibin the same run reportsimports_count 1, also wrong.Relevant binary layout (arm64 slice of
UnityPlayer.dylib)Note
LC_DYLD_CHAINED_FIXUPS.dataoffis exactly equal to__LINKEDIT.fileoff— the header sits at the very start of__LINKEDIT. That makes this look like alinkedit_basecomputation problem: reading at the intended address lands on zeroed memory. Two candidates I could not distinguish from outside:slide + vmaddr - fileoffstyle base calculation misbehaving whendataoff == linkedit.fileoff.__LINKEDITis mapped/valid for this image.I'm reporting the observation rather than asserting the cause — the exact offsets above should make it reproducible.
Second data point — Doorstop 4.3.0 fails differently
Testing BepInEx 5.4.23.2 (Doorstop 4.3.0, x86_64-only dylib, run under Rosetta) is useful because it fails at an earlier stage:
At the v4.3.0 tag,
plthook_osx.conly walks__LINKEDITand__DATA, and only matches sections flaggedS_LAZY_SYMBOL_POINTERS. It has noLC_DYLD_CHAINED_FIXUPShandling at all. Since this binary keeps__gotin__DATA_CONSTand uses chained fixups (so there are no lazy-pointer sections), 4.3.0 can never find a hook target either.So 4.5.0's
__DATA_CONST/__got/chained-fixups support is clearly the right direction — it just doesn't produce a usable header on this binary.Reproduction
executable_nameand launch viarun_bepinex.sh.BepInEx/configorLogOutput.logis ever created.To launch outside Steam without the game restarting itself, set
SteamAppId=<appid>soSteamAPI_RestartAppIfNecessaryreturns false.Ruled out
libdoorstop.dylibare universal..app, not inside the bundle;.appcode signature verifies clean.com.apple.quarantinestripped from all extracted files.DOORSTOP_MONO_LIB_PATH— present in the 4.5.0 binary's strings; pointing it directly atContents/Frameworks/libmonobdwgc-2.0.dylibchanges nothing (appears to be Windows-only in effect).SteamAppId; game reaches full init either way.Why this matters
Unity 6.3 builds ship
UnityPlayer.dylibas chained-fixups-only with__gotin__DATA_CONST. As more titles move to Unity 6.3, this will affect every macOS Mono game, on both Intel and Apple Silicon.