rtld_lock.c revision 183061
1115396Skan/*- 2115396Skan * Copyright 1999, 2000 John D. Polstra. 3115396Skan * All rights reserved. 4115396Skan * 5115396Skan * Redistribution and use in source and binary forms, with or without 6115396Skan * modification, are permitted provided that the following conditions 7115396Skan * are met: 8115396Skan * 1. Redistributions of source code must retain the above copyright 9115396Skan * notice, this list of conditions and the following disclaimer. 10115396Skan * 2. Redistributions in binary form must reproduce the above copyright 11115396Skan * notice, this list of conditions and the following disclaimer in the 12115396Skan * documentation and/or other materials provided with the distribution. 13115396Skan * 14115396Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15115396Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16115396Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17115396Skan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18115396Skan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19115396Skan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20115396Skan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21115396Skan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22115396Skan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23115396Skan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24115396Skan * 25115396Skan * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09 26115396Skan * $FreeBSD: head/libexec/rtld-elf/rtld_lock.c 183061 2008-09-16 01:46:11Z davidxu $ 27115396Skan */ 28115396Skan 29115396Skan/* 30115396Skan * Thread locking implementation for the dynamic linker. 31115396Skan * 32115396Skan * We use the "simple, non-scalable reader-preference lock" from: 33115396Skan * 34115396Skan * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer 35115396Skan * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on 36115396Skan * Principles and Practice of Parallel Programming, April 1991. 37115396Skan * 38115396Skan * In this algorithm the lock is a single word. Its low-order bit is 39115396Skan * set when a writer holds the lock. The remaining high-order bits 40115396Skan * contain a count of readers desiring the lock. The algorithm requires 41115396Skan * atomic "compare_and_store" and "add" operations, which we implement 42115396Skan * using assembly language sequences in "rtld_start.S". 43115396Skan */ 44115396Skan 45115396Skan#include <signal.h> 46115396Skan#include <stdlib.h> 47115396Skan#include <time.h> 48115396Skan 49115396Skan#include "debug.h" 50115396Skan#include "rtld.h" 51115396Skan#include "rtld_machdep.h" 52115396Skan 53115396Skan#define WAFLAG 0x1 /* A writer holds the lock */ 54115396Skan#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ 55115396Skan 56115396Skantypedef struct Struct_Lock { 57168311Skan volatile u_int lock; 58115396Skan void *base; 59115396Skan} Lock; 60115396Skan 61115396Skanstatic sigset_t fullsigmask, oldsigmask; 62115396Skanstatic int thread_flag; 63115396Skan 64115396Skanstatic void * 65115396Skandef_lock_create() 66115396Skan{ 67115396Skan void *base; 68115396Skan char *p; 69115396Skan uintptr_t r; 70115396Skan Lock *l; 71115396Skan 72115396Skan /* 73115396Skan * Arrange for the lock to occupy its own cache line. First, we 74115396Skan * optimistically allocate just a cache line, hoping that malloc 75115396Skan * will give us a well-aligned block of memory. If that doesn't 76115396Skan * work, we allocate a larger block and take a well-aligned cache 77115396Skan * line from it. 78115396Skan */ 79115396Skan base = xmalloc(CACHE_LINE_SIZE); 80115396Skan p = (char *)base; 81115396Skan if ((uintptr_t)p % CACHE_LINE_SIZE != 0) { 82115396Skan free(base); 83115396Skan base = xmalloc(2 * CACHE_LINE_SIZE); 84115396Skan p = (char *)base; 85115396Skan if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0) 86115396Skan p += CACHE_LINE_SIZE - r; 87115396Skan } 88115396Skan l = (Lock *)p; 89115396Skan l->base = base; 90115396Skan l->lock = 0; 91115396Skan return l; 92115396Skan} 93115396Skan 94115396Skanstatic void 95115396Skandef_lock_destroy(void *lock) 96115396Skan{ 97115396Skan Lock *l = (Lock *)lock; 98115396Skan 99115396Skan free(l->base); 100115396Skan} 101115396Skan 102115396Skanstatic void 103115396Skandef_rlock_acquire(void *lock) 104115396Skan{ 105115396Skan Lock *l = (Lock *)lock; 106115396Skan 107115396Skan atomic_add_acq_int(&l->lock, RC_INCR); 108115396Skan while (l->lock & WAFLAG) 109115396Skan ; /* Spin */ 110115396Skan} 111115396Skan 112115396Skanstatic void 113115396Skandef_wlock_acquire(void *lock) 114115396Skan{ 115115396Skan Lock *l = (Lock *)lock; 116115396Skan sigset_t tmp_oldsigmask; 117115396Skan 118115396Skan for ( ; ; ) { 119115396Skan sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); 120115396Skan if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) 121115396Skan break; 122115396Skan sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); 123115396Skan } 124115396Skan oldsigmask = tmp_oldsigmask; 125115396Skan} 126115396Skan 127115396Skanstatic void 128115396Skandef_lock_release(void *lock) 129115396Skan{ 130115396Skan Lock *l = (Lock *)lock; 131115396Skan 132115396Skan if ((l->lock & WAFLAG) == 0) 133115396Skan atomic_add_rel_int(&l->lock, -RC_INCR); 134115396Skan else { 135115396Skan atomic_add_rel_int(&l->lock, -WAFLAG); 136115396Skan sigprocmask(SIG_SETMASK, &oldsigmask, NULL); 137115396Skan } 138115396Skan} 139115396Skan 140115396Skanstatic int 141115396Skandef_thread_set_flag(int mask) 142115396Skan{ 143115396Skan int old_val = thread_flag; 144115396Skan thread_flag |= mask; 145115396Skan return (old_val); 146115396Skan} 147115396Skan 148115396Skanstatic int 149115396Skandef_thread_clr_flag(int mask) 150115396Skan{ 151115396Skan int old_val = thread_flag; 152115396Skan thread_flag &= ~mask; 153115396Skan return (old_val); 154115396Skan} 155115396Skan 156115396Skan/* 157115396Skan * Public interface exposed to the rest of the dynamic linker. 158115396Skan */ 159115396Skanstatic struct RtldLockInfo lockinfo; 160115396Skanstatic struct RtldLockInfo deflockinfo; 161115396Skan 162131575Sstefanfstatic __inline int 163115396Skanthread_mask_set(int mask) 164115396Skan{ 165115396Skan return lockinfo.thread_set_flag(mask); 166115396Skan} 167115396Skan 168131575Sstefanfstatic __inline void 169115396Skanthread_mask_clear(int mask) 170115396Skan{ 171115396Skan lockinfo.thread_clr_flag(mask); 172115396Skan} 173115396Skan 174178807Skib#define RTLD_LOCK_CNT 3 175115396Skanstruct rtld_lock { 176115396Skan void *handle; 177115396Skan int mask; 178115396Skan} rtld_locks[RTLD_LOCK_CNT]; 179115396Skan 180115396Skanrtld_lock_t rtld_bind_lock = &rtld_locks[0]; 181115396Skanrtld_lock_t rtld_libc_lock = &rtld_locks[1]; 182178807Skibrtld_lock_t rtld_phdr_lock = &rtld_locks[2]; 183115396Skan 184115396Skanint 185115396Skanrlock_acquire(rtld_lock_t lock) 186115396Skan{ 187183061Sdavidxu if (thread_mask_set(lock->mask) & lock->mask) { 188115396Skan dbg("rlock_acquire: recursed"); 189115396Skan return (0); 190115396Skan } 191115396Skan lockinfo.rlock_acquire(lock->handle); 192115396Skan return (1); 193115396Skan} 194115396Skan 195115396Skanint 196115396Skanwlock_acquire(rtld_lock_t lock) 197115396Skan{ 198183061Sdavidxu if (thread_mask_set(lock->mask) & lock->mask) { 199115396Skan dbg("wlock_acquire: recursed"); 200115396Skan return (0); 201115396Skan } 202115396Skan lockinfo.wlock_acquire(lock->handle); 203115396Skan return (1); 204115396Skan} 205115396Skan 206115396Skanvoid 207115396Skanrlock_release(rtld_lock_t lock, int locked) 208115396Skan{ 209115396Skan if (locked == 0) 210115396Skan return; 211115396Skan thread_mask_clear(lock->mask); 212115396Skan lockinfo.lock_release(lock->handle); 213115396Skan} 214115396Skan 215115396Skanvoid 216115396Skanwlock_release(rtld_lock_t lock, int locked) 217115396Skan{ 218115396Skan if (locked == 0) 219115396Skan return; 220115396Skan thread_mask_clear(lock->mask); 221115396Skan lockinfo.lock_release(lock->handle); 222115396Skan} 223115396Skan 224115396Skanvoid 225115396Skanlockdflt_init() 226115396Skan{ 227115396Skan int i; 228115396Skan 229115396Skan deflockinfo.rtli_version = RTLI_VERSION; 230115396Skan deflockinfo.lock_create = def_lock_create; 231115396Skan deflockinfo.lock_destroy = def_lock_destroy; 232115396Skan deflockinfo.rlock_acquire = def_rlock_acquire; 233115396Skan deflockinfo.wlock_acquire = def_wlock_acquire; 234115396Skan deflockinfo.lock_release = def_lock_release; 235115396Skan deflockinfo.thread_set_flag = def_thread_set_flag; 236115396Skan deflockinfo.thread_clr_flag = def_thread_clr_flag; 237115396Skan deflockinfo.at_fork = NULL; 238115396Skan 239115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 240115396Skan rtld_locks[i].mask = (1 << i); 241115396Skan rtld_locks[i].handle = NULL; 242115396Skan } 243115396Skan 244115396Skan memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); 245115396Skan _rtld_thread_init(NULL); 246115396Skan /* 247115396Skan * Construct a mask to block all signals except traps which might 248115396Skan * conceivably be generated within the dynamic linker itself. 249115396Skan */ 250115396Skan sigfillset(&fullsigmask); 251115396Skan sigdelset(&fullsigmask, SIGILL); 252115396Skan sigdelset(&fullsigmask, SIGTRAP); 253115396Skan sigdelset(&fullsigmask, SIGABRT); 254115396Skan sigdelset(&fullsigmask, SIGEMT); 255115396Skan sigdelset(&fullsigmask, SIGFPE); 256115396Skan sigdelset(&fullsigmask, SIGBUS); 257115396Skan sigdelset(&fullsigmask, SIGSEGV); 258115396Skan sigdelset(&fullsigmask, SIGSYS); 259115396Skan} 260115396Skan 261115396Skan/* 262115396Skan * Callback function to allow threads implementation to 263115396Skan * register their own locking primitives if the default 264115396Skan * one is not suitable. 265115396Skan * The current context should be the only context 266115396Skan * executing at the invocation time. 267115396Skan */ 268115396Skanvoid 269115396Skan_rtld_thread_init(struct RtldLockInfo *pli) 270115396Skan{ 271115396Skan int flags, i; 272115396Skan void *locks[RTLD_LOCK_CNT]; 273115396Skan 274115396Skan /* disable all locking while this function is running */ 275115396Skan flags = thread_mask_set(~0); 276115396Skan 277115396Skan if (pli == NULL) 278115396Skan pli = &deflockinfo; 279115396Skan 280115396Skan 281115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) 282115396Skan if ((locks[i] = pli->lock_create()) == NULL) 283115396Skan break; 284115396Skan 285115396Skan if (i < RTLD_LOCK_CNT) { 286115396Skan while (--i >= 0) 287115396Skan pli->lock_destroy(locks[i]); 288115396Skan abort(); 289115396Skan } 290115396Skan 291115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 292115396Skan if (rtld_locks[i].handle == NULL) 293115396Skan continue; 294115396Skan if (flags & rtld_locks[i].mask) 295115396Skan lockinfo.lock_release(rtld_locks[i].handle); 296115396Skan lockinfo.lock_destroy(rtld_locks[i].handle); 297115396Skan } 298115396Skan 299115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 300115396Skan rtld_locks[i].handle = locks[i]; 301115396Skan if (flags & rtld_locks[i].mask) 302115396Skan pli->wlock_acquire(rtld_locks[i].handle); 303115396Skan } 304115396Skan 305115396Skan lockinfo.lock_create = pli->lock_create; 306115396Skan lockinfo.lock_destroy = pli->lock_destroy; 307115396Skan lockinfo.rlock_acquire = pli->rlock_acquire; 308115396Skan lockinfo.wlock_acquire = pli->wlock_acquire; 309115396Skan lockinfo.lock_release = pli->lock_release; 310115396Skan lockinfo.thread_set_flag = pli->thread_set_flag; 311115396Skan lockinfo.thread_clr_flag = pli->thread_clr_flag; 312115396Skan lockinfo.at_fork = pli->at_fork; 313115396Skan 314115396Skan /* restore thread locking state, this time with new locks */ 315115396Skan thread_mask_clear(~0); 316115396Skan thread_mask_set(flags); 317115396Skan dbg("_rtld_thread_init: done"); 318115396Skan} 319