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 "debug.h" 51 #include "rtld.h" 52 #include "rtld_machdep.h" 53 54 void _rtld_thread_init(struct RtldLockInfo *) __exported; 55 void _rtld_atfork_pre(int *) __exported; 56 void _rtld_atfork_post(int *) __exported; 57 58 #define WAFLAG 0x1 /* A writer holds the lock */ 59 #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ 60 61 typedef struct Struct_Lock { 62 volatile u_int lock; 63 void *base; 64 } Lock; 65 66 static sigset_t fullsigmask, oldsigmask; 67 static int thread_flag; 68 69 static void * 70 def_lock_create() 71 { 72 void *base; 73 char *p; 74 uintptr_t r; 75 Lock *l; 76 77 /* 78 * Arrange for the lock to occupy its own cache line. First, we 79 * optimistically allocate just a cache line, hoping that malloc 80 * will give us a well-aligned block of memory. If that doesn't 81 * work, we allocate a larger block and take a well-aligned cache 82 * line from it. 83 */ 84 base = xmalloc(CACHE_LINE_SIZE); 85 p = (char *)base; 86 if ((uintptr_t)p % CACHE_LINE_SIZE != 0) { 87 free(base); 88 base = xmalloc(2 * CACHE_LINE_SIZE); 89 p = (char *)base; 90 if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0) 91 p += CACHE_LINE_SIZE - r; 92 } 93 l = (Lock *)p; 94 l->base = base; 95 l->lock = 0; 96 return l; 97 } 98 99 static void 100 def_lock_destroy(void *lock) 101 { 102 Lock *l = (Lock *)lock; 103 104 free(l->base); 105 } 106 107 static void 108 def_rlock_acquire(void *lock) 109 { 110 Lock *l = (Lock *)lock; 111 112 atomic_add_acq_int(&l->lock, RC_INCR); 113 while (l->lock & WAFLAG) 114 ; /* Spin */ 115 } 116 117 static void 118 def_wlock_acquire(void *lock) 119 { 120 Lock *l = (Lock *)lock; 121 sigset_t tmp_oldsigmask; 122 123 for ( ; ; ) { 124 sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); 125 if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) 126 break; 127 sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); 128 } 129 oldsigmask = tmp_oldsigmask; 130 } 131 132 static void 133 def_lock_release(void *lock) 134 { 135 Lock *l = (Lock *)lock; 136 137 if ((l->lock & WAFLAG) == 0) 138 atomic_add_rel_int(&l->lock, -RC_INCR); 139 else { 140 atomic_add_rel_int(&l->lock, -WAFLAG); 141 sigprocmask(SIG_SETMASK, &oldsigmask, NULL); 142 } 143 } 144 145 static int 146 def_thread_set_flag(int mask) 147 { 148 int old_val = thread_flag; 149 thread_flag |= mask; 150 return (old_val); 151 } 152 153 static int 154 def_thread_clr_flag(int mask) 155 { 156 int old_val = thread_flag; 157 thread_flag &= ~mask; 158 return (old_val); 159 } 160 161 /* 162 * Public interface exposed to the rest of the dynamic linker. 163 */ 164 static struct RtldLockInfo lockinfo; 165 static struct RtldLockInfo deflockinfo; 166 167 static __inline int 168 thread_mask_set(int mask) 169 { 170 return lockinfo.thread_set_flag(mask); 171 } 172 173 static __inline void 174 thread_mask_clear(int mask) 175 { 176 lockinfo.thread_clr_flag(mask); 177 } 178 179 #define RTLD_LOCK_CNT 3 180 struct rtld_lock { 181 void *handle; 182 int mask; 183 } rtld_locks[RTLD_LOCK_CNT]; 184 185 rtld_lock_t rtld_bind_lock = &rtld_locks[0]; 186 rtld_lock_t rtld_libc_lock = &rtld_locks[1]; 187 rtld_lock_t rtld_phdr_lock = &rtld_locks[2]; 188 189 void 190 rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) 191 { 192 193 if (lockstate == NULL) 194 return; 195 196 if (thread_mask_set(lock->mask) & lock->mask) { 197 dbg("rlock_acquire: recursed"); 198 lockstate->lockstate = RTLD_LOCK_UNLOCKED; 199 return; 200 } 201 lockinfo.rlock_acquire(lock->handle); 202 lockstate->lockstate = RTLD_LOCK_RLOCKED; 203 } 204 205 void 206 wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) 207 { 208 209 if (lockstate == NULL) 210 return; 211 212 if (thread_mask_set(lock->mask) & lock->mask) { 213 dbg("wlock_acquire: recursed"); 214 lockstate->lockstate = RTLD_LOCK_UNLOCKED; 215 return; 216 } 217 lockinfo.wlock_acquire(lock->handle); 218 lockstate->lockstate = RTLD_LOCK_WLOCKED; 219 } 220 221 void 222 lock_release(rtld_lock_t lock, RtldLockState *lockstate) 223 { 224 225 if (lockstate == NULL) 226 return; 227 228 switch (lockstate->lockstate) { 229 case RTLD_LOCK_UNLOCKED: 230 break; 231 case RTLD_LOCK_RLOCKED: 232 case RTLD_LOCK_WLOCKED: 233 thread_mask_clear(lock->mask); 234 lockinfo.lock_release(lock->handle); 235 break; 236 default: 237 assert(0); 238 } 239 } 240 241 void 242 lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate) 243 { 244 245 if (lockstate == NULL) 246 return; 247 248 lock_release(lock, lockstate); 249 wlock_acquire(lock, lockstate); 250 } 251 252 void 253 lock_restart_for_upgrade(RtldLockState *lockstate) 254 { 255 256 if (lockstate == NULL) 257 return; 258 259 switch (lockstate->lockstate) { 260 case RTLD_LOCK_UNLOCKED: 261 case RTLD_LOCK_WLOCKED: 262 break; 263 case RTLD_LOCK_RLOCKED: 264 siglongjmp(lockstate->env, 1); 265 break; 266 default: 267 assert(0); 268 } 269 } 270 271 void 272 lockdflt_init() 273 { 274 int i; 275 276 deflockinfo.rtli_version = RTLI_VERSION; 277 deflockinfo.lock_create = def_lock_create; 278 deflockinfo.lock_destroy = def_lock_destroy; 279 deflockinfo.rlock_acquire = def_rlock_acquire; 280 deflockinfo.wlock_acquire = def_wlock_acquire; 281 deflockinfo.lock_release = def_lock_release; 282 deflockinfo.thread_set_flag = def_thread_set_flag; 283 deflockinfo.thread_clr_flag = def_thread_clr_flag; 284 deflockinfo.at_fork = NULL; 285 286 for (i = 0; i < RTLD_LOCK_CNT; i++) { 287 rtld_locks[i].mask = (1 << i); 288 rtld_locks[i].handle = NULL; 289 } 290 291 memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); 292 _rtld_thread_init(NULL); 293 /* 294 * Construct a mask to block all signals except traps which might 295 * conceivably be generated within the dynamic linker itself. 296 */ 297 sigfillset(&fullsigmask); 298 sigdelset(&fullsigmask, SIGILL); 299 sigdelset(&fullsigmask, SIGTRAP); 300 sigdelset(&fullsigmask, SIGABRT); 301 sigdelset(&fullsigmask, SIGEMT); 302 sigdelset(&fullsigmask, SIGFPE); 303 sigdelset(&fullsigmask, SIGBUS); 304 sigdelset(&fullsigmask, SIGSEGV); 305 sigdelset(&fullsigmask, SIGSYS); 306 } 307 308 /* 309 * Callback function to allow threads implementation to 310 * register their own locking primitives if the default 311 * one is not suitable. 312 * The current context should be the only context 313 * executing at the invocation time. 314 */ 315 void 316 _rtld_thread_init(struct RtldLockInfo *pli) 317 { 318 int flags, i; 319 void *locks[RTLD_LOCK_CNT]; 320 321 /* disable all locking while this function is running */ 322 flags = thread_mask_set(~0); 323 324 if (pli == NULL) 325 pli = &deflockinfo; 326 327 328 for (i = 0; i < RTLD_LOCK_CNT; i++) 329 if ((locks[i] = pli->lock_create()) == NULL) 330 break; 331 332 if (i < RTLD_LOCK_CNT) { 333 while (--i >= 0) 334 pli->lock_destroy(locks[i]); 335 abort(); 336 } 337 338 for (i = 0; i < RTLD_LOCK_CNT; i++) { 339 if (rtld_locks[i].handle == NULL) 340 continue; 341 if (flags & rtld_locks[i].mask) 342 lockinfo.lock_release(rtld_locks[i].handle); 343 lockinfo.lock_destroy(rtld_locks[i].handle); 344 } 345 346 for (i = 0; i < RTLD_LOCK_CNT; i++) { 347 rtld_locks[i].handle = locks[i]; 348 if (flags & rtld_locks[i].mask) 349 pli->wlock_acquire(rtld_locks[i].handle); 350 } 351 352 lockinfo.lock_create = pli->lock_create; 353 lockinfo.lock_destroy = pli->lock_destroy; 354 lockinfo.rlock_acquire = pli->rlock_acquire; 355 lockinfo.wlock_acquire = pli->wlock_acquire; 356 lockinfo.lock_release = pli->lock_release; 357 lockinfo.thread_set_flag = pli->thread_set_flag; 358 lockinfo.thread_clr_flag = pli->thread_clr_flag; 359 lockinfo.at_fork = pli->at_fork; 360 361 /* restore thread locking state, this time with new locks */ 362 thread_mask_clear(~0); 363 thread_mask_set(flags); 364 dbg("_rtld_thread_init: done"); 365 } 366 367 void 368 _rtld_atfork_pre(int *locks) 369 { 370 RtldLockState ls[2]; 371 372 if (locks == NULL) 373 return; 374 375 /* 376 * Warning: this does not work with the rtld compat locks 377 * above, since the thread signal mask is corrupted (set to 378 * all signals blocked) if two locks are taken in write mode. 379 * The caller of the _rtld_atfork_pre() must provide the 380 * working implementation of the locks, and libthr locks are 381 * fine. 382 */ 383 wlock_acquire(rtld_phdr_lock, &ls[0]); 384 wlock_acquire(rtld_bind_lock, &ls[1]); 385 386 /* XXXKIB: I am really sorry for this. */ 387 locks[0] = ls[1].lockstate; 388 locks[2] = ls[0].lockstate; 389 } 390 391 void 392 _rtld_atfork_post(int *locks) 393 { 394 RtldLockState ls[2]; 395 396 if (locks == NULL) 397 return; 398 399 bzero(ls, sizeof(ls)); 400 ls[0].lockstate = locks[2]; 401 ls[1].lockstate = locks[0]; 402 lock_release(rtld_bind_lock, &ls[1]); 403 lock_release(rtld_phdr_lock, &ls[0]); 404 } 405