diff --git a/lib/libthr/pthread.map b/lib/libthr/pthread.map index 232d65084ba9..15195ce0847b 100644 --- a/lib/libthr/pthread.map +++ b/lib/libthr/pthread.map @@ -1,334 +1,337 @@ /* * $FreeBSD$ */ /* * Use the same naming scheme as libc. */ FBSD_1.0 { pthread_atfork; pthread_barrier_destroy; pthread_barrier_init; pthread_barrier_wait; pthread_barrierattr_destroy; pthread_barrierattr_getpshared; pthread_barrierattr_init; pthread_barrierattr_setpshared; pthread_attr_destroy; pthread_attr_get_np; pthread_attr_getdetachstate; pthread_attr_getguardsize; pthread_attr_getinheritsched; pthread_attr_getschedparam; pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_getstack; pthread_attr_getstackaddr; pthread_attr_getstacksize; pthread_attr_init; pthread_attr_setcreatesuspend_np; pthread_attr_setdetachstate; pthread_attr_setguardsize; pthread_attr_setinheritsched; pthread_attr_setschedparam; pthread_attr_setschedpolicy; pthread_attr_setscope; pthread_attr_setstack; pthread_attr_setstackaddr; pthread_attr_setstacksize; pthread_cancel; pthread_cleanup_pop; pthread_cleanup_push; pthread_cond_broadcast; pthread_cond_destroy; pthread_cond_init; pthread_cond_signal; pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy; pthread_condattr_getclock; pthread_condattr_getpshared; pthread_condattr_init; pthread_condattr_setclock; pthread_condattr_setpshared; pthread_create; pthread_detach; pthread_equal; pthread_exit; pthread_getconcurrency; pthread_getprio; pthread_getschedparam; pthread_getspecific; pthread_join; pthread_key_create; pthread_key_delete; pthread_kill; pthread_main_np; pthread_multi_np; pthread_mutex_destroy; pthread_mutex_getprioceiling; pthread_mutex_init; pthread_mutex_lock; pthread_mutex_setprioceiling; pthread_mutex_timedlock; pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; pthread_mutexattr_getkind_np; pthread_mutexattr_getprioceiling; pthread_mutexattr_getpshared; pthread_mutexattr_getprotocol; pthread_mutexattr_gettype; pthread_mutexattr_init; pthread_mutexattr_setkind_np; pthread_mutexattr_setprioceiling; pthread_mutexattr_setprotocol; pthread_mutexattr_setpshared; pthread_mutexattr_settype; pthread_once; pthread_resume_all_np; pthread_resume_np; pthread_rwlock_destroy; pthread_rwlock_init; pthread_rwlock_rdlock; pthread_rwlock_timedrdlock; pthread_rwlock_timedwrlock; pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; pthread_rwlock_unlock; pthread_rwlock_wrlock; pthread_rwlockattr_destroy; pthread_rwlockattr_getpshared; pthread_rwlockattr_init; pthread_rwlockattr_setpshared; pthread_set_name_np; pthread_self; pthread_setcancelstate; pthread_setcanceltype; pthread_setconcurrency; pthread_setprio; pthread_setschedparam; pthread_setspecific; pthread_sigmask; pthread_single_np; pthread_spin_destroy; pthread_spin_init; pthread_spin_lock; pthread_spin_trylock; pthread_spin_unlock; pthread_suspend_all_np; pthread_suspend_np; pthread_switch_add_np; pthread_switch_delete_np; pthread_testcancel; pthread_timedjoin_np; pthread_yield; }; /* * List the private interfaces reserved for use in FreeBSD libraries. * These are not part of our application ABI. */ FBSDprivate_1.0 { __pthread_cond_timedwait; __pthread_cond_wait; __pthread_cxa_finalize; __pthread_mutex_init; __pthread_mutex_lock; __pthread_mutex_timedlock; __pthread_mutex_trylock; __pthread_distribute_static_tls; _pthread_atfork; _pthread_barrier_destroy; _pthread_barrier_init; _pthread_barrier_wait; _pthread_barrierattr_destroy; _pthread_barrierattr_getpshared; _pthread_barrierattr_init; _pthread_barrierattr_setpshared; _pthread_attr_destroy; _pthread_attr_get_np; _pthread_attr_getaffinity_np; _pthread_attr_getdetachstate; _pthread_attr_getguardsize; _pthread_attr_getinheritsched; _pthread_attr_getschedparam; _pthread_attr_getschedpolicy; _pthread_attr_getscope; _pthread_attr_getstack; _pthread_attr_getstackaddr; _pthread_attr_getstacksize; _pthread_attr_init; _pthread_attr_setaffinity_np; _pthread_attr_setcreatesuspend_np; _pthread_attr_setdetachstate; _pthread_attr_setguardsize; _pthread_attr_setinheritsched; _pthread_attr_setschedparam; _pthread_attr_setschedpolicy; _pthread_attr_setscope; _pthread_attr_setstack; _pthread_attr_setstackaddr; _pthread_attr_setstacksize; _pthread_cancel; _pthread_cancel_enter; _pthread_cancel_leave; _pthread_cleanup_pop; _pthread_cleanup_push; _pthread_cond_broadcast; _pthread_cond_destroy; _pthread_cond_init; _pthread_cond_signal; _pthread_cond_timedwait; _pthread_cond_wait; _pthread_condattr_destroy; _pthread_condattr_getclock; _pthread_condattr_getpshared; _pthread_condattr_init; _pthread_condattr_setclock; _pthread_condattr_setpshared; _pthread_create; _pthread_detach; _pthread_equal; _pthread_exit; _pthread_getaffinity_np; _pthread_getconcurrency; _pthread_getcpuclockid; _pthread_getprio; _pthread_getschedparam; _pthread_getspecific; _pthread_getthreadid_np; _pthread_join; _pthread_key_create; _pthread_key_delete; _pthread_kill; _pthread_main_np; _pthread_multi_np; _pthread_mutex_destroy; _pthread_mutex_getprioceiling; _pthread_mutex_getspinloops_np; _pthread_mutex_getyieldloops_np; _pthread_mutex_init; _pthread_mutex_init_calloc_cb; _pthread_mutex_isowned_np; _pthread_mutex_lock; _pthread_mutex_setprioceiling; _pthread_mutex_setspinloops_np; _pthread_mutex_setyieldloops_np; _pthread_mutex_timedlock; _pthread_mutex_trylock; _pthread_mutex_unlock; _pthread_mutexattr_destroy; _pthread_mutexattr_getkind_np; _pthread_mutexattr_getprioceiling; _pthread_mutexattr_getprotocol; _pthread_mutexattr_getpshared; _pthread_mutexattr_gettype; _pthread_mutexattr_init; _pthread_mutexattr_setkind_np; _pthread_mutexattr_setprioceiling; _pthread_mutexattr_setprotocol; _pthread_mutexattr_setpshared; _pthread_mutexattr_settype; _pthread_once; _pthread_resume_all_np; _pthread_resume_np; _pthread_rwlock_destroy; _pthread_rwlock_init; _pthread_rwlock_rdlock; _pthread_rwlock_timedrdlock; _pthread_rwlock_timedwrlock; _pthread_rwlock_tryrdlock; _pthread_rwlock_trywrlock; _pthread_rwlock_unlock; _pthread_rwlock_wrlock; _pthread_rwlockattr_destroy; _pthread_rwlockattr_getpshared; _pthread_rwlockattr_init; _pthread_rwlockattr_setpshared; _pthread_self; _pthread_set_name_np; _pthread_setaffinity_np; _pthread_setcancelstate; _pthread_setcanceltype; _pthread_setconcurrency; _pthread_setprio; _pthread_setschedparam; _pthread_setspecific; _pthread_sigmask; _pthread_single_np; _pthread_spin_destroy; _pthread_spin_init; _pthread_spin_lock; _pthread_spin_trylock; _pthread_spin_unlock; _pthread_suspend_all_np; _pthread_suspend_np; _pthread_switch_add_np; _pthread_switch_delete_np; _pthread_testcancel; _pthread_timedjoin_np; _pthread_yield; /* Debugger needs these. */ _libthr_debug; _thread_active_threads; _thread_bp_create; _thread_bp_death; _thread_event_mask; _thread_keytable; _thread_last_event; _thread_list; _thread_max_keys; _thread_off_attr_flags; _thread_off_dtv; _thread_off_event_buf; _thread_off_event_mask; _thread_off_key_allocated; _thread_off_key_destructor; _thread_off_linkmap; _thread_off_next; _thread_off_report_events; _thread_off_state; _thread_off_tcb; _thread_off_tid; _thread_off_tlsindex; _thread_size_key; _thread_state_running; _thread_state_zoombie; + + /* ABI bug workaround, indicate that pli->rtli_version is valid */ + _pli_rtli_version; }; FBSD_1.1 { __pthread_cleanup_pop_imp; __pthread_cleanup_push_imp; pthread_attr_getaffinity_np; pthread_attr_setaffinity_np; pthread_getaffinity_np; pthread_getcpuclockid; pthread_setaffinity_np; pthread_mutex_getspinloops_np; pthread_mutex_getyieldloops_np; pthread_mutex_isowned_np; pthread_mutex_setspinloops_np; pthread_mutex_setyieldloops_np; }; FBSD_1.2 { pthread_getthreadid_np; }; FBSD_1.4 { pthread_mutex_consistent; pthread_mutexattr_getrobust; pthread_mutexattr_setrobust; }; FBSD_1.5 { pthread_get_name_np; }; FBSD_1.6 { pthread_getname_np; pthread_peekjoin_np; pthread_setname_np; }; diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c index 291ca17e2068..5848fc6d5f61 100644 --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -1,246 +1,254 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006, David Xu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * A lockless rwlock for rtld. */ #include #include #include #include #include #include #include "libc_private.h" #include "rtld_lock.h" #include "thr_private.h" #undef errno extern int errno; static int _thr_rtld_clr_flag(int); static void *_thr_rtld_lock_create(void); static void _thr_rtld_lock_destroy(void *); static void _thr_rtld_lock_release(void *); static void _thr_rtld_rlock_acquire(void *); static int _thr_rtld_set_flag(int); static void _thr_rtld_wlock_acquire(void *); struct rtld_lock { struct urwlock lock; char _pad[CACHE_LINE_SIZE - sizeof(struct urwlock)]; }; static struct rtld_lock lock_place[MAX_RTLD_LOCKS] __aligned(CACHE_LINE_SIZE); static int busy_places; static void * _thr_rtld_lock_create(void) { int locki; struct rtld_lock *l; static const char fail[] = "_thr_rtld_lock_create failed\n"; for (locki = 0; locki < MAX_RTLD_LOCKS; locki++) { if ((busy_places & (1 << locki)) == 0) break; } if (locki == MAX_RTLD_LOCKS) { write(2, fail, sizeof(fail) - 1); return (NULL); } busy_places |= (1 << locki); l = &lock_place[locki]; l->lock.rw_flags = URWLOCK_PREFER_READER; return (l); } static void _thr_rtld_lock_destroy(void *lock) { int locki; size_t i; locki = (struct rtld_lock *)lock - &lock_place[0]; for (i = 0; i < sizeof(struct rtld_lock); ++i) ((char *)lock)[i] = 0; busy_places &= ~(1 << locki); } #define SAVE_ERRNO() { \ if (curthread != _thr_initial) \ errsave = curthread->error; \ else \ errsave = errno; \ } #define RESTORE_ERRNO() { \ if (curthread != _thr_initial) \ curthread->error = errsave; \ else \ errno = errsave; \ } static void _thr_rtld_rlock_acquire(void *lock) { struct pthread *curthread; struct rtld_lock *l; int errsave; curthread = _get_curthread(); SAVE_ERRNO(); l = (struct rtld_lock *)lock; THR_CRITICAL_ENTER(curthread); while (_thr_rwlock_rdlock(&l->lock, 0, NULL) != 0) ; curthread->rdlock_count++; RESTORE_ERRNO(); } static void _thr_rtld_wlock_acquire(void *lock) { struct pthread *curthread; struct rtld_lock *l; int errsave; curthread = _get_curthread(); SAVE_ERRNO(); l = (struct rtld_lock *)lock; THR_CRITICAL_ENTER(curthread); while (_thr_rwlock_wrlock(&l->lock, NULL) != 0) ; RESTORE_ERRNO(); } static void _thr_rtld_lock_release(void *lock) { struct pthread *curthread; struct rtld_lock *l; int32_t state; int errsave; curthread = _get_curthread(); SAVE_ERRNO(); l = (struct rtld_lock *)lock; state = l->lock.rw_state; if (_thr_rwlock_unlock(&l->lock) == 0) { if ((state & URWLOCK_WRITE_OWNER) == 0) curthread->rdlock_count--; THR_CRITICAL_LEAVE(curthread); } RESTORE_ERRNO(); } static int _thr_rtld_set_flag(int mask __unused) { /* * The caller's code in rtld-elf is broken, it is not signal safe, * just return zero to fool it. */ return (0); } static int _thr_rtld_clr_flag(int mask __unused) { return (0); } +/* + * ABI bug workaround: This symbol must be present for rtld to accept + * RTLI_VERSION from RtldLockInfo + */ +extern char _pli_rtli_version; +char _pli_rtli_version; + void _thr_rtld_init(void) { struct RtldLockInfo li; struct pthread *curthread; ucontext_t *uc; long dummy = -1; int uc_len; curthread = _get_curthread(); /* force to resolve _umtx_op PLT */ _umtx_op_err((struct umtx *)&dummy, UMTX_OP_WAKE, 1, 0, 0); /* force to resolve errno() PLT */ __error(); /* force to resolve memcpy PLT */ memcpy(&dummy, &dummy, sizeof(dummy)); mprotect(NULL, 0, 0); _rtld_get_stack_prot(); + li.rtli_version = RTLI_VERSION; li.lock_create = _thr_rtld_lock_create; li.lock_destroy = _thr_rtld_lock_destroy; li.rlock_acquire = _thr_rtld_rlock_acquire; li.wlock_acquire = _thr_rtld_wlock_acquire; li.lock_release = _thr_rtld_lock_release; li.thread_set_flag = _thr_rtld_set_flag; li.thread_clr_flag = _thr_rtld_clr_flag; li.at_fork = NULL; /* * Preresolve the symbols needed for the fork interposer. We * call _rtld_atfork_pre() and _rtld_atfork_post() with NULL * argument to indicate that no actual locking inside the * functions should happen. Neither rtld compat locks nor * libthr rtld locks cannot work there: * - compat locks do not handle the case of two locks taken * in write mode (the signal mask for the thread is corrupted); * - libthr locks would work, but locked rtld_bind_lock prevents * symbol resolution for _rtld_atfork_post. */ _rtld_atfork_pre(NULL); _rtld_atfork_post(NULL); _malloc_prefork(); _malloc_postfork(); getpid(); syscall(SYS_getpid); /* mask signals, also force to resolve __sys_sigprocmask PLT */ _thr_signal_block(curthread); _rtld_thread_init(&li); _thr_signal_unblock(curthread); _thr_signal_block_check_fast(); _thr_signal_block_setup(curthread); uc_len = __getcontextx_size(); uc = alloca(uc_len); getcontext(uc); __fillcontextx2((char *)uc); } diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index d94bd1a283e9..4d54c687ee6f 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -1,436 +1,451 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09 * $FreeBSD$ */ /* * Thread locking implementation for the dynamic linker. * * We use the "simple, non-scalable reader-preference lock" from: * * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on * Principles and Practice of Parallel Programming, April 1991. * * In this algorithm the lock is a single word. Its low-order bit is * set when a writer holds the lock. The remaining high-order bits * contain a count of readers desiring the lock. The algorithm requires * atomic "compare_and_store" and "add" operations, which we take * from machine/atomic.h. */ #include #include #include #include #include #include "debug.h" #include "rtld.h" #include "rtld_machdep.h" #include "rtld_libc.h" void _rtld_thread_init(struct RtldLockInfo *) __exported; void _rtld_atfork_pre(int *) __exported; void _rtld_atfork_post(int *) __exported; #define WAFLAG 0x1 /* A writer holds the lock */ #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ typedef struct Struct_Lock { volatile u_int lock; void *base; } Lock; static sigset_t fullsigmask, oldsigmask; static int thread_flag, wnested; static uint32_t fsigblock; static void * def_lock_create(void) { void *base; char *p; uintptr_t r; Lock *l; /* * Arrange for the lock to occupy its own cache line. First, we * optimistically allocate just a cache line, hoping that malloc * will give us a well-aligned block of memory. If that doesn't * work, we allocate a larger block and take a well-aligned cache * line from it. */ base = xmalloc(CACHE_LINE_SIZE); p = (char *)base; if ((uintptr_t)p % CACHE_LINE_SIZE != 0) { free(base); base = xmalloc(2 * CACHE_LINE_SIZE); p = (char *)base; if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0) p += CACHE_LINE_SIZE - r; } l = (Lock *)p; l->base = base; l->lock = 0; return l; } static void def_lock_destroy(void *lock) { Lock *l = (Lock *)lock; free(l->base); } static void def_rlock_acquire(void *lock) { Lock *l = (Lock *)lock; atomic_add_acq_int(&l->lock, RC_INCR); while (l->lock & WAFLAG) ; /* Spin */ } static void sig_fastunblock(void) { uint32_t oldval; assert((fsigblock & ~SIGFASTBLOCK_FLAGS) >= SIGFASTBLOCK_INC); oldval = atomic_fetchadd_32(&fsigblock, -SIGFASTBLOCK_INC); if (oldval == (SIGFASTBLOCK_PEND | SIGFASTBLOCK_INC)) __sys_sigfastblock(SIGFASTBLOCK_UNBLOCK, NULL); } static void def_wlock_acquire(void *lock) { Lock *l; sigset_t tmp_oldsigmask; l = (Lock *)lock; if (ld_fast_sigblock) { for (;;) { atomic_add_32(&fsigblock, SIGFASTBLOCK_INC); if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) break; sig_fastunblock(); } } else { for (;;) { sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) break; sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); } if (atomic_fetchadd_int(&wnested, 1) == 0) oldsigmask = tmp_oldsigmask; } } static void def_lock_release(void *lock) { Lock *l; l = (Lock *)lock; if ((l->lock & WAFLAG) == 0) atomic_add_rel_int(&l->lock, -RC_INCR); else { atomic_add_rel_int(&l->lock, -WAFLAG); if (ld_fast_sigblock) sig_fastunblock(); else if (atomic_fetchadd_int(&wnested, -1) == 1) sigprocmask(SIG_SETMASK, &oldsigmask, NULL); } } static int def_thread_set_flag(int mask) { int old_val = thread_flag; thread_flag |= mask; return (old_val); } static int def_thread_clr_flag(int mask) { int old_val = thread_flag; thread_flag &= ~mask; return (old_val); } /* * Public interface exposed to the rest of the dynamic linker. */ struct RtldLockInfo lockinfo; static struct RtldLockInfo deflockinfo; static __inline int thread_mask_set(int mask) { return lockinfo.thread_set_flag(mask); } static __inline void thread_mask_clear(int mask) { lockinfo.thread_clr_flag(mask); } #define RTLD_LOCK_CNT 3 static struct rtld_lock { void *handle; int mask; } rtld_locks[RTLD_LOCK_CNT]; rtld_lock_t rtld_bind_lock = &rtld_locks[0]; rtld_lock_t rtld_libc_lock = &rtld_locks[1]; rtld_lock_t rtld_phdr_lock = &rtld_locks[2]; void rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; if (thread_mask_set(lock->mask) & lock->mask) { dbg("rlock_acquire: recursed"); lockstate->lockstate = RTLD_LOCK_UNLOCKED; return; } lockinfo.rlock_acquire(lock->handle); lockstate->lockstate = RTLD_LOCK_RLOCKED; } void wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; if (thread_mask_set(lock->mask) & lock->mask) { dbg("wlock_acquire: recursed"); lockstate->lockstate = RTLD_LOCK_UNLOCKED; return; } lockinfo.wlock_acquire(lock->handle); lockstate->lockstate = RTLD_LOCK_WLOCKED; } void lock_release(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; switch (lockstate->lockstate) { case RTLD_LOCK_UNLOCKED: break; case RTLD_LOCK_RLOCKED: case RTLD_LOCK_WLOCKED: thread_mask_clear(lock->mask); lockinfo.lock_release(lock->handle); break; default: assert(0); } } void lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; lock_release(lock, lockstate); wlock_acquire(lock, lockstate); } void lock_restart_for_upgrade(RtldLockState *lockstate) { if (lockstate == NULL) return; switch (lockstate->lockstate) { case RTLD_LOCK_UNLOCKED: case RTLD_LOCK_WLOCKED: break; case RTLD_LOCK_RLOCKED: siglongjmp(lockstate->env, 1); break; default: assert(0); } } void lockdflt_init(void) { int i; deflockinfo.rtli_version = RTLI_VERSION; deflockinfo.lock_create = def_lock_create; deflockinfo.lock_destroy = def_lock_destroy; deflockinfo.rlock_acquire = def_rlock_acquire; deflockinfo.wlock_acquire = def_wlock_acquire; deflockinfo.lock_release = def_lock_release; deflockinfo.thread_set_flag = def_thread_set_flag; deflockinfo.thread_clr_flag = def_thread_clr_flag; deflockinfo.at_fork = NULL; for (i = 0; i < RTLD_LOCK_CNT; i++) { rtld_locks[i].mask = (1 << i); rtld_locks[i].handle = NULL; } memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); _rtld_thread_init(NULL); if (ld_fast_sigblock) { __sys_sigfastblock(SIGFASTBLOCK_SETPTR, &fsigblock); } else { /* * Construct a mask to block all signals. Note that * blocked traps mean that the process is terminated * if trap occurs while we are in locked section, with * the default settings for kern.forcesigexit. */ sigfillset(&fullsigmask); } } /* * Callback function to allow threads implementation to * register their own locking primitives if the default * one is not suitable. * The current context should be the only context * executing at the invocation time. */ void _rtld_thread_init(struct RtldLockInfo *pli) { - int flags, i; + const Obj_Entry *obj; + SymLook req; void *locks[RTLD_LOCK_CNT]; + int flags, i, res; + + if (pli == NULL) { + lockinfo.rtli_version = RTLI_VERSION; + } else { + lockinfo.rtli_version = RTLI_VERSION_ONE; + obj = obj_from_addr(pli->lock_create); + if (obj != NULL) { + symlook_init(&req, "_pli_rtli_version"); + res = symlook_obj(&req, obj); + if (res == 0) + lockinfo.rtli_version = pli->rtli_version; + } + } /* disable all locking while this function is running */ flags = thread_mask_set(~0); if (pli == NULL) pli = &deflockinfo; else if (ld_fast_sigblock) { fsigblock = 0; __sys_sigfastblock(SIGFASTBLOCK_UNSETPTR, NULL); } for (i = 0; i < RTLD_LOCK_CNT; i++) if ((locks[i] = pli->lock_create()) == NULL) break; if (i < RTLD_LOCK_CNT) { while (--i >= 0) pli->lock_destroy(locks[i]); abort(); } for (i = 0; i < RTLD_LOCK_CNT; i++) { if (rtld_locks[i].handle == NULL) continue; if (flags & rtld_locks[i].mask) lockinfo.lock_release(rtld_locks[i].handle); lockinfo.lock_destroy(rtld_locks[i].handle); } for (i = 0; i < RTLD_LOCK_CNT; i++) { rtld_locks[i].handle = locks[i]; if (flags & rtld_locks[i].mask) pli->wlock_acquire(rtld_locks[i].handle); } lockinfo.lock_create = pli->lock_create; lockinfo.lock_destroy = pli->lock_destroy; lockinfo.rlock_acquire = pli->rlock_acquire; lockinfo.wlock_acquire = pli->wlock_acquire; lockinfo.lock_release = pli->lock_release; lockinfo.thread_set_flag = pli->thread_set_flag; lockinfo.thread_clr_flag = pli->thread_clr_flag; lockinfo.at_fork = pli->at_fork; /* restore thread locking state, this time with new locks */ thread_mask_clear(~0); thread_mask_set(flags); dbg("_rtld_thread_init: done"); } void _rtld_atfork_pre(int *locks) { RtldLockState ls[2]; if (locks == NULL) return; /* * Warning: this did not worked well with the rtld compat * locks above, when the thread signal mask was corrupted (set * to all signals blocked) if two locks were taken * simultaneously in the write mode. The caller of the * _rtld_atfork_pre() must provide the working implementation * of the locks anyway, and libthr locks are fine. */ wlock_acquire(rtld_phdr_lock, &ls[0]); wlock_acquire(rtld_bind_lock, &ls[1]); /* XXXKIB: I am really sorry for this. */ locks[0] = ls[1].lockstate; locks[2] = ls[0].lockstate; } void _rtld_atfork_post(int *locks) { RtldLockState ls[2]; if (locks == NULL) return; bzero(ls, sizeof(ls)); ls[0].lockstate = locks[2]; ls[1].lockstate = locks[0]; lock_release(rtld_bind_lock, &ls[1]); lock_release(rtld_phdr_lock, &ls[0]); } diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h index ecc733a06e44..7a61a1a525e2 100644 --- a/libexec/rtld-elf/rtld_lock.h +++ b/libexec/rtld-elf/rtld_lock.h @@ -1,83 +1,85 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2003 Alexander Kabaev. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _RTLD_LOCK_H_ #define _RTLD_LOCK_H_ -#define RTLI_VERSION 0x01 +#define RTLI_VERSION_ONE 0x01 +#define RTLI_VERSION 0x01 + #define MAX_RTLD_LOCKS 8 struct RtldLockInfo { unsigned int rtli_version; void *(*lock_create)(void); void (*lock_destroy)(void *); void (*rlock_acquire)(void *); void (*wlock_acquire)(void *); void (*lock_release)(void *); int (*thread_set_flag)(int); int (*thread_clr_flag)(int); void (*at_fork)(void); }; #if defined(IN_RTLD) || defined(PTHREAD_KERNEL) void _rtld_thread_init(struct RtldLockInfo *) __exported; void _rtld_atfork_pre(int *) __exported; void _rtld_atfork_post(int *) __exported; #endif /* IN_RTLD || PTHREAD_KERNEL */ #ifdef IN_RTLD struct rtld_lock; typedef struct rtld_lock *rtld_lock_t; extern rtld_lock_t rtld_bind_lock; extern rtld_lock_t rtld_libc_lock; extern rtld_lock_t rtld_phdr_lock; extern struct RtldLockInfo lockinfo; #define RTLD_LOCK_UNLOCKED 0 #define RTLD_LOCK_RLOCKED 1 #define RTLD_LOCK_WLOCKED 2 struct Struct_RtldLockState; typedef struct Struct_RtldLockState RtldLockState; void rlock_acquire(rtld_lock_t, RtldLockState *); void wlock_acquire(rtld_lock_t, RtldLockState *); void lock_release(rtld_lock_t, RtldLockState *); void lock_upgrade(rtld_lock_t, RtldLockState *); void lock_restart_for_upgrade(RtldLockState *); #endif /* IN_RTLD */ #endif