rtld_lock.c revision 191303
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 191303 2009-04-20 10:35:50Z rwatson $ 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 45191303Srwatson#include <sys/param.h> 46191292Srwatson 47115396Skan#include <signal.h> 48115396Skan#include <stdlib.h> 49115396Skan#include <time.h> 50115396Skan 51115396Skan#include "debug.h" 52115396Skan#include "rtld.h" 53115396Skan#include "rtld_machdep.h" 54115396Skan 55115396Skan#define WAFLAG 0x1 /* A writer holds the lock */ 56115396Skan#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ 57115396Skan 58115396Skantypedef struct Struct_Lock { 59168311Skan volatile u_int lock; 60115396Skan void *base; 61115396Skan} Lock; 62115396Skan 63115396Skanstatic sigset_t fullsigmask, oldsigmask; 64115396Skanstatic int thread_flag; 65115396Skan 66115396Skanstatic void * 67115396Skandef_lock_create() 68115396Skan{ 69115396Skan void *base; 70115396Skan char *p; 71115396Skan uintptr_t r; 72115396Skan Lock *l; 73115396Skan 74115396Skan /* 75115396Skan * Arrange for the lock to occupy its own cache line. First, we 76115396Skan * optimistically allocate just a cache line, hoping that malloc 77115396Skan * will give us a well-aligned block of memory. If that doesn't 78115396Skan * work, we allocate a larger block and take a well-aligned cache 79115396Skan * line from it. 80115396Skan */ 81115396Skan base = xmalloc(CACHE_LINE_SIZE); 82115396Skan p = (char *)base; 83115396Skan if ((uintptr_t)p % CACHE_LINE_SIZE != 0) { 84115396Skan free(base); 85115396Skan base = xmalloc(2 * CACHE_LINE_SIZE); 86115396Skan p = (char *)base; 87115396Skan if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0) 88115396Skan p += CACHE_LINE_SIZE - r; 89115396Skan } 90115396Skan l = (Lock *)p; 91115396Skan l->base = base; 92115396Skan l->lock = 0; 93115396Skan return l; 94115396Skan} 95115396Skan 96115396Skanstatic void 97115396Skandef_lock_destroy(void *lock) 98115396Skan{ 99115396Skan Lock *l = (Lock *)lock; 100115396Skan 101115396Skan free(l->base); 102115396Skan} 103115396Skan 104115396Skanstatic void 105115396Skandef_rlock_acquire(void *lock) 106115396Skan{ 107115396Skan Lock *l = (Lock *)lock; 108115396Skan 109115396Skan atomic_add_acq_int(&l->lock, RC_INCR); 110115396Skan while (l->lock & WAFLAG) 111115396Skan ; /* Spin */ 112115396Skan} 113115396Skan 114115396Skanstatic void 115115396Skandef_wlock_acquire(void *lock) 116115396Skan{ 117115396Skan Lock *l = (Lock *)lock; 118115396Skan sigset_t tmp_oldsigmask; 119115396Skan 120115396Skan for ( ; ; ) { 121115396Skan sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); 122115396Skan if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) 123115396Skan break; 124115396Skan sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); 125115396Skan } 126115396Skan oldsigmask = tmp_oldsigmask; 127115396Skan} 128115396Skan 129115396Skanstatic void 130115396Skandef_lock_release(void *lock) 131115396Skan{ 132115396Skan Lock *l = (Lock *)lock; 133115396Skan 134115396Skan if ((l->lock & WAFLAG) == 0) 135115396Skan atomic_add_rel_int(&l->lock, -RC_INCR); 136115396Skan else { 137115396Skan atomic_add_rel_int(&l->lock, -WAFLAG); 138115396Skan sigprocmask(SIG_SETMASK, &oldsigmask, NULL); 139115396Skan } 140115396Skan} 141115396Skan 142115396Skanstatic int 143115396Skandef_thread_set_flag(int mask) 144115396Skan{ 145115396Skan int old_val = thread_flag; 146115396Skan thread_flag |= mask; 147115396Skan return (old_val); 148115396Skan} 149115396Skan 150115396Skanstatic int 151115396Skandef_thread_clr_flag(int mask) 152115396Skan{ 153115396Skan int old_val = thread_flag; 154115396Skan thread_flag &= ~mask; 155115396Skan return (old_val); 156115396Skan} 157115396Skan 158115396Skan/* 159115396Skan * Public interface exposed to the rest of the dynamic linker. 160115396Skan */ 161115396Skanstatic struct RtldLockInfo lockinfo; 162115396Skanstatic struct RtldLockInfo deflockinfo; 163115396Skan 164131575Sstefanfstatic __inline int 165115396Skanthread_mask_set(int mask) 166115396Skan{ 167115396Skan return lockinfo.thread_set_flag(mask); 168115396Skan} 169115396Skan 170131575Sstefanfstatic __inline void 171115396Skanthread_mask_clear(int mask) 172115396Skan{ 173115396Skan lockinfo.thread_clr_flag(mask); 174115396Skan} 175115396Skan 176178807Skib#define RTLD_LOCK_CNT 3 177115396Skanstruct rtld_lock { 178115396Skan void *handle; 179115396Skan int mask; 180115396Skan} rtld_locks[RTLD_LOCK_CNT]; 181115396Skan 182115396Skanrtld_lock_t rtld_bind_lock = &rtld_locks[0]; 183115396Skanrtld_lock_t rtld_libc_lock = &rtld_locks[1]; 184178807Skibrtld_lock_t rtld_phdr_lock = &rtld_locks[2]; 185115396Skan 186115396Skanint 187115396Skanrlock_acquire(rtld_lock_t lock) 188115396Skan{ 189183061Sdavidxu if (thread_mask_set(lock->mask) & lock->mask) { 190115396Skan dbg("rlock_acquire: recursed"); 191115396Skan return (0); 192115396Skan } 193115396Skan lockinfo.rlock_acquire(lock->handle); 194115396Skan return (1); 195115396Skan} 196115396Skan 197115396Skanint 198115396Skanwlock_acquire(rtld_lock_t lock) 199115396Skan{ 200183061Sdavidxu if (thread_mask_set(lock->mask) & lock->mask) { 201115396Skan dbg("wlock_acquire: recursed"); 202115396Skan return (0); 203115396Skan } 204115396Skan lockinfo.wlock_acquire(lock->handle); 205115396Skan return (1); 206115396Skan} 207115396Skan 208115396Skanvoid 209115396Skanrlock_release(rtld_lock_t lock, int locked) 210115396Skan{ 211115396Skan if (locked == 0) 212115396Skan return; 213115396Skan thread_mask_clear(lock->mask); 214115396Skan lockinfo.lock_release(lock->handle); 215115396Skan} 216115396Skan 217115396Skanvoid 218115396Skanwlock_release(rtld_lock_t lock, int locked) 219115396Skan{ 220115396Skan if (locked == 0) 221115396Skan return; 222115396Skan thread_mask_clear(lock->mask); 223115396Skan lockinfo.lock_release(lock->handle); 224115396Skan} 225115396Skan 226115396Skanvoid 227115396Skanlockdflt_init() 228115396Skan{ 229115396Skan int i; 230115396Skan 231115396Skan deflockinfo.rtli_version = RTLI_VERSION; 232115396Skan deflockinfo.lock_create = def_lock_create; 233115396Skan deflockinfo.lock_destroy = def_lock_destroy; 234115396Skan deflockinfo.rlock_acquire = def_rlock_acquire; 235115396Skan deflockinfo.wlock_acquire = def_wlock_acquire; 236115396Skan deflockinfo.lock_release = def_lock_release; 237115396Skan deflockinfo.thread_set_flag = def_thread_set_flag; 238115396Skan deflockinfo.thread_clr_flag = def_thread_clr_flag; 239115396Skan deflockinfo.at_fork = NULL; 240115396Skan 241115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 242115396Skan rtld_locks[i].mask = (1 << i); 243115396Skan rtld_locks[i].handle = NULL; 244115396Skan } 245115396Skan 246115396Skan memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); 247115396Skan _rtld_thread_init(NULL); 248115396Skan /* 249115396Skan * Construct a mask to block all signals except traps which might 250115396Skan * conceivably be generated within the dynamic linker itself. 251115396Skan */ 252115396Skan sigfillset(&fullsigmask); 253115396Skan sigdelset(&fullsigmask, SIGILL); 254115396Skan sigdelset(&fullsigmask, SIGTRAP); 255115396Skan sigdelset(&fullsigmask, SIGABRT); 256115396Skan sigdelset(&fullsigmask, SIGEMT); 257115396Skan sigdelset(&fullsigmask, SIGFPE); 258115396Skan sigdelset(&fullsigmask, SIGBUS); 259115396Skan sigdelset(&fullsigmask, SIGSEGV); 260115396Skan sigdelset(&fullsigmask, SIGSYS); 261115396Skan} 262115396Skan 263115396Skan/* 264115396Skan * Callback function to allow threads implementation to 265115396Skan * register their own locking primitives if the default 266115396Skan * one is not suitable. 267115396Skan * The current context should be the only context 268115396Skan * executing at the invocation time. 269115396Skan */ 270115396Skanvoid 271115396Skan_rtld_thread_init(struct RtldLockInfo *pli) 272115396Skan{ 273115396Skan int flags, i; 274115396Skan void *locks[RTLD_LOCK_CNT]; 275115396Skan 276115396Skan /* disable all locking while this function is running */ 277115396Skan flags = thread_mask_set(~0); 278115396Skan 279115396Skan if (pli == NULL) 280115396Skan pli = &deflockinfo; 281115396Skan 282115396Skan 283115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) 284115396Skan if ((locks[i] = pli->lock_create()) == NULL) 285115396Skan break; 286115396Skan 287115396Skan if (i < RTLD_LOCK_CNT) { 288115396Skan while (--i >= 0) 289115396Skan pli->lock_destroy(locks[i]); 290115396Skan abort(); 291115396Skan } 292115396Skan 293115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 294115396Skan if (rtld_locks[i].handle == NULL) 295115396Skan continue; 296115396Skan if (flags & rtld_locks[i].mask) 297115396Skan lockinfo.lock_release(rtld_locks[i].handle); 298115396Skan lockinfo.lock_destroy(rtld_locks[i].handle); 299115396Skan } 300115396Skan 301115396Skan for (i = 0; i < RTLD_LOCK_CNT; i++) { 302115396Skan rtld_locks[i].handle = locks[i]; 303115396Skan if (flags & rtld_locks[i].mask) 304115396Skan pli->wlock_acquire(rtld_locks[i].handle); 305115396Skan } 306115396Skan 307115396Skan lockinfo.lock_create = pli->lock_create; 308115396Skan lockinfo.lock_destroy = pli->lock_destroy; 309115396Skan lockinfo.rlock_acquire = pli->rlock_acquire; 310115396Skan lockinfo.wlock_acquire = pli->wlock_acquire; 311115396Skan lockinfo.lock_release = pli->lock_release; 312115396Skan lockinfo.thread_set_flag = pli->thread_set_flag; 313115396Skan lockinfo.thread_clr_flag = pli->thread_clr_flag; 314115396Skan lockinfo.at_fork = pli->at_fork; 315115396Skan 316115396Skan /* restore thread locking state, this time with new locks */ 317115396Skan thread_mask_clear(~0); 318115396Skan thread_mask_set(flags); 319115396Skan dbg("_rtld_thread_init: done"); 320115396Skan} 321185369Skib 322185369Skibvoid 323185369Skib_rtld_atfork_pre(int *locks) 324185369Skib{ 325185369Skib 326185369Skib locks[2] = wlock_acquire(rtld_phdr_lock); 327185369Skib locks[0] = rlock_acquire(rtld_bind_lock); 328185369Skib} 329185369Skib 330185369Skibvoid 331185369Skib_rtld_atfork_post(int *locks) 332185369Skib{ 333185369Skib 334185369Skib rlock_release(rtld_bind_lock, locks[0]); 335185369Skib wlock_release(rtld_phdr_lock, locks[2]); 336185369Skib} 337