1fcf53d9bSJohn Marino /*- 2fcf53d9bSJohn Marino * Copyright 1999, 2000 John D. Polstra. 3fcf53d9bSJohn Marino * All rights reserved. 4fcf53d9bSJohn Marino * 5fcf53d9bSJohn Marino * Redistribution and use in source and binary forms, with or without 6fcf53d9bSJohn Marino * modification, are permitted provided that the following conditions 7fcf53d9bSJohn Marino * are met: 8fcf53d9bSJohn Marino * 1. Redistributions of source code must retain the above copyright 9fcf53d9bSJohn Marino * notice, this list of conditions and the following disclaimer. 10fcf53d9bSJohn Marino * 2. Redistributions in binary form must reproduce the above copyright 11fcf53d9bSJohn Marino * notice, this list of conditions and the following disclaimer in the 12fcf53d9bSJohn Marino * documentation and/or other materials provided with the distribution. 13fcf53d9bSJohn Marino * 14fcf53d9bSJohn Marino * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15fcf53d9bSJohn Marino * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16fcf53d9bSJohn Marino * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17fcf53d9bSJohn Marino * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18fcf53d9bSJohn Marino * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19fcf53d9bSJohn Marino * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20fcf53d9bSJohn Marino * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21fcf53d9bSJohn Marino * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22fcf53d9bSJohn Marino * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23fcf53d9bSJohn Marino * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24fcf53d9bSJohn Marino * 25fcf53d9bSJohn Marino * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09 26f4f4bfd5SJohn Marino * $FreeBSD$ 27fcf53d9bSJohn Marino */ 28fcf53d9bSJohn Marino 29fcf53d9bSJohn Marino /* 30fcf53d9bSJohn Marino * Thread locking implementation for the dynamic linker. 31fcf53d9bSJohn Marino * 32fcf53d9bSJohn Marino * We use the "simple, non-scalable reader-preference lock" from: 33fcf53d9bSJohn Marino * 34fcf53d9bSJohn Marino * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer 35fcf53d9bSJohn Marino * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on 36fcf53d9bSJohn Marino * Principles and Practice of Parallel Programming, April 1991. 37fcf53d9bSJohn Marino * 38fcf53d9bSJohn Marino * In this algorithm the lock is a single word. Its low-order bit is 39fcf53d9bSJohn Marino * set when a writer holds the lock. The remaining high-order bits 40fcf53d9bSJohn Marino * contain a count of readers desiring the lock. The algorithm requires 41fcf53d9bSJohn Marino * atomic "compare_and_store" and "add" operations, which we implement 42fcf53d9bSJohn Marino * using assembly language sequences in "rtld_start.S". 43fcf53d9bSJohn Marino */ 44fcf53d9bSJohn Marino 45fcf53d9bSJohn Marino #include <sys/param.h> 46fcf53d9bSJohn Marino #include <signal.h> 47fcf53d9bSJohn Marino #include <stdlib.h> 48fcf53d9bSJohn Marino #include <time.h> 49fcf53d9bSJohn Marino 50*98247283SMatthew Dillon #include <stdio.h> 51*98247283SMatthew Dillon #include <sys/file.h> 52*98247283SMatthew Dillon 53fcf53d9bSJohn Marino #include "debug.h" 54fcf53d9bSJohn Marino #include "rtld.h" 55fcf53d9bSJohn Marino #include "rtld_machdep.h" 56fcf53d9bSJohn Marino 57*98247283SMatthew Dillon extern pid_t __sys_getpid(void); 58*98247283SMatthew Dillon 59fcf53d9bSJohn Marino #define WAFLAG 0x1 /* A writer holds the lock */ 60fcf53d9bSJohn Marino #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ 61fcf53d9bSJohn Marino 62*98247283SMatthew Dillon struct Struct_Lock { 63fcf53d9bSJohn Marino volatile u_int lock; 64*98247283SMatthew Dillon int tid; /* owner (exclusive) */ 65*98247283SMatthew Dillon int count; /* recursion (exclusive) */ 66*98247283SMatthew Dillon sigset_t savesigmask; /* first exclusive owner sets mask */ 67*98247283SMatthew Dillon } __cachealign; 68fcf53d9bSJohn Marino 69*98247283SMatthew Dillon #define cpu_ccfence() __asm __volatile("" : : : "memory") 70fcf53d9bSJohn Marino 71*98247283SMatthew Dillon static sigset_t fullsigmask; 72*98247283SMatthew Dillon 73*98247283SMatthew Dillon struct Struct_Lock phdr_lock; 74*98247283SMatthew Dillon struct Struct_Lock bind_lock; 75*98247283SMatthew Dillon struct Struct_Lock libc_lock; 76*98247283SMatthew Dillon 77*98247283SMatthew Dillon rtld_lock_t rtld_phdr_lock = &phdr_lock; 78*98247283SMatthew Dillon rtld_lock_t rtld_bind_lock = &bind_lock; 79*98247283SMatthew Dillon rtld_lock_t rtld_libc_lock = &libc_lock; 80*98247283SMatthew Dillon 81*98247283SMatthew Dillon void 82*98247283SMatthew Dillon rlock_acquire(rtld_lock_t lock, RtldLockState *state) 83fcf53d9bSJohn Marino { 84*98247283SMatthew Dillon int v; 85*98247283SMatthew Dillon int tid = 0; 86fcf53d9bSJohn Marino 87*98247283SMatthew Dillon v = lock->lock; 88*98247283SMatthew Dillon cpu_ccfence(); 89fcf53d9bSJohn Marino for (;;) { 90*98247283SMatthew Dillon if ((v & WAFLAG) == 0) { 91*98247283SMatthew Dillon if (atomic_fcmpset_int(&lock->lock, &v, v + RC_INCR)) { 92*98247283SMatthew Dillon state->lockstate = RTLD_LOCK_RLOCKED; 93fcf53d9bSJohn Marino break; 94*98247283SMatthew Dillon } 95*98247283SMatthew Dillon } else { 96*98247283SMatthew Dillon if (tid == 0) 97*98247283SMatthew Dillon tid = lwp_gettid(); 98*98247283SMatthew Dillon if (lock->tid == tid) { 99*98247283SMatthew Dillon ++lock->count; 100*98247283SMatthew Dillon state->lockstate = RTLD_LOCK_WLOCKED; 101*98247283SMatthew Dillon break; 102*98247283SMatthew Dillon } 103*98247283SMatthew Dillon umtx_sleep(&lock->lock, v, 0); 104*98247283SMatthew Dillon v = lock->lock; 105*98247283SMatthew Dillon cpu_ccfence(); 106*98247283SMatthew Dillon } 107*98247283SMatthew Dillon } 108*98247283SMatthew Dillon } 109*98247283SMatthew Dillon 110*98247283SMatthew Dillon void 111*98247283SMatthew Dillon wlock_acquire(rtld_lock_t lock, RtldLockState *state) 112*98247283SMatthew Dillon { 113*98247283SMatthew Dillon sigset_t tmp_oldsigmask; 114*98247283SMatthew Dillon int tid = lwp_gettid(); 115*98247283SMatthew Dillon 116*98247283SMatthew Dillon if (lock->tid == tid) { 117*98247283SMatthew Dillon ++lock->count; 118*98247283SMatthew Dillon state->lockstate = RTLD_LOCK_WLOCKED; 119*98247283SMatthew Dillon return; 120*98247283SMatthew Dillon } 121*98247283SMatthew Dillon 122*98247283SMatthew Dillon sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); 123*98247283SMatthew Dillon for (;;) { 124*98247283SMatthew Dillon if (atomic_cmpset_acq_int(&lock->lock, 0, WAFLAG)) 125*98247283SMatthew Dillon break; 126*98247283SMatthew Dillon umtx_sleep(&lock->lock, 0, 0); 127*98247283SMatthew Dillon } 128*98247283SMatthew Dillon lock->tid = tid; 129*98247283SMatthew Dillon lock->count = 1; 130*98247283SMatthew Dillon lock->savesigmask = tmp_oldsigmask; 131*98247283SMatthew Dillon state->lockstate = RTLD_LOCK_WLOCKED; 132*98247283SMatthew Dillon } 133*98247283SMatthew Dillon 134*98247283SMatthew Dillon void 135*98247283SMatthew Dillon lock_release(rtld_lock_t lock, RtldLockState *state) 136*98247283SMatthew Dillon { 137*98247283SMatthew Dillon sigset_t tmp_oldsigmask; 138*98247283SMatthew Dillon int v; 139*98247283SMatthew Dillon 140*98247283SMatthew Dillon if (state->lockstate == RTLD_LOCK_UNLOCKED) 141*98247283SMatthew Dillon return; 142*98247283SMatthew Dillon if ((lock->lock & WAFLAG) == 0) { 143*98247283SMatthew Dillon v = atomic_fetchadd_int(&lock->lock, -RC_INCR) - RC_INCR; 144*98247283SMatthew Dillon if (v == 0) 145*98247283SMatthew Dillon umtx_wakeup(&lock->lock, 0); 146*98247283SMatthew Dillon } else if (--lock->count == 0) { 147*98247283SMatthew Dillon tmp_oldsigmask = lock->savesigmask; 148*98247283SMatthew Dillon lock->tid = 0; 149*98247283SMatthew Dillon v = atomic_fetchadd_int(&lock->lock, -WAFLAG) - WAFLAG; 150*98247283SMatthew Dillon if (v == 0) 151*98247283SMatthew Dillon umtx_wakeup(&lock->lock, 0); 152fcf53d9bSJohn Marino sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); 153fcf53d9bSJohn Marino } 154*98247283SMatthew Dillon state->lockstate = RTLD_LOCK_UNLOCKED; 155fcf53d9bSJohn Marino } 156fcf53d9bSJohn Marino 157*98247283SMatthew Dillon static 158fcf53d9bSJohn Marino void 159*98247283SMatthew Dillon lock_reset(rtld_lock_t lock) 160fcf53d9bSJohn Marino { 161*98247283SMatthew Dillon memset(lock, 0, sizeof(*lock)); 162fcf53d9bSJohn Marino } 163fcf53d9bSJohn Marino 164fcf53d9bSJohn Marino void 165*98247283SMatthew Dillon lock_upgrade(rtld_lock_t lock, RtldLockState *state) 166fcf53d9bSJohn Marino { 167*98247283SMatthew Dillon if (state == NULL) 168fcf53d9bSJohn Marino return; 169*98247283SMatthew Dillon if (state->lockstate == RTLD_LOCK_RLOCKED) { 170*98247283SMatthew Dillon lock_release(lock, state); 171*98247283SMatthew Dillon wlock_acquire(lock, state); 172fcf53d9bSJohn Marino } 173fcf53d9bSJohn Marino } 174fcf53d9bSJohn Marino 175fcf53d9bSJohn Marino void 176*98247283SMatthew Dillon lock_restart_for_upgrade(RtldLockState *state) 177fcf53d9bSJohn Marino { 178*98247283SMatthew Dillon if (state == NULL) 179fcf53d9bSJohn Marino return; 180*98247283SMatthew Dillon switch (state->lockstate) { 181fcf53d9bSJohn Marino case RTLD_LOCK_UNLOCKED: 182fcf53d9bSJohn Marino case RTLD_LOCK_WLOCKED: 183fcf53d9bSJohn Marino break; 184fcf53d9bSJohn Marino case RTLD_LOCK_RLOCKED: 185*98247283SMatthew Dillon siglongjmp(state->env, 1); 186fcf53d9bSJohn Marino break; 187fcf53d9bSJohn Marino default: 188fcf53d9bSJohn Marino assert(0); 189fcf53d9bSJohn Marino } 190fcf53d9bSJohn Marino } 191fcf53d9bSJohn Marino 192fcf53d9bSJohn Marino void 193472de6d1SSascha Wildner lockdflt_init(void) 194fcf53d9bSJohn Marino { 195fcf53d9bSJohn Marino /* 196fcf53d9bSJohn Marino * Construct a mask to block all signals except traps which might 197fcf53d9bSJohn Marino * conceivably be generated within the dynamic linker itself. 198fcf53d9bSJohn Marino */ 199fcf53d9bSJohn Marino sigfillset(&fullsigmask); 200fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGILL); 201fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGTRAP); 202fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGABRT); 203fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGEMT); 204fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGFPE); 205fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGBUS); 206fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGSEGV); 207fcf53d9bSJohn Marino sigdelset(&fullsigmask, SIGSYS); 208*98247283SMatthew Dillon 209*98247283SMatthew Dillon _rtld_thread_init(NULL); 210fcf53d9bSJohn Marino } 211fcf53d9bSJohn Marino 212fcf53d9bSJohn Marino /* 213*98247283SMatthew Dillon * (also called by pthreads) 214fcf53d9bSJohn Marino */ 215fcf53d9bSJohn Marino void 216*98247283SMatthew Dillon _rtld_thread_init(void *dummy __unused) 217fcf53d9bSJohn Marino { 218*98247283SMatthew Dillon lock_reset(rtld_phdr_lock); 219*98247283SMatthew Dillon lock_reset(rtld_bind_lock); 220*98247283SMatthew Dillon lock_reset(rtld_libc_lock); 221e19be507SMatthew Dillon } 222fcf53d9bSJohn Marino 223*98247283SMatthew Dillon static RtldLockState fork_states[3]; 224e19be507SMatthew Dillon 225e19be507SMatthew Dillon void 226e19be507SMatthew Dillon _rtld_thread_prefork(void) 227e19be507SMatthew Dillon { 228*98247283SMatthew Dillon wlock_acquire(rtld_phdr_lock, &fork_states[0]); 229*98247283SMatthew Dillon wlock_acquire(rtld_bind_lock, &fork_states[1]); 230*98247283SMatthew Dillon wlock_acquire(rtld_libc_lock, &fork_states[2]); 231e19be507SMatthew Dillon } 232e19be507SMatthew Dillon 233e19be507SMatthew Dillon void 234e19be507SMatthew Dillon _rtld_thread_postfork(void) 235e19be507SMatthew Dillon { 236*98247283SMatthew Dillon lock_release(rtld_libc_lock, &fork_states[2]); 237*98247283SMatthew Dillon lock_release(rtld_bind_lock, &fork_states[1]); 238*98247283SMatthew Dillon lock_release(rtld_phdr_lock, &fork_states[0]); 239e19be507SMatthew Dillon } 240e19be507SMatthew Dillon 241e19be507SMatthew Dillon void 242e19be507SMatthew Dillon _rtld_thread_childfork(void) 243e19be507SMatthew Dillon { 244*98247283SMatthew Dillon sigset_t tmp_oldsigmask; 245*98247283SMatthew Dillon 246*98247283SMatthew Dillon lock_reset(rtld_phdr_lock); 247*98247283SMatthew Dillon lock_reset(rtld_bind_lock); 248*98247283SMatthew Dillon tmp_oldsigmask = rtld_libc_lock->savesigmask; 249*98247283SMatthew Dillon lock_reset(rtld_libc_lock); 250*98247283SMatthew Dillon sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); 251e19be507SMatthew Dillon } 252