xref: /dragonfly/libexec/rtld-elf/rtld_lock.c (revision 98247283)
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 
50*98247283SMatthew Dillon #include <stdio.h>
51*98247283SMatthew Dillon #include <sys/file.h>
52*98247283SMatthew Dillon 
53fcf53d9bSJohn Marino #include "debug.h"
54fcf53d9bSJohn Marino #include "rtld.h"
55fcf53d9bSJohn Marino #include "rtld_machdep.h"
56fcf53d9bSJohn Marino 
57*98247283SMatthew Dillon extern pid_t __sys_getpid(void);
58*98247283SMatthew Dillon 
59fcf53d9bSJohn Marino #define WAFLAG		0x1	/* A writer holds the lock */
60fcf53d9bSJohn Marino #define RC_INCR		0x2	/* Adjusts count of readers desiring lock */
61fcf53d9bSJohn Marino 
62*98247283SMatthew Dillon struct Struct_Lock {
63fcf53d9bSJohn Marino 	volatile u_int lock;
64*98247283SMatthew Dillon 	int tid;		/* owner (exclusive) */
65*98247283SMatthew Dillon 	int count;		/* recursion (exclusive) */
66*98247283SMatthew Dillon 	sigset_t savesigmask;	/* first exclusive owner sets mask */
67*98247283SMatthew Dillon } __cachealign;
68fcf53d9bSJohn Marino 
69*98247283SMatthew Dillon #define cpu_ccfence()	__asm __volatile("" : : : "memory")
70fcf53d9bSJohn Marino 
71*98247283SMatthew Dillon static sigset_t fullsigmask;
72*98247283SMatthew Dillon 
73*98247283SMatthew Dillon struct Struct_Lock phdr_lock;
74*98247283SMatthew Dillon struct Struct_Lock bind_lock;
75*98247283SMatthew Dillon struct Struct_Lock libc_lock;
76*98247283SMatthew Dillon 
77*98247283SMatthew Dillon rtld_lock_t	rtld_phdr_lock = &phdr_lock;
78*98247283SMatthew Dillon rtld_lock_t	rtld_bind_lock = &bind_lock;
79*98247283SMatthew Dillon rtld_lock_t	rtld_libc_lock = &libc_lock;
80*98247283SMatthew Dillon 
81*98247283SMatthew Dillon void
82*98247283SMatthew Dillon rlock_acquire(rtld_lock_t lock, RtldLockState *state)
83fcf53d9bSJohn Marino {
84*98247283SMatthew Dillon 	int v;
85*98247283SMatthew Dillon 	int tid = 0;
86fcf53d9bSJohn Marino 
87*98247283SMatthew Dillon 	v = lock->lock;
88*98247283SMatthew Dillon 	cpu_ccfence();
89fcf53d9bSJohn Marino 	for (;;) {
90*98247283SMatthew Dillon 		if ((v & WAFLAG) == 0) {
91*98247283SMatthew Dillon 			if (atomic_fcmpset_int(&lock->lock, &v, v + RC_INCR)) {
92*98247283SMatthew Dillon 				state->lockstate = RTLD_LOCK_RLOCKED;
93fcf53d9bSJohn Marino 				break;
94*98247283SMatthew Dillon 			}
95*98247283SMatthew Dillon 		} else {
96*98247283SMatthew Dillon 			if (tid == 0)
97*98247283SMatthew Dillon 				tid = lwp_gettid();
98*98247283SMatthew Dillon 			if (lock->tid == tid) {
99*98247283SMatthew Dillon 				++lock->count;
100*98247283SMatthew Dillon 				state->lockstate = RTLD_LOCK_WLOCKED;
101*98247283SMatthew Dillon 				break;
102*98247283SMatthew Dillon 			}
103*98247283SMatthew Dillon 			umtx_sleep(&lock->lock, v, 0);
104*98247283SMatthew Dillon 			v = lock->lock;
105*98247283SMatthew Dillon 			cpu_ccfence();
106*98247283SMatthew Dillon 		}
107*98247283SMatthew Dillon 	}
108*98247283SMatthew Dillon }
109*98247283SMatthew Dillon 
110*98247283SMatthew Dillon void
111*98247283SMatthew Dillon wlock_acquire(rtld_lock_t lock, RtldLockState *state)
112*98247283SMatthew Dillon {
113*98247283SMatthew Dillon 	sigset_t tmp_oldsigmask;
114*98247283SMatthew Dillon 	int tid = lwp_gettid();
115*98247283SMatthew Dillon 
116*98247283SMatthew Dillon 	if (lock->tid == tid) {
117*98247283SMatthew Dillon 		++lock->count;
118*98247283SMatthew Dillon 		state->lockstate = RTLD_LOCK_WLOCKED;
119*98247283SMatthew Dillon 		return;
120*98247283SMatthew Dillon 	}
121*98247283SMatthew Dillon 
122*98247283SMatthew Dillon 	sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
123*98247283SMatthew Dillon 	for (;;) {
124*98247283SMatthew Dillon 		if (atomic_cmpset_acq_int(&lock->lock, 0, WAFLAG))
125*98247283SMatthew Dillon 			break;
126*98247283SMatthew Dillon 		umtx_sleep(&lock->lock, 0, 0);
127*98247283SMatthew Dillon 	}
128*98247283SMatthew Dillon 	lock->tid = tid;
129*98247283SMatthew Dillon 	lock->count = 1;
130*98247283SMatthew Dillon 	lock->savesigmask = tmp_oldsigmask;
131*98247283SMatthew Dillon 	state->lockstate = RTLD_LOCK_WLOCKED;
132*98247283SMatthew Dillon }
133*98247283SMatthew Dillon 
134*98247283SMatthew Dillon void
135*98247283SMatthew Dillon lock_release(rtld_lock_t lock, RtldLockState *state)
136*98247283SMatthew Dillon {
137*98247283SMatthew Dillon 	sigset_t tmp_oldsigmask;
138*98247283SMatthew Dillon 	int v;
139*98247283SMatthew Dillon 
140*98247283SMatthew Dillon 	if (state->lockstate == RTLD_LOCK_UNLOCKED)
141*98247283SMatthew Dillon 		return;
142*98247283SMatthew Dillon 	if ((lock->lock & WAFLAG) == 0) {
143*98247283SMatthew Dillon 		v = atomic_fetchadd_int(&lock->lock, -RC_INCR) - RC_INCR;
144*98247283SMatthew Dillon 		if (v == 0)
145*98247283SMatthew Dillon 			umtx_wakeup(&lock->lock, 0);
146*98247283SMatthew Dillon 	} else if (--lock->count == 0) {
147*98247283SMatthew Dillon 		tmp_oldsigmask = lock->savesigmask;
148*98247283SMatthew Dillon 		lock->tid = 0;
149*98247283SMatthew Dillon 		v = atomic_fetchadd_int(&lock->lock, -WAFLAG) - WAFLAG;
150*98247283SMatthew Dillon 		if (v == 0)
151*98247283SMatthew Dillon 			umtx_wakeup(&lock->lock, 0);
152fcf53d9bSJohn Marino 		sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
153fcf53d9bSJohn Marino 	}
154*98247283SMatthew Dillon 	state->lockstate = RTLD_LOCK_UNLOCKED;
155fcf53d9bSJohn Marino }
156fcf53d9bSJohn Marino 
157*98247283SMatthew Dillon static
158fcf53d9bSJohn Marino void
159*98247283SMatthew Dillon lock_reset(rtld_lock_t lock)
160fcf53d9bSJohn Marino {
161*98247283SMatthew Dillon 	memset(lock, 0, sizeof(*lock));
162fcf53d9bSJohn Marino }
163fcf53d9bSJohn Marino 
164fcf53d9bSJohn Marino void
165*98247283SMatthew Dillon lock_upgrade(rtld_lock_t lock, RtldLockState *state)
166fcf53d9bSJohn Marino {
167*98247283SMatthew Dillon 	if (state == NULL)
168fcf53d9bSJohn Marino 		return;
169*98247283SMatthew Dillon 	if (state->lockstate == RTLD_LOCK_RLOCKED) {
170*98247283SMatthew Dillon 		lock_release(lock, state);
171*98247283SMatthew Dillon 		wlock_acquire(lock, state);
172fcf53d9bSJohn Marino 	}
173fcf53d9bSJohn Marino }
174fcf53d9bSJohn Marino 
175fcf53d9bSJohn Marino void
176*98247283SMatthew Dillon lock_restart_for_upgrade(RtldLockState *state)
177fcf53d9bSJohn Marino {
178*98247283SMatthew Dillon 	if (state == NULL)
179fcf53d9bSJohn Marino 		return;
180*98247283SMatthew Dillon 	switch (state->lockstate) {
181fcf53d9bSJohn Marino 	case RTLD_LOCK_UNLOCKED:
182fcf53d9bSJohn Marino 	case RTLD_LOCK_WLOCKED:
183fcf53d9bSJohn Marino 		break;
184fcf53d9bSJohn Marino 	case RTLD_LOCK_RLOCKED:
185*98247283SMatthew Dillon 		siglongjmp(state->env, 1);
186fcf53d9bSJohn Marino 		break;
187fcf53d9bSJohn Marino 	default:
188fcf53d9bSJohn Marino 		assert(0);
189fcf53d9bSJohn Marino 	}
190fcf53d9bSJohn Marino }
191fcf53d9bSJohn Marino 
192fcf53d9bSJohn Marino void
193472de6d1SSascha Wildner lockdflt_init(void)
194fcf53d9bSJohn Marino {
195fcf53d9bSJohn Marino 	/*
196fcf53d9bSJohn Marino 	 * Construct a mask to block all signals except traps which might
197fcf53d9bSJohn Marino 	 * conceivably be generated within the dynamic linker itself.
198fcf53d9bSJohn Marino 	 */
199fcf53d9bSJohn Marino 	sigfillset(&fullsigmask);
200fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGILL);
201fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGTRAP);
202fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGABRT);
203fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGEMT);
204fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGFPE);
205fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGBUS);
206fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGSEGV);
207fcf53d9bSJohn Marino 	sigdelset(&fullsigmask, SIGSYS);
208*98247283SMatthew Dillon 
209*98247283SMatthew Dillon 	_rtld_thread_init(NULL);
210fcf53d9bSJohn Marino }
211fcf53d9bSJohn Marino 
212fcf53d9bSJohn Marino /*
213*98247283SMatthew Dillon  * (also called by pthreads)
214fcf53d9bSJohn Marino  */
215fcf53d9bSJohn Marino void
216*98247283SMatthew Dillon _rtld_thread_init(void *dummy __unused)
217fcf53d9bSJohn Marino {
218*98247283SMatthew Dillon 	lock_reset(rtld_phdr_lock);
219*98247283SMatthew Dillon 	lock_reset(rtld_bind_lock);
220*98247283SMatthew Dillon 	lock_reset(rtld_libc_lock);
221e19be507SMatthew Dillon }
222fcf53d9bSJohn Marino 
223*98247283SMatthew Dillon static RtldLockState fork_states[3];
224e19be507SMatthew Dillon 
225e19be507SMatthew Dillon void
226e19be507SMatthew Dillon _rtld_thread_prefork(void)
227e19be507SMatthew Dillon {
228*98247283SMatthew Dillon 	wlock_acquire(rtld_phdr_lock, &fork_states[0]);
229*98247283SMatthew Dillon 	wlock_acquire(rtld_bind_lock, &fork_states[1]);
230*98247283SMatthew Dillon 	wlock_acquire(rtld_libc_lock, &fork_states[2]);
231e19be507SMatthew Dillon }
232e19be507SMatthew Dillon 
233e19be507SMatthew Dillon void
234e19be507SMatthew Dillon _rtld_thread_postfork(void)
235e19be507SMatthew Dillon {
236*98247283SMatthew Dillon 	lock_release(rtld_libc_lock, &fork_states[2]);
237*98247283SMatthew Dillon 	lock_release(rtld_bind_lock, &fork_states[1]);
238*98247283SMatthew Dillon 	lock_release(rtld_phdr_lock, &fork_states[0]);
239e19be507SMatthew Dillon }
240e19be507SMatthew Dillon 
241e19be507SMatthew Dillon void
242e19be507SMatthew Dillon _rtld_thread_childfork(void)
243e19be507SMatthew Dillon {
244*98247283SMatthew Dillon 	sigset_t tmp_oldsigmask;
245*98247283SMatthew Dillon 
246*98247283SMatthew Dillon 	lock_reset(rtld_phdr_lock);
247*98247283SMatthew Dillon 	lock_reset(rtld_bind_lock);
248*98247283SMatthew Dillon 	tmp_oldsigmask = rtld_libc_lock->savesigmask;
249*98247283SMatthew Dillon 	lock_reset(rtld_libc_lock);
250*98247283SMatthew Dillon 	sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
251e19be507SMatthew Dillon }
252