xref: /freebsd/lib/libthr/thread/thr_rtld.c (revision 1c70d00733741b90a62c44b0a5cea6a1971b65b6)
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>
34da2fcff7SKonstantin Belousov #include <sys/mman.h>
35*1c70d007SKonstantin Belousov #include <sys/syscall.h>
36da2fcff7SKonstantin Belousov #include <link.h>
37b6b894f6SDavid Xu #include <stdlib.h>
3802c3c858SDavid Xu #include <string.h>
39b6b894f6SDavid Xu 
40*1c70d007SKonstantin Belousov #include "libc_private.h"
41b6b894f6SDavid Xu #include "rtld_lock.h"
42b6b894f6SDavid Xu #include "thr_private.h"
43b6b894f6SDavid Xu 
448c38215fSDavid Xu #undef errno
458c38215fSDavid Xu extern int errno;
468c38215fSDavid Xu 
47b6b894f6SDavid Xu static int	_thr_rtld_clr_flag(int);
48b6b894f6SDavid Xu static void	*_thr_rtld_lock_create(void);
49b6b894f6SDavid Xu static void	_thr_rtld_lock_destroy(void *);
50b6b894f6SDavid Xu static void	_thr_rtld_lock_release(void *);
51b6b894f6SDavid Xu static void	_thr_rtld_rlock_acquire(void *);
52b6b894f6SDavid Xu static int	_thr_rtld_set_flag(int);
53b6b894f6SDavid Xu static void	_thr_rtld_wlock_acquire(void *);
54b6b894f6SDavid Xu 
55b6b894f6SDavid Xu struct rtld_lock {
56fb2641d9SDavid Xu 	struct	urwlock	lock;
5710b40346SKonstantin Belousov 	char		_pad[CACHE_LINE_SIZE - sizeof(struct urwlock)];
58b6b894f6SDavid Xu };
59b6b894f6SDavid Xu 
6010b40346SKonstantin Belousov static struct rtld_lock lock_place[MAX_RTLD_LOCKS] __aligned(CACHE_LINE_SIZE);
6110b40346SKonstantin Belousov static int busy_places;
6210b40346SKonstantin Belousov 
63b6b894f6SDavid Xu static void *
64b6b894f6SDavid Xu _thr_rtld_lock_create(void)
65b6b894f6SDavid Xu {
6610b40346SKonstantin Belousov 	int locki;
67b6b894f6SDavid Xu 	struct rtld_lock *l;
6810b40346SKonstantin Belousov 	static const char fail[] = "_thr_rtld_lock_create failed\n";
69b6b894f6SDavid Xu 
7010b40346SKonstantin Belousov 	for (locki = 0; locki < MAX_RTLD_LOCKS; locki++) {
7110b40346SKonstantin Belousov 		if ((busy_places & (1 << locki)) == 0)
7210b40346SKonstantin Belousov 			break;
73b6b894f6SDavid Xu 	}
7410b40346SKonstantin Belousov 	if (locki == MAX_RTLD_LOCKS) {
7510b40346SKonstantin Belousov 		write(2, fail, sizeof(fail) - 1);
7610b40346SKonstantin Belousov 		return (NULL);
7710b40346SKonstantin Belousov 	}
7810b40346SKonstantin Belousov 	busy_places |= (1 << locki);
7910b40346SKonstantin Belousov 
8010b40346SKonstantin Belousov 	l = &lock_place[locki];
81fb2641d9SDavid Xu 	l->lock.rw_flags = URWLOCK_PREFER_READER;
82b6b894f6SDavid Xu 	return (l);
83b6b894f6SDavid Xu }
84b6b894f6SDavid Xu 
85b6b894f6SDavid Xu static void
86b6b894f6SDavid Xu _thr_rtld_lock_destroy(void *lock)
87b6b894f6SDavid Xu {
8810b40346SKonstantin Belousov 	int locki;
898e60ce99SDavid Xu 	size_t i;
9010b40346SKonstantin Belousov 
9110b40346SKonstantin Belousov 	locki = (struct rtld_lock *)lock - &lock_place[0];
928e60ce99SDavid Xu 	for (i = 0; i < sizeof(struct rtld_lock); ++i)
938e60ce99SDavid Xu 		((char *)lock)[i] = 0;
9410b40346SKonstantin Belousov 	busy_places &= ~(1 << locki);
95b6b894f6SDavid Xu }
96b6b894f6SDavid Xu 
978c38215fSDavid Xu #define SAVE_ERRNO()	{			\
988c38215fSDavid Xu 	if (curthread != _thr_initial)		\
998c38215fSDavid Xu 		errsave = curthread->error;	\
1008c38215fSDavid Xu 	else					\
1018c38215fSDavid Xu 		errsave = errno;		\
1028c38215fSDavid Xu }
1038c38215fSDavid Xu 
1048c38215fSDavid Xu #define RESTORE_ERRNO()	{ 			\
1058c38215fSDavid Xu 	if (curthread != _thr_initial)  	\
1068c38215fSDavid Xu 		curthread->error = errsave;	\
1078c38215fSDavid Xu 	else					\
1088c38215fSDavid Xu 		errno = errsave;		\
1098c38215fSDavid Xu }
1108c38215fSDavid Xu 
111b6b894f6SDavid Xu static void
112b6b894f6SDavid Xu _thr_rtld_rlock_acquire(void *lock)
113b6b894f6SDavid Xu {
114b6b894f6SDavid Xu 	struct pthread		*curthread;
115b6b894f6SDavid Xu 	struct rtld_lock	*l;
1168c38215fSDavid Xu 	int			errsave;
117b6b894f6SDavid Xu 
118b6b894f6SDavid Xu 	curthread = _get_curthread();
1198c38215fSDavid Xu 	SAVE_ERRNO();
120b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
121b6b894f6SDavid Xu 
122b6b894f6SDavid Xu 	THR_CRITICAL_ENTER(curthread);
123fb2641d9SDavid Xu 	while (_thr_rwlock_rdlock(&l->lock, 0, NULL) != 0)
124fb2641d9SDavid Xu 		;
125137ae5d2SAttilio Rao 	curthread->rdlock_count++;
1268c38215fSDavid Xu 	RESTORE_ERRNO();
127b6b894f6SDavid Xu }
128b6b894f6SDavid Xu 
129b6b894f6SDavid Xu static void
130b6b894f6SDavid Xu _thr_rtld_wlock_acquire(void *lock)
131b6b894f6SDavid Xu {
132b6b894f6SDavid Xu 	struct pthread		*curthread;
133b6b894f6SDavid Xu 	struct rtld_lock	*l;
1348c38215fSDavid Xu 	int			errsave;
135b6b894f6SDavid Xu 
136b6b894f6SDavid Xu 	curthread = _get_curthread();
1378c38215fSDavid Xu 	SAVE_ERRNO();
138b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
139b6b894f6SDavid Xu 
14002c3c858SDavid Xu 	THR_CRITICAL_ENTER(curthread);
141fb2641d9SDavid Xu 	while (_thr_rwlock_wrlock(&l->lock, NULL) != 0)
142fb2641d9SDavid Xu 		;
1438c38215fSDavid Xu 	RESTORE_ERRNO();
144b6b894f6SDavid Xu }
145b6b894f6SDavid Xu 
146b6b894f6SDavid Xu static void
147b6b894f6SDavid Xu _thr_rtld_lock_release(void *lock)
148b6b894f6SDavid Xu {
149b6b894f6SDavid Xu 	struct pthread		*curthread;
150b6b894f6SDavid Xu 	struct rtld_lock	*l;
151fb2641d9SDavid Xu 	int32_t			state;
1528c38215fSDavid Xu 	int			errsave;
153b6b894f6SDavid Xu 
154b6b894f6SDavid Xu 	curthread = _get_curthread();
1558c38215fSDavid Xu 	SAVE_ERRNO();
156b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
157b6b894f6SDavid Xu 
158fb2641d9SDavid Xu 	state = l->lock.rw_state;
159fb2641d9SDavid Xu 	if (_thr_rwlock_unlock(&l->lock) == 0) {
16002c3c858SDavid Xu 		if ((state & URWLOCK_WRITE_OWNER) == 0)
1616b932ecaSDavid Xu 			curthread->rdlock_count--;
162b6b894f6SDavid Xu 		THR_CRITICAL_LEAVE(curthread);
163fb2641d9SDavid Xu 	}
1648c38215fSDavid Xu 	RESTORE_ERRNO();
165b6b894f6SDavid Xu }
166b6b894f6SDavid Xu 
167b6b894f6SDavid Xu static int
16837a6356bSDavid Xu _thr_rtld_set_flag(int mask __unused)
169b6b894f6SDavid Xu {
170b6b894f6SDavid Xu 	/*
171b6b894f6SDavid Xu 	 * The caller's code in rtld-elf is broken, it is not signal safe,
172b6b894f6SDavid Xu 	 * just return zero to fool it.
173b6b894f6SDavid Xu 	 */
174b6b894f6SDavid Xu 	return (0);
175b6b894f6SDavid Xu }
176b6b894f6SDavid Xu 
177b6b894f6SDavid Xu static int
17837a6356bSDavid Xu _thr_rtld_clr_flag(int mask __unused)
179b6b894f6SDavid Xu {
180b6b894f6SDavid Xu 	return (0);
181b6b894f6SDavid Xu }
182b6b894f6SDavid Xu 
183b6b894f6SDavid Xu void
184b6b894f6SDavid Xu _thr_rtld_init(void)
185b6b894f6SDavid Xu {
186b6b894f6SDavid Xu 	struct RtldLockInfo	li;
187b6b894f6SDavid Xu 	struct pthread		*curthread;
18870ba1e8fSPeter Wemm 	long dummy = -1;
189b6b894f6SDavid Xu 
190b6b894f6SDavid Xu 	curthread = _get_curthread();
191b6b894f6SDavid Xu 
192b6b894f6SDavid Xu 	/* force to resolve _umtx_op PLT */
193d6e0eb0aSDavid Xu 	_umtx_op_err((struct umtx *)&dummy, UMTX_OP_WAKE, 1, 0, 0);
19404a57d2cSDavid Xu 
19504a57d2cSDavid Xu 	/* force to resolve errno() PLT */
19604a57d2cSDavid Xu 	__error();
197b6b894f6SDavid Xu 
19802c3c858SDavid Xu 	/* force to resolve memcpy PLT */
19902c3c858SDavid Xu 	memcpy(&dummy, &dummy, sizeof(dummy));
20002c3c858SDavid Xu 
201da2fcff7SKonstantin Belousov 	mprotect(NULL, 0, 0);
202da2fcff7SKonstantin Belousov 	_rtld_get_stack_prot();
203da2fcff7SKonstantin Belousov 
204b6b894f6SDavid Xu 	li.lock_create  = _thr_rtld_lock_create;
205b6b894f6SDavid Xu 	li.lock_destroy = _thr_rtld_lock_destroy;
206b6b894f6SDavid Xu 	li.rlock_acquire = _thr_rtld_rlock_acquire;
207b6b894f6SDavid Xu 	li.wlock_acquire = _thr_rtld_wlock_acquire;
208b6b894f6SDavid Xu 	li.lock_release  = _thr_rtld_lock_release;
209b6b894f6SDavid Xu 	li.thread_set_flag = _thr_rtld_set_flag;
210b6b894f6SDavid Xu 	li.thread_clr_flag = _thr_rtld_clr_flag;
211b6b894f6SDavid Xu 	li.at_fork = NULL;
212b6b894f6SDavid Xu 
213*1c70d007SKonstantin Belousov 	/*
214*1c70d007SKonstantin Belousov 	 * Preresolve the symbols needed for the fork interposer.  We
215*1c70d007SKonstantin Belousov 	 * call _rtld_atfork_pre() and _rtld_atfork_post() with NULL
216*1c70d007SKonstantin Belousov 	 * argument to indicate that no actual locking inside the
217*1c70d007SKonstantin Belousov 	 * functions should happen.  Neither rtld compat locks nor
218*1c70d007SKonstantin Belousov 	 * libthr rtld locks cannot work there:
219*1c70d007SKonstantin Belousov 	 * - compat locks do not handle the case of two locks taken
220*1c70d007SKonstantin Belousov 	 *   in write mode (the signal mask for the thread is corrupted);
221*1c70d007SKonstantin Belousov 	 * - libthr locks would work, but locked rtld_bind_lock prevents
222*1c70d007SKonstantin Belousov 	 *   symbol resolution for _rtld_atfork_post.
223*1c70d007SKonstantin Belousov 	 */
224*1c70d007SKonstantin Belousov 	_rtld_atfork_pre(NULL);
225*1c70d007SKonstantin Belousov 	_rtld_atfork_post(NULL);
226*1c70d007SKonstantin Belousov 	_malloc_prefork();
227*1c70d007SKonstantin Belousov 	_malloc_postfork();
228*1c70d007SKonstantin Belousov 	syscall(SYS_getpid);
229*1c70d007SKonstantin Belousov 
230b6b894f6SDavid Xu 	/* mask signals, also force to resolve __sys_sigprocmask PLT */
231b6b894f6SDavid Xu 	_thr_signal_block(curthread);
232b6b894f6SDavid Xu 	_rtld_thread_init(&li);
233b6b894f6SDavid Xu 	_thr_signal_unblock(curthread);
234b6b894f6SDavid Xu }
235