From 5ec2d687a6ba85d0a64a69884c01f54ff10a9195 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Jan 2022 00:20:32 -0500 Subject: [PATCH] macOS: extend the workaround to cover the dyld/exc_server deadlock issue, since 12.1 Later, we should probably switch to using mach_exc_server generated from `mig mach_exc.defs`. (cherry picked from commit 267b124f1f1f1558a7619a527e11a786a99f01d4) --- src/signals-mach.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index a46df6ef52ce7..7fab17ba60750 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -516,27 +516,30 @@ static kern_return_t profiler_segv_handler } #endif -static int jl_lock_profile_mach(void) +// WARNING: we are unable to handle sigsegv while the dlsymlock is held +static int jl_lock_profile_mach(int dlsymlock) { jl_lock_profile(); + // workaround for old keymgr bugs void *unused = NULL; int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0; - if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + // workaround for new dlsym4 bugs (API and bugs introduced in macOS 12.1) + if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_prepare(); return keymgr_locked; } -static void jl_unlock_profile_mach(int keymgr_locked) +static void jl_unlock_profile_mach(int dlsymlock, int keymgr_locked) { - if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) - _dyld_atfork_parent(); + if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) \ + _dyld_atfork_parent(); \ if (keymgr_locked) _keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); jl_unlock_profile(); } -#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach() -#define jl_unlock_profile() jl_unlock_profile_mach(keymgr_locked) +#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach(1) +#define jl_unlock_profile() jl_unlock_profile_mach(1, keymgr_locked) void *mach_profile_listener(void *arg) { @@ -555,7 +558,7 @@ void *mach_profile_listener(void *arg) HANDLE_MACH_ERROR("mach_msg", ret); // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) - jl_lock_profile(); + int keymgr_locked = jl_lock_profile_mach(0); for (i = jl_n_threads; i-- > 0; ) { // if there is no space left, break early if (jl_profile_is_buffer_full()) { @@ -563,9 +566,13 @@ void *mach_profile_listener(void *arg) break; } + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_prepare(); // briefly acquire the dlsym lock host_thread_state_t state; jl_thread_suspend_and_get_state2(i, &state); unw_context_t *uc = (unw_context_t*)&state; + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_parent(); // quickly release the dlsym lock if (running) { #ifdef LIBOSXUNWIND @@ -608,7 +615,7 @@ void *mach_profile_listener(void *arg) // We're done! Resume the thread. jl_thread_resume(i, 0); } - jl_unlock_profile(); + jl_unlock_profile_mach(0, keymgr_locked); if (running) { // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);