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
42*1235d276SBrooks Davis extern int __libsys_errno;
438c38215fSDavid Xu
44b6b894f6SDavid Xu static int _thr_rtld_clr_flag(int);
45b6b894f6SDavid Xu static void *_thr_rtld_lock_create(void);
46b6b894f6SDavid Xu static void _thr_rtld_lock_destroy(void *);
47b6b894f6SDavid Xu static void _thr_rtld_lock_release(void *);
48b6b894f6SDavid Xu static void _thr_rtld_rlock_acquire(void *);
49b6b894f6SDavid Xu static int _thr_rtld_set_flag(int);
50b6b894f6SDavid Xu static void _thr_rtld_wlock_acquire(void *);
51b6b894f6SDavid Xu
52b6b894f6SDavid Xu struct rtld_lock {
53fb2641d9SDavid Xu struct urwlock lock;
5410b40346SKonstantin Belousov char _pad[CACHE_LINE_SIZE - sizeof(struct urwlock)];
55b6b894f6SDavid Xu };
56b6b894f6SDavid Xu
5710b40346SKonstantin Belousov static struct rtld_lock lock_place[MAX_RTLD_LOCKS] __aligned(CACHE_LINE_SIZE);
5810b40346SKonstantin Belousov static int busy_places;
5910b40346SKonstantin Belousov
60b6b894f6SDavid Xu static void *
_thr_rtld_lock_create(void)61b6b894f6SDavid Xu _thr_rtld_lock_create(void)
62b6b894f6SDavid Xu {
6310b40346SKonstantin Belousov int locki;
64b6b894f6SDavid Xu struct rtld_lock *l;
6510b40346SKonstantin Belousov static const char fail[] = "_thr_rtld_lock_create failed\n";
66b6b894f6SDavid Xu
6710b40346SKonstantin Belousov for (locki = 0; locki < MAX_RTLD_LOCKS; locki++) {
6810b40346SKonstantin Belousov if ((busy_places & (1 << locki)) == 0)
6910b40346SKonstantin Belousov break;
70b6b894f6SDavid Xu }
7110b40346SKonstantin Belousov if (locki == MAX_RTLD_LOCKS) {
7210b40346SKonstantin Belousov write(2, fail, sizeof(fail) - 1);
7310b40346SKonstantin Belousov return (NULL);
7410b40346SKonstantin Belousov }
7510b40346SKonstantin Belousov busy_places |= (1 << locki);
7610b40346SKonstantin Belousov
7710b40346SKonstantin Belousov l = &lock_place[locki];
78fb2641d9SDavid Xu l->lock.rw_flags = URWLOCK_PREFER_READER;
79b6b894f6SDavid Xu return (l);
80b6b894f6SDavid Xu }
81b6b894f6SDavid Xu
82b6b894f6SDavid Xu static void
_thr_rtld_lock_destroy(void * lock)83b6b894f6SDavid Xu _thr_rtld_lock_destroy(void *lock)
84b6b894f6SDavid Xu {
8510b40346SKonstantin Belousov int locki;
868e60ce99SDavid Xu size_t i;
8710b40346SKonstantin Belousov
8810b40346SKonstantin Belousov locki = (struct rtld_lock *)lock - &lock_place[0];
898e60ce99SDavid Xu for (i = 0; i < sizeof(struct rtld_lock); ++i)
908e60ce99SDavid Xu ((char *)lock)[i] = 0;
9110b40346SKonstantin Belousov busy_places &= ~(1 << locki);
92b6b894f6SDavid Xu }
93b6b894f6SDavid Xu
948c38215fSDavid Xu #define SAVE_ERRNO() { \
958c38215fSDavid Xu if (curthread != _thr_initial) \
968c38215fSDavid Xu errsave = curthread->error; \
978c38215fSDavid Xu else \
98*1235d276SBrooks Davis errsave = __libsys_errno; \
998c38215fSDavid Xu }
1008c38215fSDavid Xu
1018c38215fSDavid Xu #define RESTORE_ERRNO() { \
1028c38215fSDavid Xu if (curthread != _thr_initial) \
1038c38215fSDavid Xu curthread->error = errsave; \
1048c38215fSDavid Xu else \
105*1235d276SBrooks Davis __libsys_errno = errsave; \
1068c38215fSDavid Xu }
1078c38215fSDavid Xu
108b6b894f6SDavid Xu static void
_thr_rtld_rlock_acquire(void * lock)109b6b894f6SDavid Xu _thr_rtld_rlock_acquire(void *lock)
110b6b894f6SDavid Xu {
111b6b894f6SDavid Xu struct pthread *curthread;
112b6b894f6SDavid Xu struct rtld_lock *l;
1138c38215fSDavid Xu int errsave;
114b6b894f6SDavid Xu
115b6b894f6SDavid Xu curthread = _get_curthread();
1168c38215fSDavid Xu SAVE_ERRNO();
117b6b894f6SDavid Xu l = (struct rtld_lock *)lock;
118b6b894f6SDavid Xu
119b6b894f6SDavid Xu THR_CRITICAL_ENTER(curthread);
120fb2641d9SDavid Xu while (_thr_rwlock_rdlock(&l->lock, 0, NULL) != 0)
121fb2641d9SDavid Xu ;
122137ae5d2SAttilio Rao curthread->rdlock_count++;
1238c38215fSDavid Xu RESTORE_ERRNO();
124b6b894f6SDavid Xu }
125b6b894f6SDavid Xu
126b6b894f6SDavid Xu static void
_thr_rtld_wlock_acquire(void * lock)127b6b894f6SDavid Xu _thr_rtld_wlock_acquire(void *lock)
128b6b894f6SDavid Xu {
129b6b894f6SDavid Xu struct pthread *curthread;
130b6b894f6SDavid Xu struct rtld_lock *l;
1318c38215fSDavid Xu int errsave;
132b6b894f6SDavid Xu
133b6b894f6SDavid Xu curthread = _get_curthread();
1348c38215fSDavid Xu SAVE_ERRNO();
135b6b894f6SDavid Xu l = (struct rtld_lock *)lock;
136b6b894f6SDavid Xu
13702c3c858SDavid Xu THR_CRITICAL_ENTER(curthread);
138fb2641d9SDavid Xu while (_thr_rwlock_wrlock(&l->lock, NULL) != 0)
139fb2641d9SDavid Xu ;
1408c38215fSDavid Xu RESTORE_ERRNO();
141b6b894f6SDavid Xu }
142b6b894f6SDavid Xu
143b6b894f6SDavid Xu static void
_thr_rtld_lock_release(void * lock)144b6b894f6SDavid Xu _thr_rtld_lock_release(void *lock)
145b6b894f6SDavid Xu {
146b6b894f6SDavid Xu struct pthread *curthread;
147b6b894f6SDavid Xu struct rtld_lock *l;
148fb2641d9SDavid Xu int32_t state;
1498c38215fSDavid Xu int errsave;
150b6b894f6SDavid Xu
151b6b894f6SDavid Xu curthread = _get_curthread();
1528c38215fSDavid Xu SAVE_ERRNO();
153b6b894f6SDavid Xu l = (struct rtld_lock *)lock;
154b6b894f6SDavid Xu
155fb2641d9SDavid Xu state = l->lock.rw_state;
1566f49eafbSKonstantin Belousov if (__predict_false(_thr_after_fork)) {
1576f49eafbSKonstantin Belousov /*
1586f49eafbSKonstantin Belousov * After fork, only this thread is running, there is no
1596f49eafbSKonstantin Belousov * waiters. Keeping waiters recorded in rwlock breaks
1606f49eafbSKonstantin Belousov * wake logic.
1616f49eafbSKonstantin Belousov */
1626f49eafbSKonstantin Belousov atomic_clear_int(&l->lock.rw_state,
1636f49eafbSKonstantin Belousov URWLOCK_WRITE_WAITERS | URWLOCK_READ_WAITERS);
1646f49eafbSKonstantin Belousov l->lock.rw_blocked_readers = 0;
1656f49eafbSKonstantin Belousov l->lock.rw_blocked_writers = 0;
1666f49eafbSKonstantin Belousov }
167fb2641d9SDavid Xu if (_thr_rwlock_unlock(&l->lock) == 0) {
16802c3c858SDavid Xu if ((state & URWLOCK_WRITE_OWNER) == 0)
1696b932ecaSDavid Xu curthread->rdlock_count--;
170b6b894f6SDavid Xu THR_CRITICAL_LEAVE(curthread);
171fb2641d9SDavid Xu }
1728c38215fSDavid Xu RESTORE_ERRNO();
173b6b894f6SDavid Xu }
174b6b894f6SDavid Xu
175b6b894f6SDavid Xu static int
_thr_rtld_set_flag(int mask __unused)17637a6356bSDavid Xu _thr_rtld_set_flag(int mask __unused)
177b6b894f6SDavid Xu {
178b6b894f6SDavid Xu /*
179b6b894f6SDavid Xu * The caller's code in rtld-elf is broken, it is not signal safe,
180b6b894f6SDavid Xu * just return zero to fool it.
181b6b894f6SDavid Xu */
182b6b894f6SDavid Xu return (0);
183b6b894f6SDavid Xu }
184b6b894f6SDavid Xu
185b6b894f6SDavid Xu static int
_thr_rtld_clr_flag(int mask __unused)18637a6356bSDavid Xu _thr_rtld_clr_flag(int mask __unused)
187b6b894f6SDavid Xu {
188b6b894f6SDavid Xu return (0);
189b6b894f6SDavid Xu }
190b6b894f6SDavid Xu
19108bfbd43SKonstantin Belousov /*
19208bfbd43SKonstantin Belousov * ABI bug workaround: This symbol must be present for rtld to accept
19308bfbd43SKonstantin Belousov * RTLI_VERSION from RtldLockInfo
19408bfbd43SKonstantin Belousov */
19508bfbd43SKonstantin Belousov extern char _pli_rtli_version;
19608bfbd43SKonstantin Belousov char _pli_rtli_version;
19708bfbd43SKonstantin Belousov
1984d9128daSKonstantin Belousov static char *
_thr_dlerror_loc(void)1994d9128daSKonstantin Belousov _thr_dlerror_loc(void)
2004d9128daSKonstantin Belousov {
2014d9128daSKonstantin Belousov struct pthread *curthread;
2024d9128daSKonstantin Belousov
2034d9128daSKonstantin Belousov curthread = _get_curthread();
2044d9128daSKonstantin Belousov return (curthread->dlerror_msg);
2054d9128daSKonstantin Belousov }
2064d9128daSKonstantin Belousov
2074d9128daSKonstantin Belousov static int *
_thr_dlerror_seen(void)2084d9128daSKonstantin Belousov _thr_dlerror_seen(void)
2094d9128daSKonstantin Belousov {
2104d9128daSKonstantin Belousov struct pthread *curthread;
2114d9128daSKonstantin Belousov
2124d9128daSKonstantin Belousov curthread = _get_curthread();
2134d9128daSKonstantin Belousov return (&curthread->dlerror_seen);
2144d9128daSKonstantin Belousov }
2154d9128daSKonstantin Belousov
216b6b894f6SDavid Xu void
_thr_rtld_init(void)217b6b894f6SDavid Xu _thr_rtld_init(void)
218b6b894f6SDavid Xu {
219b6b894f6SDavid Xu struct RtldLockInfo li;
220b6b894f6SDavid Xu struct pthread *curthread;
2213d40192dSKonstantin Belousov ucontext_t *uc;
2223d40192dSKonstantin Belousov int uc_len;
223f7f9a033SRyan Libby char dummy[2] = {};
224b6b894f6SDavid Xu
225b6b894f6SDavid Xu curthread = _get_curthread();
226b6b894f6SDavid Xu
227b6b894f6SDavid Xu /* force to resolve _umtx_op PLT */
228f7f9a033SRyan Libby _umtx_op_err(&dummy, UMTX_OP_WAKE, 1, 0, 0);
22904a57d2cSDavid Xu
23004a57d2cSDavid Xu /* force to resolve errno() PLT */
23104a57d2cSDavid Xu __error();
232b6b894f6SDavid Xu
23302c3c858SDavid Xu /* force to resolve memcpy PLT */
234f7f9a033SRyan Libby memcpy(&dummy[0], &dummy[1], 1);
23502c3c858SDavid Xu
236da2fcff7SKonstantin Belousov mprotect(NULL, 0, 0);
237da2fcff7SKonstantin Belousov _rtld_get_stack_prot();
23836f0a34cSMark Johnston thr_wake(-1);
239da2fcff7SKonstantin Belousov
24008bfbd43SKonstantin Belousov li.rtli_version = RTLI_VERSION;
241b6b894f6SDavid Xu li.lock_create = _thr_rtld_lock_create;
242b6b894f6SDavid Xu li.lock_destroy = _thr_rtld_lock_destroy;
243b6b894f6SDavid Xu li.rlock_acquire = _thr_rtld_rlock_acquire;
244b6b894f6SDavid Xu li.wlock_acquire = _thr_rtld_wlock_acquire;
245b6b894f6SDavid Xu li.lock_release = _thr_rtld_lock_release;
246b6b894f6SDavid Xu li.thread_set_flag = _thr_rtld_set_flag;
247b6b894f6SDavid Xu li.thread_clr_flag = _thr_rtld_clr_flag;
248b6b894f6SDavid Xu li.at_fork = NULL;
2494d9128daSKonstantin Belousov li.dlerror_loc = _thr_dlerror_loc;
2504d9128daSKonstantin Belousov li.dlerror_loc_sz = sizeof(curthread->dlerror_msg);
2514d9128daSKonstantin Belousov li.dlerror_seen = _thr_dlerror_seen;
252b6b894f6SDavid Xu
2531c70d007SKonstantin Belousov /*
2541c70d007SKonstantin Belousov * Preresolve the symbols needed for the fork interposer. We
2551c70d007SKonstantin Belousov * call _rtld_atfork_pre() and _rtld_atfork_post() with NULL
2561c70d007SKonstantin Belousov * argument to indicate that no actual locking inside the
2571c70d007SKonstantin Belousov * functions should happen. Neither rtld compat locks nor
2581c70d007SKonstantin Belousov * libthr rtld locks cannot work there:
2591c70d007SKonstantin Belousov * - compat locks do not handle the case of two locks taken
2601c70d007SKonstantin Belousov * in write mode (the signal mask for the thread is corrupted);
2611c70d007SKonstantin Belousov * - libthr locks would work, but locked rtld_bind_lock prevents
2621c70d007SKonstantin Belousov * symbol resolution for _rtld_atfork_post.
2631c70d007SKonstantin Belousov */
2641c70d007SKonstantin Belousov _rtld_atfork_pre(NULL);
2651c70d007SKonstantin Belousov _rtld_atfork_post(NULL);
2661c70d007SKonstantin Belousov _malloc_prefork();
2671c70d007SKonstantin Belousov _malloc_postfork();
268b6751c3fSKonstantin Belousov getpid();
2691c70d007SKonstantin Belousov syscall(SYS_getpid);
2701c70d007SKonstantin Belousov
271b6b894f6SDavid Xu /* mask signals, also force to resolve __sys_sigprocmask PLT */
272b6b894f6SDavid Xu _thr_signal_block(curthread);
273b6b894f6SDavid Xu _rtld_thread_init(&li);
274b6b894f6SDavid Xu _thr_signal_unblock(curthread);
275fc908e50SKonstantin Belousov _thr_signal_block_check_fast();
276fc908e50SKonstantin Belousov _thr_signal_block_setup(curthread);
2773d40192dSKonstantin Belousov
2785670b8ccSMichal Meloun /* resolve machine depended functions, if any */
2795670b8ccSMichal Meloun _thr_resolve_machdep();
2805670b8ccSMichal Meloun
2813d40192dSKonstantin Belousov uc_len = __getcontextx_size();
2823d40192dSKonstantin Belousov uc = alloca(uc_len);
2833d40192dSKonstantin Belousov getcontext(uc);
2843d40192dSKonstantin Belousov __fillcontextx2((char *)uc);
285b6b894f6SDavid Xu }
286