From 8441d006b4504812b76b4b277b45034015cda844 Mon Sep 17 00:00:00 2001 From: Taylor McNutt Date: Wed, 2 Sep 2026 20:43:47 -0700 Subject: [PATCH] Patch every matching PLT/lazy-bind entry, not just the first plthook_replace() on macOS stopped at the first symbol match and returned immediately. On arm64 builds we observed duplicate lazy-bind entries for the same imported symbol (e.g. two separate _dlsym slots in UnityPlayer.dylib). Only patching the first left the second, actually-called slot unhooked, so Doorstop's dlsym interception (used to redirect mono_jit_init_version on modern Unity/Mono builds) never fired and Chainloader never started -- silently, with no error. Continue iterating and patch every matching entry instead. Verified against Valheim's dedicated server binary (Unity 6000.0.61f1, arm64 native, no Rosetta): before this fix, Doorstop's own verbose log showed no successful hook of dlsym/mono_jit_init_version and BepInEx.Preloader.dll was never invoked. After, the full chain (dlsym hook -> mono domain init -> Preloader.dll load and invoke) completes successfully. --- src/nix/plthook/plthook_osx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nix/plthook/plthook_osx.c b/src/nix/plthook/plthook_osx.c index 5a0010e..8d4cd5f 100644 --- a/src/nix/plthook/plthook_osx.c +++ b/src/nix/plthook/plthook_osx.c @@ -983,6 +983,7 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo const char *name; void **addr; int rv; + int matches = 0; if (plthook == NULL) { set_errmsg("invalid argument: The first argument is null."); @@ -1013,7 +1014,12 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo } continue; matched: - if (oldfunc) { + /* A symbol can appear as more than one PLT/lazy-bind entry (observed + on arm64 builds where the classic-bind fallback produces duplicate + stub slots). Patch every matching entry instead of stopping at the + first, since the real call site may resolve through a later one. */ + matches++; + if (oldfunc && matches == 1) { *oldfunc = *addr; } if (plthook->readonly_segment) { @@ -1028,6 +1034,8 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo } else { *addr = funcaddr; } + } + if (matches > 0) { return 0; } if (rv == EOF) {