xref: /freebsd/lib/libthr/thread/thr_rtld.c (revision 04a57d2c838813acef28df70c18cfb63f452653a)
1b6b894f6SDavid Xu /*
2b6b894f6SDavid Xu  * Copyright (c) 2006, David Xu <davidxu@freebsd.org>
3b6b894f6SDavid Xu  * All rights reserved.
4b6b894f6SDavid Xu  *
5b6b894f6SDavid Xu  * Redistribution and use in source and binary forms, with or without
6b6b894f6SDavid Xu  * modification, are permitted provided that the following conditions
7b6b894f6SDavid Xu  * are met:
8b6b894f6SDavid Xu  * 1. Redistributions of source code must retain the above copyright
9b6b894f6SDavid Xu  *    notice unmodified, this list of conditions, and the following
10b6b894f6SDavid Xu  *    disclaimer.
11b6b894f6SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
12b6b894f6SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
13b6b894f6SDavid Xu  *    documentation and/or other materials provided with the distribution.
14b6b894f6SDavid Xu  *
15b6b894f6SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16b6b894f6SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17b6b894f6SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18b6b894f6SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19b6b894f6SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20b6b894f6SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21b6b894f6SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22b6b894f6SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23b6b894f6SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24b6b894f6SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25b6b894f6SDavid Xu  *
26b6b894f6SDavid Xu  * $FreeBSD$
27b6b894f6SDavid Xu  *
28b6b894f6SDavid Xu  */
29b6b894f6SDavid Xu 
30b6b894f6SDavid Xu  /*
31b6b894f6SDavid Xu   * A lockless rwlock for rtld.
32b6b894f6SDavid Xu   */
33b6b894f6SDavid Xu #include <sys/cdefs.h>
34b6b894f6SDavid Xu #include <stdlib.h>
35b6b894f6SDavid Xu 
36b6b894f6SDavid Xu #include "rtld_lock.h"
37b6b894f6SDavid Xu #include "thr_private.h"
38b6b894f6SDavid Xu 
398c38215fSDavid Xu #undef errno
408c38215fSDavid Xu extern int errno;
418c38215fSDavid Xu 
42b6b894f6SDavid Xu #define CACHE_LINE_SIZE		64
43b6b894f6SDavid Xu #define WAFLAG			0x1
44b6b894f6SDavid Xu #define RC_INCR			0x2
45b6b894f6SDavid Xu 
46b6b894f6SDavid Xu static int	_thr_rtld_clr_flag(int);
47b6b894f6SDavid Xu static void	*_thr_rtld_lock_create(void);
48b6b894f6SDavid Xu static void	_thr_rtld_lock_destroy(void *);
49b6b894f6SDavid Xu static void	_thr_rtld_lock_release(void *);
50b6b894f6SDavid Xu static void	_thr_rtld_rlock_acquire(void *);
51b6b894f6SDavid Xu static int	_thr_rtld_set_flag(int);
52b6b894f6SDavid Xu static void	_thr_rtld_wlock_acquire(void *);
53b6b894f6SDavid Xu 
54b6b894f6SDavid Xu struct rtld_lock {
55b6b894f6SDavid Xu 	volatile int		lock;
56b6b894f6SDavid Xu 	volatile int		rd_waiters;
57b6b894f6SDavid Xu 	volatile int		wr_waiters;
586fdfcacbSDavid Xu 	volatile long		rd_cv;
596fdfcacbSDavid Xu 	volatile long		wr_cv;
60b6b894f6SDavid Xu 	void			*base;
61b6b894f6SDavid Xu };
62b6b894f6SDavid Xu 
63b6b894f6SDavid Xu static void *
64b6b894f6SDavid Xu _thr_rtld_lock_create(void)
65b6b894f6SDavid Xu {
66b6b894f6SDavid Xu 	void			*base;
67b6b894f6SDavid Xu 	char			*p;
68b6b894f6SDavid Xu 	uintptr_t		r;
69b6b894f6SDavid Xu 	struct rtld_lock	*l;
70b6b894f6SDavid Xu 
71b6b894f6SDavid Xu 	THR_ASSERT(sizeof(struct rtld_lock) <= CACHE_LINE_SIZE,
72b6b894f6SDavid Xu 		"rtld_lock too large");
73b6b894f6SDavid Xu 	base = calloc(1, CACHE_LINE_SIZE);
74b6b894f6SDavid Xu 	p = (char *)base;
75b6b894f6SDavid Xu 	if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
76b6b894f6SDavid Xu 		free(base);
77b6b894f6SDavid Xu 		base = calloc(1, 2 * CACHE_LINE_SIZE);
78b6b894f6SDavid Xu 		p = (char *)base;
79b6b894f6SDavid Xu 		if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
80b6b894f6SDavid Xu 			p += CACHE_LINE_SIZE - r;
81b6b894f6SDavid Xu 	}
82b6b894f6SDavid Xu 	l = (struct rtld_lock *)p;
83b6b894f6SDavid Xu 	l->base = base;
84b6b894f6SDavid Xu 	return (l);
85b6b894f6SDavid Xu }
86b6b894f6SDavid Xu 
87b6b894f6SDavid Xu static void
88b6b894f6SDavid Xu _thr_rtld_lock_destroy(void *lock)
89b6b894f6SDavid Xu {
90b6b894f6SDavid Xu 	struct rtld_lock *l = (struct rtld_lock *)lock;
91b6b894f6SDavid Xu 	free(l->base);
92b6b894f6SDavid Xu }
93b6b894f6SDavid Xu 
948c38215fSDavid Xu #define SAVE_ERRNO()	{			\
958c38215fSDavid Xu 	if (curthread != _thr_initial)		\
968c38215fSDavid Xu 		errsave = curthread->error;	\
978c38215fSDavid Xu 	else					\
988c38215fSDavid Xu 		errsave = errno;		\
998c38215fSDavid Xu }
1008c38215fSDavid Xu 
1018c38215fSDavid Xu #define RESTORE_ERRNO()	{ 			\
1028c38215fSDavid Xu 	if (curthread != _thr_initial)  	\
1038c38215fSDavid Xu 		curthread->error = errsave;	\
1048c38215fSDavid Xu 	else					\
1058c38215fSDavid Xu 		errno = errsave;		\
1068c38215fSDavid Xu }
1078c38215fSDavid Xu 
108b6b894f6SDavid Xu static void
109b6b894f6SDavid Xu _thr_rtld_rlock_acquire(void *lock)
110b6b894f6SDavid Xu {
111b6b894f6SDavid Xu 	struct pthread		*curthread;
112b6b894f6SDavid Xu 	struct rtld_lock	*l;
1136fdfcacbSDavid Xu 	long			v;
1148c38215fSDavid Xu 	int			errsave;
115b6b894f6SDavid Xu 
116b6b894f6SDavid Xu 	curthread = _get_curthread();
1178c38215fSDavid Xu 	SAVE_ERRNO();
118b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
119b6b894f6SDavid Xu 
120b6b894f6SDavid Xu 	THR_CRITICAL_ENTER(curthread);
121b6b894f6SDavid Xu 	atomic_add_acq_int(&l->lock, RC_INCR);
1228c38215fSDavid Xu 	if (!(l->lock & WAFLAG)) {
1238c38215fSDavid Xu 		RESTORE_ERRNO();
124b6b894f6SDavid Xu 		return;
1258c38215fSDavid Xu 	}
126b6b894f6SDavid Xu 	v = l->rd_cv;
127b6b894f6SDavid Xu 	atomic_add_int(&l->rd_waiters, 1);
128b6b894f6SDavid Xu 	while (l->lock & WAFLAG) {
129b6b894f6SDavid Xu 		_thr_umtx_wait(&l->rd_cv, v, NULL);
130b6b894f6SDavid Xu 		v = l->rd_cv;
131b6b894f6SDavid Xu 	}
132b6b894f6SDavid Xu 	atomic_add_int(&l->rd_waiters, -1);
1338c38215fSDavid Xu 	RESTORE_ERRNO();
134b6b894f6SDavid Xu }
135b6b894f6SDavid Xu 
136b6b894f6SDavid Xu static void
137b6b894f6SDavid Xu _thr_rtld_wlock_acquire(void *lock)
138b6b894f6SDavid Xu {
139b6b894f6SDavid Xu 	struct pthread		*curthread;
140b6b894f6SDavid Xu 	struct rtld_lock	*l;
1416fdfcacbSDavid Xu 	long			v;
1428c38215fSDavid Xu 	int			errsave;
143b6b894f6SDavid Xu 
144b6b894f6SDavid Xu 	curthread = _get_curthread();
1458c38215fSDavid Xu 	SAVE_ERRNO();
146b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
147b6b894f6SDavid Xu 
148b6b894f6SDavid Xu 	_thr_signal_block(curthread);
149b6b894f6SDavid Xu 	for (;;) {
1508c38215fSDavid Xu 		if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) {
1518c38215fSDavid Xu 			RESTORE_ERRNO();
152b6b894f6SDavid Xu 			return;
1538c38215fSDavid Xu 		}
154b6b894f6SDavid Xu 		v = l->wr_cv;
155b6b894f6SDavid Xu 		atomic_add_int(&l->wr_waiters, 1);
156b6b894f6SDavid Xu 		while (l->lock != 0) {
157b6b894f6SDavid Xu 			_thr_umtx_wait(&l->wr_cv, v, NULL);
158b6b894f6SDavid Xu 			v = l->wr_cv;
159b6b894f6SDavid Xu 		}
160b6b894f6SDavid Xu 		atomic_add_int(&l->wr_waiters, -1);
161b6b894f6SDavid Xu 	}
162b6b894f6SDavid Xu }
163b6b894f6SDavid Xu 
164b6b894f6SDavid Xu static void
165b6b894f6SDavid Xu _thr_rtld_lock_release(void *lock)
166b6b894f6SDavid Xu {
167b6b894f6SDavid Xu 	struct pthread		*curthread;
168b6b894f6SDavid Xu 	struct rtld_lock	*l;
1698c38215fSDavid Xu 	int			errsave;
170b6b894f6SDavid Xu 
171b6b894f6SDavid Xu 	curthread = _get_curthread();
1728c38215fSDavid Xu 	SAVE_ERRNO();
173b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
174b6b894f6SDavid Xu 
175b6b894f6SDavid Xu 	if ((l->lock & WAFLAG) == 0) {
176b6b894f6SDavid Xu 		atomic_add_rel_int(&l->lock, -RC_INCR);
177f656ae70SDavid Xu 		if (l->lock == 0 && l->wr_waiters) {
178b6b894f6SDavid Xu 			atomic_add_long(&l->wr_cv, 1);
179b6b894f6SDavid Xu 			_thr_umtx_wake(&l->wr_cv, l->wr_waiters);
180b6b894f6SDavid Xu 		}
181b6b894f6SDavid Xu 		THR_CRITICAL_LEAVE(curthread);
182b6b894f6SDavid Xu 	} else {
183b6b894f6SDavid Xu 		atomic_add_rel_int(&l->lock, -WAFLAG);
184f656ae70SDavid Xu 		if (l->lock == 0 && l->wr_waiters) {
185b6b894f6SDavid Xu 			atomic_add_long(&l->wr_cv, 1);
186b6b894f6SDavid Xu 			_thr_umtx_wake(&l->wr_cv, l->wr_waiters);
187b6b894f6SDavid Xu 		} else if (l->rd_waiters) {
188b6b894f6SDavid Xu 			atomic_add_long(&l->rd_cv, 1);
189b6b894f6SDavid Xu 			_thr_umtx_wake(&l->rd_cv, l->rd_waiters);
190b6b894f6SDavid Xu 		}
191b6b894f6SDavid Xu 		_thr_signal_unblock(curthread);
192b6b894f6SDavid Xu 	}
1938c38215fSDavid Xu 	RESTORE_ERRNO();
194b6b894f6SDavid Xu }
195b6b894f6SDavid Xu 
196b6b894f6SDavid Xu static int
19737a6356bSDavid Xu _thr_rtld_set_flag(int mask __unused)
198b6b894f6SDavid Xu {
199b6b894f6SDavid Xu 	/*
200b6b894f6SDavid Xu 	 * The caller's code in rtld-elf is broken, it is not signal safe,
201b6b894f6SDavid Xu 	 * just return zero to fool it.
202b6b894f6SDavid Xu 	 */
203b6b894f6SDavid Xu 	return (0);
204b6b894f6SDavid Xu }
205b6b894f6SDavid Xu 
206b6b894f6SDavid Xu static int
20737a6356bSDavid Xu _thr_rtld_clr_flag(int mask __unused)
208b6b894f6SDavid Xu {
209b6b894f6SDavid Xu 	return (0);
210b6b894f6SDavid Xu }
211b6b894f6SDavid Xu 
212b6b894f6SDavid Xu void
213b6b894f6SDavid Xu _thr_rtld_init(void)
214b6b894f6SDavid Xu {
215b6b894f6SDavid Xu 	struct RtldLockInfo	li;
216b6b894f6SDavid Xu 	struct pthread		*curthread;
2176fdfcacbSDavid Xu 	long dummy;
218b6b894f6SDavid Xu 
219b6b894f6SDavid Xu 	curthread = _get_curthread();
220b6b894f6SDavid Xu 
221b6b894f6SDavid Xu 	/* force to resolve _umtx_op PLT */
222b6b894f6SDavid Xu 	_umtx_op((struct umtx *)&dummy, UMTX_OP_WAKE, 1, 0, 0);
22304a57d2cSDavid Xu 
22404a57d2cSDavid Xu 	/* force to resolve errno() PLT */
22504a57d2cSDavid Xu 	__error();
226b6b894f6SDavid Xu 
227b6b894f6SDavid Xu 	li.lock_create  = _thr_rtld_lock_create;
228b6b894f6SDavid Xu 	li.lock_destroy = _thr_rtld_lock_destroy;
229b6b894f6SDavid Xu 	li.rlock_acquire = _thr_rtld_rlock_acquire;
230b6b894f6SDavid Xu 	li.wlock_acquire = _thr_rtld_wlock_acquire;
231b6b894f6SDavid Xu 	li.lock_release  = _thr_rtld_lock_release;
232b6b894f6SDavid Xu 	li.thread_set_flag = _thr_rtld_set_flag;
233b6b894f6SDavid Xu 	li.thread_clr_flag = _thr_rtld_clr_flag;
234b6b894f6SDavid Xu 	li.at_fork = NULL;
235b6b894f6SDavid Xu 
236b6b894f6SDavid Xu 	/* mask signals, also force to resolve __sys_sigprocmask PLT */
237b6b894f6SDavid Xu 	_thr_signal_block(curthread);
238b6b894f6SDavid Xu 	_rtld_thread_init(&li);
239b6b894f6SDavid Xu 	_thr_signal_unblock(curthread);
240b6b894f6SDavid Xu }
241b6b894f6SDavid Xu 
242b6b894f6SDavid Xu void
243b6b894f6SDavid Xu _thr_rtld_fini(void)
244b6b894f6SDavid Xu {
245b6b894f6SDavid Xu 	struct pthread	*curthread;
246b6b894f6SDavid Xu 
247b6b894f6SDavid Xu 	curthread = _get_curthread();
248b6b894f6SDavid Xu 	_thr_signal_block(curthread);
249b6b894f6SDavid Xu 	_rtld_thread_init(NULL);
250b6b894f6SDavid Xu 	_thr_signal_unblock(curthread);
251b6b894f6SDavid Xu }
252