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