diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c --- a/lib/libthr/thread/thr_exit.c +++ b/lib/libthr/thread/thr_exit.c @@ -55,7 +55,6 @@ static void thread_unwind(void) __dead2; #ifdef PIC -static void thread_uw_init(void); static _Unwind_Reason_Code thread_unwind_stop(int version, _Unwind_Action actions, uint64_t exc_class, @@ -66,8 +65,8 @@ _Unwind_Stop_Fn, void *); static uintptr_t (*uwl_getcfa)(struct _Unwind_Context *); -static void -thread_uw_init(void) +void +_thread_uw_init(struct pthread *curthread) { static int inited = 0; Dl_info dli; @@ -83,13 +82,14 @@ * Make sure the address is always valid by holding the library, * also assume functions are in same library. */ - if ((handle = dlopen(dli.dli_fname, RTLD_LAZY)) != NULL) { + if ((handle = dlopen(dli.dli_fname, RTLD_NOW)) != NULL) { forcedunwind = dlsym(handle, "_Unwind_ForcedUnwind"); getcfa = dlsym(handle, "_Unwind_GetCFA"); if (forcedunwind != NULL && getcfa != NULL) { uwl_getcfa = getcfa; atomic_store_rel_ptr((volatile void *)&uwl_forcedunwind, (uintptr_t)forcedunwind); + curthread->unwind_dlhandle = handle; } else { dlclose(handle); } @@ -103,7 +103,11 @@ _Unwind_ForcedUnwind(struct _Unwind_Exception *ex, _Unwind_Stop_Fn stop_func, void *stop_arg) { - return (*uwl_forcedunwind)(ex, stop_func, stop_arg); + struct pthread *curthread = _get_curthread(); + _Unwind_Reason_Code r = (*uwl_forcedunwind)(ex, stop_func, stop_arg); + if (curthread->unwind_dlhandle != NULL) + dlclose(curthread->unwind_dlhandle); + return r; } uintptr_t @@ -238,7 +242,6 @@ #ifdef _PTHREAD_FORCED_UNWIND #ifdef PIC - thread_uw_init(); if (uwl_forcedunwind != NULL) { #else if (_Unwind_ForcedUnwind != NULL) { @@ -249,17 +252,17 @@ _thread_printf(2, "Warning: old _pthread_cleanup_push was called, " "stack unwinding is disabled.\n"); } + if (curthread->unwind_dlhandle != NULL) + dlclose(curthread->unwind_dlhandle); goto cleanup; } thread_unwind(); - } else { cleanup: while (curthread->cleanup != NULL) { __pthread_cleanup_pop_imp(1); } __cxa_thread_call_dtors(); - exit_thread(); } diff --git a/lib/libthr/thread/thr_fork.c b/lib/libthr/thread/thr_fork.c --- a/lib/libthr/thread/thr_fork.c +++ b/lib/libthr/thread/thr_fork.c @@ -97,7 +97,7 @@ _thr_rwl_wrlock(&_thr_atfork_lock); TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe); _thr_rwl_unlock(&_thr_atfork_lock); - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); return (0); } @@ -122,7 +122,7 @@ } } _thr_rwl_unlock(&_thr_atfork_lock); - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); while ((af = TAILQ_FIRST(&temp_list)) != NULL) { TAILQ_REMOVE(&temp_list, af, qe); free(af); diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c --- a/lib/libthr/thread/thr_init.c +++ b/lib/libthr/thread/thr_init.c @@ -427,6 +427,9 @@ #ifdef _PTHREAD_FORCED_UNWIND thread->unwind_stackend = _usrstack; +#ifdef PIC + _thread_uw_init(thread); +#endif #endif /* Others cleared to zero by thr_alloc() */ diff --git a/lib/libthr/thread/thr_list.c b/lib/libthr/thread/thr_list.c --- a/lib/libthr/thread/thr_list.c +++ b/lib/libthr/thread/thr_list.c @@ -311,7 +311,7 @@ THR_THREAD_LOCK(curthread, thread); thread->refcount--; _thr_try_gc(curthread, thread); - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); } /* entered with thread lock held, exit with thread lock released */ diff --git a/lib/libthr/thread/thr_mutex.c b/lib/libthr/thread/thr_mutex.c --- a/lib/libthr/thread/thr_mutex.c +++ b/lib/libthr/thread/thr_mutex.c @@ -639,7 +639,7 @@ _mutex_leave_robust(curthread, m); if (ret != 0 && ret != EOWNERDEAD && (m->m_flags & PMUTEX_FLAG_PRIVATE) != 0) - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); return (ret); } @@ -736,7 +736,7 @@ _mutex_leave_robust(curthread, m); if (ret != 0 && ret != EOWNERDEAD && (m->m_flags & PMUTEX_FLAG_PRIVATE) != 0 && !cvattach) - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); return (ret); } @@ -997,7 +997,7 @@ _mutex_leave_robust(curthread, m); } if (!cv && private) - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); return (error); } diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -525,6 +525,7 @@ struct _Unwind_Exception ex; void *unwind_stackend; int unwind_disabled; + void *unwind_dlhandle; #endif /* @@ -587,12 +588,15 @@ ((thrd)->critical_count > 0)) #define THR_CRITICAL_ENTER(thrd) \ - (thrd)->critical_count++ + do { \ + (thrd)->critical_count++; \ + } while (0) -#define THR_CRITICAL_LEAVE(thrd) \ +#define THR_CRITICAL_LEAVE(thrd, do_ast) \ do { \ (thrd)->critical_count--; \ - _thr_ast(thrd); \ + if (do_ast) \ + _thr_ast(thrd); \ } while (0) #define THR_UMUTEX_TRYLOCK(thrd, lck) \ @@ -701,7 +705,7 @@ #define THR_REF_DEL(curthread, pthread) { \ pthread->refcount--; \ - THR_CRITICAL_LEAVE(curthread); \ + THR_CRITICAL_LEAVE(curthread, 1); \ } while (0) #define GC_NEEDED() (_gc_count >= 5) @@ -1103,6 +1107,9 @@ bool __thr_get_main_stack_base(char **base); bool __thr_get_main_stack_lim(size_t *lim); int _Tthr_sigqueue(pthread_t pthread, int sig, const union sigval value); +#ifdef PIC +void _thread_uw_init(struct pthread *); +#endif __END_DECLS __NULLABILITY_PRAGMA_POP diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -168,7 +168,14 @@ if (_thr_rwlock_unlock(&l->lock) == 0) { if ((state & URWLOCK_WRITE_OWNER) == 0) curthread->rdlock_count--; - THR_CRITICAL_LEAVE(curthread); + THR_CRITICAL_LEAVE(curthread, 1); + } else if (curthread->cancelling) { + /* + * If the main thread is being destroyed as well and + * tries to cancel us, some of the rtld locks might + * already be gone and unlock() will err. + */ + THR_CRITICAL_LEAVE(curthread, 0); } RESTORE_ERRNO(); } diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -426,7 +426,7 @@ * because we are leaf code, we don't want to recursively call * ourself. */ - curthread->critical_count++; + THR_CRITICAL_ENTER(curthread); THR_UMUTEX_LOCK(curthread, &(curthread)->lock); while ((curthread->flags & THR_FLAGS_NEED_SUSPEND) != 0) { curthread->cycle++; @@ -447,7 +447,7 @@ THR_UMUTEX_LOCK(curthread, &(curthread)->lock); } THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); - curthread->critical_count--; + THR_CRITICAL_LEAVE(curthread, 0); _thr_signal_unblock(curthread); } diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1044,8 +1044,10 @@ where = (Elf_Addr *)(obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT, NULL, &lockstate); - if (def == NULL) + if (def == NULL) { + lock_release(rtld_bind_lock, &lockstate); rtld_die(); + } if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); else