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