xref: /freebsd/lib/libthr/thread/thr_rtld.c (revision f7f9a033f3ec755c8f1657103ba083294eb155d4)
15e53a4f9SPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
4b6b894f6SDavid Xu  * Copyright (c) 2006, David Xu <davidxu@freebsd.org>
5b6b894f6SDavid Xu  * All rights reserved.
6b6b894f6SDavid Xu  *
7b6b894f6SDavid Xu  * Redistribution and use in source and binary forms, with or without
8b6b894f6SDavid Xu  * modification, are permitted provided that the following conditions
9b6b894f6SDavid Xu  * are met:
10b6b894f6SDavid Xu  * 1. Redistributions of source code must retain the above copyright
11b6b894f6SDavid Xu  *    notice unmodified, this list of conditions, and the following
12b6b894f6SDavid Xu  *    disclaimer.
13b6b894f6SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
14b6b894f6SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
15b6b894f6SDavid Xu  *    documentation and/or other materials provided with the distribution.
16b6b894f6SDavid Xu  *
17b6b894f6SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18b6b894f6SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19b6b894f6SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20b6b894f6SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21b6b894f6SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22b6b894f6SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23b6b894f6SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24b6b894f6SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25b6b894f6SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26b6b894f6SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27b6b894f6SDavid Xu  */
28b6b894f6SDavid Xu 
29b6b894f6SDavid Xu  /*
30b6b894f6SDavid Xu   * A lockless rwlock for rtld.
31b6b894f6SDavid Xu   */
32da2fcff7SKonstantin Belousov #include <sys/mman.h>
331c70d007SKonstantin Belousov #include <sys/syscall.h>
34da2fcff7SKonstantin Belousov #include <link.h>
35b6b894f6SDavid Xu #include <stdlib.h>
3602c3c858SDavid Xu #include <string.h>
37b6b894f6SDavid Xu 
381c70d007SKonstantin Belousov #include "libc_private.h"
39b6b894f6SDavid Xu #include "rtld_lock.h"
40b6b894f6SDavid Xu #include "thr_private.h"
41b6b894f6SDavid Xu 
428c38215fSDavid Xu #undef errno
438c38215fSDavid Xu extern int errno;
448c38215fSDavid Xu 
45b6b894f6SDavid Xu static int	_thr_rtld_clr_flag(int);
46b6b894f6SDavid Xu static void	*_thr_rtld_lock_create(void);
47b6b894f6SDavid Xu static void	_thr_rtld_lock_destroy(void *);
48b6b894f6SDavid Xu static void	_thr_rtld_lock_release(void *);
49b6b894f6SDavid Xu static void	_thr_rtld_rlock_acquire(void *);
50b6b894f6SDavid Xu static int	_thr_rtld_set_flag(int);
51b6b894f6SDavid Xu static void	_thr_rtld_wlock_acquire(void *);
52b6b894f6SDavid Xu 
53b6b894f6SDavid Xu struct rtld_lock {
54fb2641d9SDavid Xu 	struct	urwlock	lock;
5510b40346SKonstantin Belousov 	char		_pad[CACHE_LINE_SIZE - sizeof(struct urwlock)];
56b6b894f6SDavid Xu };
57b6b894f6SDavid Xu 
5810b40346SKonstantin Belousov static struct rtld_lock lock_place[MAX_RTLD_LOCKS] __aligned(CACHE_LINE_SIZE);
5910b40346SKonstantin Belousov static int busy_places;
6010b40346SKonstantin Belousov 
61b6b894f6SDavid Xu static void *
62b6b894f6SDavid Xu _thr_rtld_lock_create(void)
63b6b894f6SDavid Xu {
6410b40346SKonstantin Belousov 	int locki;
65b6b894f6SDavid Xu 	struct rtld_lock *l;
6610b40346SKonstantin Belousov 	static const char fail[] = "_thr_rtld_lock_create failed\n";
67b6b894f6SDavid Xu 
6810b40346SKonstantin Belousov 	for (locki = 0; locki < MAX_RTLD_LOCKS; locki++) {
6910b40346SKonstantin Belousov 		if ((busy_places & (1 << locki)) == 0)
7010b40346SKonstantin Belousov 			break;
71b6b894f6SDavid Xu 	}
7210b40346SKonstantin Belousov 	if (locki == MAX_RTLD_LOCKS) {
7310b40346SKonstantin Belousov 		write(2, fail, sizeof(fail) - 1);
7410b40346SKonstantin Belousov 		return (NULL);
7510b40346SKonstantin Belousov 	}
7610b40346SKonstantin Belousov 	busy_places |= (1 << locki);
7710b40346SKonstantin Belousov 
7810b40346SKonstantin Belousov 	l = &lock_place[locki];
79fb2641d9SDavid Xu 	l->lock.rw_flags = URWLOCK_PREFER_READER;
80b6b894f6SDavid Xu 	return (l);
81b6b894f6SDavid Xu }
82b6b894f6SDavid Xu 
83b6b894f6SDavid Xu static void
84b6b894f6SDavid Xu _thr_rtld_lock_destroy(void *lock)
85b6b894f6SDavid Xu {
8610b40346SKonstantin Belousov 	int locki;
878e60ce99SDavid Xu 	size_t i;
8810b40346SKonstantin Belousov 
8910b40346SKonstantin Belousov 	locki = (struct rtld_lock *)lock - &lock_place[0];
908e60ce99SDavid Xu 	for (i = 0; i < sizeof(struct rtld_lock); ++i)
918e60ce99SDavid Xu 		((char *)lock)[i] = 0;
9210b40346SKonstantin Belousov 	busy_places &= ~(1 << locki);
93b6b894f6SDavid Xu }
94b6b894f6SDavid Xu 
958c38215fSDavid Xu #define SAVE_ERRNO()	{			\
968c38215fSDavid Xu 	if (curthread != _thr_initial)		\
978c38215fSDavid Xu 		errsave = curthread->error;	\
988c38215fSDavid Xu 	else					\
998c38215fSDavid Xu 		errsave = errno;		\
1008c38215fSDavid Xu }
1018c38215fSDavid Xu 
1028c38215fSDavid Xu #define RESTORE_ERRNO()	{ 			\
1038c38215fSDavid Xu 	if (curthread != _thr_initial)  	\
1048c38215fSDavid Xu 		curthread->error = errsave;	\
1058c38215fSDavid Xu 	else					\
1068c38215fSDavid Xu 		errno = errsave;		\
1078c38215fSDavid Xu }
1088c38215fSDavid Xu 
109b6b894f6SDavid Xu static void
110b6b894f6SDavid Xu _thr_rtld_rlock_acquire(void *lock)
111b6b894f6SDavid Xu {
112b6b894f6SDavid Xu 	struct pthread		*curthread;
113b6b894f6SDavid Xu 	struct rtld_lock	*l;
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);
121fb2641d9SDavid Xu 	while (_thr_rwlock_rdlock(&l->lock, 0, NULL) != 0)
122fb2641d9SDavid Xu 		;
123137ae5d2SAttilio Rao 	curthread->rdlock_count++;
1248c38215fSDavid Xu 	RESTORE_ERRNO();
125b6b894f6SDavid Xu }
126b6b894f6SDavid Xu 
127b6b894f6SDavid Xu static void
128b6b894f6SDavid Xu _thr_rtld_wlock_acquire(void *lock)
129b6b894f6SDavid Xu {
130b6b894f6SDavid Xu 	struct pthread		*curthread;
131b6b894f6SDavid Xu 	struct rtld_lock	*l;
1328c38215fSDavid Xu 	int			errsave;
133b6b894f6SDavid Xu 
134b6b894f6SDavid Xu 	curthread = _get_curthread();
1358c38215fSDavid Xu 	SAVE_ERRNO();
136b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
137b6b894f6SDavid Xu 
13802c3c858SDavid Xu 	THR_CRITICAL_ENTER(curthread);
139fb2641d9SDavid Xu 	while (_thr_rwlock_wrlock(&l->lock, NULL) != 0)
140fb2641d9SDavid Xu 		;
1418c38215fSDavid Xu 	RESTORE_ERRNO();
142b6b894f6SDavid Xu }
143b6b894f6SDavid Xu 
144b6b894f6SDavid Xu static void
145b6b894f6SDavid Xu _thr_rtld_lock_release(void *lock)
146b6b894f6SDavid Xu {
147b6b894f6SDavid Xu 	struct pthread		*curthread;
148b6b894f6SDavid Xu 	struct rtld_lock	*l;
149fb2641d9SDavid Xu 	int32_t			state;
1508c38215fSDavid Xu 	int			errsave;
151b6b894f6SDavid Xu 
152b6b894f6SDavid Xu 	curthread = _get_curthread();
1538c38215fSDavid Xu 	SAVE_ERRNO();
154b6b894f6SDavid Xu 	l = (struct rtld_lock *)lock;
155b6b894f6SDavid Xu 
156fb2641d9SDavid Xu 	state = l->lock.rw_state;
1576f49eafbSKonstantin Belousov 	if (__predict_false(_thr_after_fork)) {
1586f49eafbSKonstantin Belousov 		/*
1596f49eafbSKonstantin Belousov 		 * After fork, only this thread is running, there is no
1606f49eafbSKonstantin Belousov 		 * waiters.  Keeping waiters recorded in rwlock breaks
1616f49eafbSKonstantin Belousov 		 * wake logic.
1626f49eafbSKonstantin Belousov 		 */
1636f49eafbSKonstantin Belousov 		atomic_clear_int(&l->lock.rw_state,
1646f49eafbSKonstantin Belousov 		    URWLOCK_WRITE_WAITERS | URWLOCK_READ_WAITERS);
1656f49eafbSKonstantin Belousov 		l->lock.rw_blocked_readers = 0;
1666f49eafbSKonstantin Belousov 		l->lock.rw_blocked_writers = 0;
1676f49eafbSKonstantin Belousov 	}
168fb2641d9SDavid Xu 	if (_thr_rwlock_unlock(&l->lock) == 0) {
16902c3c858SDavid Xu 		if ((state & URWLOCK_WRITE_OWNER) == 0)
1706b932ecaSDavid Xu 			curthread->rdlock_count--;
171b6b894f6SDavid Xu 		THR_CRITICAL_LEAVE(curthread);
172fb2641d9SDavid Xu 	}
1738c38215fSDavid Xu 	RESTORE_ERRNO();
174b6b894f6SDavid Xu }
175b6b894f6SDavid Xu 
176b6b894f6SDavid Xu static int
17737a6356bSDavid Xu _thr_rtld_set_flag(int mask __unused)
178b6b894f6SDavid Xu {
179b6b894f6SDavid Xu 	/*
180b6b894f6SDavid Xu 	 * The caller's code in rtld-elf is broken, it is not signal safe,
181b6b894f6SDavid Xu 	 * just return zero to fool it.
182b6b894f6SDavid Xu 	 */
183b6b894f6SDavid Xu 	return (0);
184b6b894f6SDavid Xu }
185b6b894f6SDavid Xu 
186b6b894f6SDavid Xu static int
18737a6356bSDavid Xu _thr_rtld_clr_flag(int mask __unused)
188b6b894f6SDavid Xu {
189b6b894f6SDavid Xu 	return (0);
190b6b894f6SDavid Xu }
191b6b894f6SDavid Xu 
19208bfbd43SKonstantin Belousov /*
19308bfbd43SKonstantin Belousov  * ABI bug workaround: This symbol must be present for rtld to accept
19408bfbd43SKonstantin Belousov  * RTLI_VERSION from RtldLockInfo
19508bfbd43SKonstantin Belousov  */
19608bfbd43SKonstantin Belousov extern char _pli_rtli_version;
19708bfbd43SKonstantin Belousov char _pli_rtli_version;
19808bfbd43SKonstantin Belousov 
1994d9128daSKonstantin Belousov static char *
2004d9128daSKonstantin Belousov _thr_dlerror_loc(void)
2014d9128daSKonstantin Belousov {
2024d9128daSKonstantin Belousov 	struct pthread *curthread;
2034d9128daSKonstantin Belousov 
2044d9128daSKonstantin Belousov 	curthread = _get_curthread();
2054d9128daSKonstantin Belousov 	return (curthread->dlerror_msg);
2064d9128daSKonstantin Belousov }
2074d9128daSKonstantin Belousov 
2084d9128daSKonstantin Belousov static int *
2094d9128daSKonstantin Belousov _thr_dlerror_seen(void)
2104d9128daSKonstantin Belousov {
2114d9128daSKonstantin Belousov 	struct pthread *curthread;
2124d9128daSKonstantin Belousov 
2134d9128daSKonstantin Belousov 	curthread = _get_curthread();
2144d9128daSKonstantin Belousov 	return (&curthread->dlerror_seen);
2154d9128daSKonstantin Belousov }
2164d9128daSKonstantin Belousov 
217b6b894f6SDavid Xu void
218b6b894f6SDavid Xu _thr_rtld_init(void)
219b6b894f6SDavid Xu {
220b6b894f6SDavid Xu 	struct RtldLockInfo	li;
221b6b894f6SDavid Xu 	struct pthread		*curthread;
2223d40192dSKonstantin Belousov 	ucontext_t *uc;
2233d40192dSKonstantin Belousov 	int uc_len;
224*f7f9a033SRyan Libby 	char dummy[2] = {};
225b6b894f6SDavid Xu 
226b6b894f6SDavid Xu 	curthread = _get_curthread();
227b6b894f6SDavid Xu 
228b6b894f6SDavid Xu 	/* force to resolve _umtx_op PLT */
229*f7f9a033SRyan Libby 	_umtx_op_err(&dummy, UMTX_OP_WAKE, 1, 0, 0);
23004a57d2cSDavid Xu 
23104a57d2cSDavid Xu 	/* force to resolve errno() PLT */
23204a57d2cSDavid Xu 	__error();
233b6b894f6SDavid Xu 
23402c3c858SDavid Xu 	/* force to resolve memcpy PLT */
235*f7f9a033SRyan Libby 	memcpy(&dummy[0], &dummy[1], 1);
23602c3c858SDavid Xu 
237da2fcff7SKonstantin Belousov 	mprotect(NULL, 0, 0);
238da2fcff7SKonstantin Belousov 	_rtld_get_stack_prot();
23936f0a34cSMark Johnston 	thr_wake(-1);
240da2fcff7SKonstantin Belousov 
24108bfbd43SKonstantin Belousov 	li.rtli_version = RTLI_VERSION;
242b6b894f6SDavid Xu 	li.lock_create  = _thr_rtld_lock_create;
243b6b894f6SDavid Xu 	li.lock_destroy = _thr_rtld_lock_destroy;
244b6b894f6SDavid Xu 	li.rlock_acquire = _thr_rtld_rlock_acquire;
245b6b894f6SDavid Xu 	li.wlock_acquire = _thr_rtld_wlock_acquire;
246b6b894f6SDavid Xu 	li.lock_release  = _thr_rtld_lock_release;
247b6b894f6SDavid Xu 	li.thread_set_flag = _thr_rtld_set_flag;
248b6b894f6SDavid Xu 	li.thread_clr_flag = _thr_rtld_clr_flag;
249b6b894f6SDavid Xu 	li.at_fork = NULL;
2504d9128daSKonstantin Belousov 	li.dlerror_loc = _thr_dlerror_loc;
2514d9128daSKonstantin Belousov 	li.dlerror_loc_sz = sizeof(curthread->dlerror_msg);
2524d9128daSKonstantin Belousov 	li.dlerror_seen = _thr_dlerror_seen;
253b6b894f6SDavid Xu 
2541c70d007SKonstantin Belousov 	/*
2551c70d007SKonstantin Belousov 	 * Preresolve the symbols needed for the fork interposer.  We
2561c70d007SKonstantin Belousov 	 * call _rtld_atfork_pre() and _rtld_atfork_post() with NULL
2571c70d007SKonstantin Belousov 	 * argument to indicate that no actual locking inside the
2581c70d007SKonstantin Belousov 	 * functions should happen.  Neither rtld compat locks nor
2591c70d007SKonstantin Belousov 	 * libthr rtld locks cannot work there:
2601c70d007SKonstantin Belousov 	 * - compat locks do not handle the case of two locks taken
2611c70d007SKonstantin Belousov 	 *   in write mode (the signal mask for the thread is corrupted);
2621c70d007SKonstantin Belousov 	 * - libthr locks would work, but locked rtld_bind_lock prevents
2631c70d007SKonstantin Belousov 	 *   symbol resolution for _rtld_atfork_post.
2641c70d007SKonstantin Belousov 	 */
2651c70d007SKonstantin Belousov 	_rtld_atfork_pre(NULL);
2661c70d007SKonstantin Belousov 	_rtld_atfork_post(NULL);
2671c70d007SKonstantin Belousov 	_malloc_prefork();
2681c70d007SKonstantin Belousov 	_malloc_postfork();
269b6751c3fSKonstantin Belousov 	getpid();
2701c70d007SKonstantin Belousov 	syscall(SYS_getpid);
2711c70d007SKonstantin Belousov 
272b6b894f6SDavid Xu 	/* mask signals, also force to resolve __sys_sigprocmask PLT */
273b6b894f6SDavid Xu 	_thr_signal_block(curthread);
274b6b894f6SDavid Xu 	_rtld_thread_init(&li);
275b6b894f6SDavid Xu 	_thr_signal_unblock(curthread);
276fc908e50SKonstantin Belousov 	_thr_signal_block_check_fast();
277fc908e50SKonstantin Belousov 	_thr_signal_block_setup(curthread);
2783d40192dSKonstantin Belousov 
2793d40192dSKonstantin Belousov 	uc_len = __getcontextx_size();
2803d40192dSKonstantin Belousov 	uc = alloca(uc_len);
2813d40192dSKonstantin Belousov 	getcontext(uc);
2823d40192dSKonstantin Belousov 	__fillcontextx2((char *)uc);
283b6b894f6SDavid Xu }
284