]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/rtld-elf/rtld_lock.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / libexec / rtld-elf / rtld_lock.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 1999, 2000 John D. Polstra.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  *      from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09
28  * $FreeBSD$
29  */
30
31 /*
32  * Thread locking implementation for the dynamic linker.
33  *
34  * We use the "simple, non-scalable reader-preference lock" from:
35  *
36  *   J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
37  *   Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
38  *   Principles and Practice of Parallel Programming, April 1991.
39  *
40  * In this algorithm the lock is a single word.  Its low-order bit is
41  * set when a writer holds the lock.  The remaining high-order bits
42  * contain a count of readers desiring the lock.  The algorithm requires
43  * atomic "compare_and_store" and "add" operations, which we take
44  * from machine/atomic.h.
45  */
46
47 #include <sys/param.h>
48 #include <signal.h>
49 #include <stdlib.h>
50 #include <time.h>
51
52 #include "debug.h"
53 #include "rtld.h"
54 #include "rtld_machdep.h"
55 #include "rtld_libc.h"
56
57 void _rtld_thread_init(struct RtldLockInfo *) __exported;
58 void _rtld_atfork_pre(int *) __exported;
59 void _rtld_atfork_post(int *) __exported;
60
61 #define WAFLAG          0x1     /* A writer holds the lock */
62 #define RC_INCR         0x2     /* Adjusts count of readers desiring lock */
63
64 typedef struct Struct_Lock {
65         volatile u_int lock;
66         void *base;
67 } Lock;
68
69 static sigset_t fullsigmask, oldsigmask;
70 static int thread_flag, wnested;
71
72 static void *
73 def_lock_create(void)
74 {
75     void *base;
76     char *p;
77     uintptr_t r;
78     Lock *l;
79
80     /*
81      * Arrange for the lock to occupy its own cache line.  First, we
82      * optimistically allocate just a cache line, hoping that malloc
83      * will give us a well-aligned block of memory.  If that doesn't
84      * work, we allocate a larger block and take a well-aligned cache
85      * line from it.
86      */
87     base = xmalloc(CACHE_LINE_SIZE);
88     p = (char *)base;
89     if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
90         free(base);
91         base = xmalloc(2 * CACHE_LINE_SIZE);
92         p = (char *)base;
93         if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
94             p += CACHE_LINE_SIZE - r;
95     }
96     l = (Lock *)p;
97     l->base = base;
98     l->lock = 0;
99     return l;
100 }
101
102 static void
103 def_lock_destroy(void *lock)
104 {
105     Lock *l = (Lock *)lock;
106
107     free(l->base);
108 }
109
110 static void
111 def_rlock_acquire(void *lock)
112 {
113     Lock *l = (Lock *)lock;
114
115     atomic_add_acq_int(&l->lock, RC_INCR);
116     while (l->lock & WAFLAG)
117             ;   /* Spin */
118 }
119
120 static void
121 def_wlock_acquire(void *lock)
122 {
123         Lock *l;
124         sigset_t tmp_oldsigmask;
125
126         l = (Lock *)lock;
127         for (;;) {
128                 sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
129                 if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG))
130                         break;
131                 sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
132         }
133         if (atomic_fetchadd_int(&wnested, 1) == 0)
134                 oldsigmask = tmp_oldsigmask;
135 }
136
137 static void
138 def_lock_release(void *lock)
139 {
140         Lock *l;
141
142         l = (Lock *)lock;
143         if ((l->lock & WAFLAG) == 0)
144                 atomic_add_rel_int(&l->lock, -RC_INCR);
145         else {
146                 assert(wnested > 0);
147                 atomic_add_rel_int(&l->lock, -WAFLAG);
148                 if (atomic_fetchadd_int(&wnested, -1) == 1)
149                         sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
150         }
151 }
152
153 static int
154 def_thread_set_flag(int mask)
155 {
156         int old_val = thread_flag;
157         thread_flag |= mask;
158         return (old_val);
159 }
160
161 static int
162 def_thread_clr_flag(int mask)
163 {
164         int old_val = thread_flag;
165         thread_flag &= ~mask;
166         return (old_val);
167 }
168
169 /*
170  * Public interface exposed to the rest of the dynamic linker.
171  */
172 static struct RtldLockInfo lockinfo;
173 static struct RtldLockInfo deflockinfo;
174
175 static __inline int
176 thread_mask_set(int mask)
177 {
178         return lockinfo.thread_set_flag(mask);
179 }
180
181 static __inline void
182 thread_mask_clear(int mask)
183 {
184         lockinfo.thread_clr_flag(mask);
185 }
186
187 #define RTLD_LOCK_CNT   3
188 static struct rtld_lock {
189         void    *handle;
190         int      mask;
191 } rtld_locks[RTLD_LOCK_CNT];
192
193 rtld_lock_t     rtld_bind_lock = &rtld_locks[0];
194 rtld_lock_t     rtld_libc_lock = &rtld_locks[1];
195 rtld_lock_t     rtld_phdr_lock = &rtld_locks[2];
196
197 void
198 rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
199 {
200
201         if (lockstate == NULL)
202                 return;
203
204         if (thread_mask_set(lock->mask) & lock->mask) {
205                 dbg("rlock_acquire: recursed");
206                 lockstate->lockstate = RTLD_LOCK_UNLOCKED;
207                 return;
208         }
209         lockinfo.rlock_acquire(lock->handle);
210         lockstate->lockstate = RTLD_LOCK_RLOCKED;
211 }
212
213 void
214 wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
215 {
216
217         if (lockstate == NULL)
218                 return;
219
220         if (thread_mask_set(lock->mask) & lock->mask) {
221                 dbg("wlock_acquire: recursed");
222                 lockstate->lockstate = RTLD_LOCK_UNLOCKED;
223                 return;
224         }
225         lockinfo.wlock_acquire(lock->handle);
226         lockstate->lockstate = RTLD_LOCK_WLOCKED;
227 }
228
229 void
230 lock_release(rtld_lock_t lock, RtldLockState *lockstate)
231 {
232
233         if (lockstate == NULL)
234                 return;
235
236         switch (lockstate->lockstate) {
237         case RTLD_LOCK_UNLOCKED:
238                 break;
239         case RTLD_LOCK_RLOCKED:
240         case RTLD_LOCK_WLOCKED:
241                 thread_mask_clear(lock->mask);
242                 lockinfo.lock_release(lock->handle);
243                 break;
244         default:
245                 assert(0);
246         }
247 }
248
249 void
250 lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate)
251 {
252
253         if (lockstate == NULL)
254                 return;
255
256         lock_release(lock, lockstate);
257         wlock_acquire(lock, lockstate);
258 }
259
260 void
261 lock_restart_for_upgrade(RtldLockState *lockstate)
262 {
263
264         if (lockstate == NULL)
265                 return;
266
267         switch (lockstate->lockstate) {
268         case RTLD_LOCK_UNLOCKED:
269         case RTLD_LOCK_WLOCKED:
270                 break;
271         case RTLD_LOCK_RLOCKED:
272                 siglongjmp(lockstate->env, 1);
273                 break;
274         default:
275                 assert(0);
276         }
277 }
278
279 void
280 lockdflt_init(void)
281 {
282     int i;
283
284     deflockinfo.rtli_version  = RTLI_VERSION;
285     deflockinfo.lock_create   = def_lock_create;
286     deflockinfo.lock_destroy  = def_lock_destroy;
287     deflockinfo.rlock_acquire = def_rlock_acquire;
288     deflockinfo.wlock_acquire = def_wlock_acquire;
289     deflockinfo.lock_release  = def_lock_release;
290     deflockinfo.thread_set_flag = def_thread_set_flag;
291     deflockinfo.thread_clr_flag = def_thread_clr_flag;
292     deflockinfo.at_fork = NULL;
293
294     for (i = 0; i < RTLD_LOCK_CNT; i++) {
295             rtld_locks[i].mask   = (1 << i);
296             rtld_locks[i].handle = NULL;
297     }
298
299     memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo));
300     _rtld_thread_init(NULL);
301     /*
302      * Construct a mask to block all signals except traps which might
303      * conceivably be generated within the dynamic linker itself.
304      */
305     sigfillset(&fullsigmask);
306     sigdelset(&fullsigmask, SIGILL);
307     sigdelset(&fullsigmask, SIGTRAP);
308     sigdelset(&fullsigmask, SIGABRT);
309     sigdelset(&fullsigmask, SIGEMT);
310     sigdelset(&fullsigmask, SIGFPE);
311     sigdelset(&fullsigmask, SIGBUS);
312     sigdelset(&fullsigmask, SIGSEGV);
313     sigdelset(&fullsigmask, SIGSYS);
314 }
315
316 /*
317  * Callback function to allow threads implementation to
318  * register their own locking primitives if the default
319  * one is not suitable.
320  * The current context should be the only context
321  * executing at the invocation time.
322  */
323 void
324 _rtld_thread_init(struct RtldLockInfo *pli)
325 {
326         int flags, i;
327         void *locks[RTLD_LOCK_CNT];
328
329         /* disable all locking while this function is running */
330         flags = thread_mask_set(~0);
331
332         if (pli == NULL)
333                 pli = &deflockinfo;
334
335
336         for (i = 0; i < RTLD_LOCK_CNT; i++)
337                 if ((locks[i] = pli->lock_create()) == NULL)
338                         break;
339
340         if (i < RTLD_LOCK_CNT) {
341                 while (--i >= 0)
342                         pli->lock_destroy(locks[i]);
343                 abort();
344         }
345
346         for (i = 0; i < RTLD_LOCK_CNT; i++) {
347                 if (rtld_locks[i].handle == NULL)
348                         continue;
349                 if (flags & rtld_locks[i].mask)
350                         lockinfo.lock_release(rtld_locks[i].handle);
351                 lockinfo.lock_destroy(rtld_locks[i].handle);
352         }
353
354         for (i = 0; i < RTLD_LOCK_CNT; i++) {
355                 rtld_locks[i].handle = locks[i];
356                 if (flags & rtld_locks[i].mask)
357                         pli->wlock_acquire(rtld_locks[i].handle);
358         }
359
360         lockinfo.lock_create = pli->lock_create;
361         lockinfo.lock_destroy = pli->lock_destroy;
362         lockinfo.rlock_acquire = pli->rlock_acquire;
363         lockinfo.wlock_acquire = pli->wlock_acquire;
364         lockinfo.lock_release  = pli->lock_release;
365         lockinfo.thread_set_flag = pli->thread_set_flag;
366         lockinfo.thread_clr_flag = pli->thread_clr_flag;
367         lockinfo.at_fork = pli->at_fork;
368
369         /* restore thread locking state, this time with new locks */
370         thread_mask_clear(~0);
371         thread_mask_set(flags);
372         dbg("_rtld_thread_init: done");
373 }
374
375 void
376 _rtld_atfork_pre(int *locks)
377 {
378         RtldLockState ls[2];
379
380         if (locks == NULL)
381                 return;
382
383         /*
384          * Warning: this did not worked well with the rtld compat
385          * locks above, when the thread signal mask was corrupted (set
386          * to all signals blocked) if two locks were taken
387          * simultaneously in the write mode.  The caller of the
388          * _rtld_atfork_pre() must provide the working implementation
389          * of the locks anyway, and libthr locks are fine.
390          */
391         wlock_acquire(rtld_phdr_lock, &ls[0]);
392         wlock_acquire(rtld_bind_lock, &ls[1]);
393
394         /* XXXKIB: I am really sorry for this. */
395         locks[0] = ls[1].lockstate;
396         locks[2] = ls[0].lockstate;
397 }
398
399 void
400 _rtld_atfork_post(int *locks)
401 {
402         RtldLockState ls[2];
403
404         if (locks == NULL)
405                 return;
406
407         bzero(ls, sizeof(ls));
408         ls[0].lockstate = locks[2];
409         ls[1].lockstate = locks[0];
410         lock_release(rtld_bind_lock, &ls[1]);
411         lock_release(rtld_phdr_lock, &ls[0]);
412 }