1*b1f732fcSchristos /* $NetBSD: rtld.c,v 1.217 2024/01/19 19:21:34 christos Exp $ */
241fe218bScgd
341fe218bScgd /*
441fe218bScgd * Copyright 1996 John D. Polstra.
541fe218bScgd * Copyright 1996 Matt Thomas <matt@3am-software.com>
6ad8ccd62Smycroft * Copyright 2002 Charles M. Hannum <root@ihack.net>
741fe218bScgd * All rights reserved.
841fe218bScgd *
941fe218bScgd * Redistribution and use in source and binary forms, with or without
1041fe218bScgd * modification, are permitted provided that the following conditions
1141fe218bScgd * are met:
1241fe218bScgd * 1. Redistributions of source code must retain the above copyright
1341fe218bScgd * notice, this list of conditions and the following disclaimer.
1441fe218bScgd * 2. Redistributions in binary form must reproduce the above copyright
1541fe218bScgd * notice, this list of conditions and the following disclaimer in the
1641fe218bScgd * documentation and/or other materials provided with the distribution.
1741fe218bScgd * 3. All advertising materials mentioning features or use of this software
1841fe218bScgd * must display the following acknowledgement:
1941fe218bScgd * This product includes software developed by John Polstra.
2041fe218bScgd * 4. The name of the author may not be used to endorse or promote products
2141fe218bScgd * derived from this software without specific prior written permission.
2241fe218bScgd *
2341fe218bScgd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2441fe218bScgd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2541fe218bScgd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2641fe218bScgd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2741fe218bScgd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2841fe218bScgd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2941fe218bScgd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3041fe218bScgd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3141fe218bScgd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3241fe218bScgd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3341fe218bScgd */
3441fe218bScgd
3541fe218bScgd /*
3641fe218bScgd * Dynamic linker for ELF.
3741fe218bScgd *
3841fe218bScgd * John Polstra <jdp@polstra.com>.
3941fe218bScgd */
4041fe218bScgd
412728318eSskrll #include <sys/cdefs.h>
422728318eSskrll #ifndef lint
43*b1f732fcSchristos __RCSID("$NetBSD: rtld.c,v 1.217 2024/01/19 19:21:34 christos Exp $");
442728318eSskrll #endif /* not lint */
452728318eSskrll
46cb1cd7e8Sjoerg #include <sys/param.h>
47cb1cd7e8Sjoerg #include <sys/atomic.h>
48cb1cd7e8Sjoerg #include <sys/mman.h>
4941fe218bScgd #include <err.h>
5041fe218bScgd #include <errno.h>
5141fe218bScgd #include <fcntl.h>
52cb1cd7e8Sjoerg #include <lwp.h>
5341fe218bScgd #include <stdarg.h>
5441fe218bScgd #include <stdio.h>
5541fe218bScgd #include <stdlib.h>
5641fe218bScgd #include <string.h>
5741fe218bScgd #include <unistd.h>
5841fe218bScgd #include <dirent.h>
5941fe218bScgd
6041fe218bScgd #include <ctype.h>
6141fe218bScgd
620b2b5475Scgd #include <dlfcn.h>
63eeb15e76Sriastradh
6441fe218bScgd #include "debug.h"
65eeb15e76Sriastradh #include "hash.h"
6641fe218bScgd #include "rtld.h"
6741fe218bScgd
68571aef82Skleink #if !defined(lint)
69fbf6ff62Scgd #include "sysident.h"
70571aef82Skleink #endif
71fbf6ff62Scgd
7241fe218bScgd /*
73aecd65a0Smartin * Hidden function from common/lib/libc/atomic - nop on machines
74aecd65a0Smartin * with enough atomic ops. Need to explicitly call it early.
75aecd65a0Smartin * libc has the same symbol and will initialize itself, but not our copy.
76aecd65a0Smartin */
77aecd65a0Smartin void __libc_atomic_init(void);
78aecd65a0Smartin
79aecd65a0Smartin /*
8041fe218bScgd * Function declarations.
8141fe218bScgd */
8204ee85adSchristos static void _rtld_init(caddr_t, caddr_t, const char *);
835f573ab6Sskrll static void _rtld_exit(void);
8426475619Schristos
855f573ab6Sskrll Elf_Addr _rtld(Elf_Addr *, Elf_Addr);
8626475619Schristos
8741fe218bScgd
8841fe218bScgd /*
8941fe218bScgd * Data declarations.
9041fe218bScgd */
9141fe218bScgd static char *error_message; /* Message for dlopen(), or NULL */
9241fe218bScgd
93f3656ffaSkamil struct r_debug _rtld_debug; /* The SVR4 interface for the debugger */
9441fe218bScgd bool _rtld_trust; /* False for setuid and setgid programs */
9541fe218bScgd Obj_Entry *_rtld_objlist; /* Head of linked list of shared objects */
9641fe218bScgd Obj_Entry **_rtld_objtail; /* Link field of last object in list */
9741fe218bScgd Obj_Entry *_rtld_objmain; /* The main program shared object */
9841fe218bScgd Obj_Entry _rtld_objself; /* The dynamic linker shared object */
99e6cdac9cSskrll u_int _rtld_objcount; /* Number of objects in _rtld_objlist */
100e6cdac9cSskrll u_int _rtld_objloads; /* Number of objects loaded in _rtld_objlist */
101e49f915cSjoerg u_int _rtld_objgen; /* Generation count for _rtld_objlist */
10238bdc895Smatt const char _rtld_path[] = _PATH_RTLD;
10338bdc895Smatt
10438bdc895Smatt /* Initialize a fake symbol for resolving undefined weak references. */
10538bdc895Smatt Elf_Sym _rtld_sym_zero = {
10638bdc895Smatt .st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
10738bdc895Smatt .st_shndx = SHN_ABS,
10838bdc895Smatt };
10977fd7698Sskrll size_t _rtld_pagesz; /* Page size, as provided by kernel */
11041fe218bScgd
1111cc052ceSkleink Search_Path *_rtld_default_paths;
11241fe218bScgd Search_Path *_rtld_paths;
113ea816c78Schristos
114ea816c78Schristos Library_Xform *_rtld_xforms;
11552a4c27eSjoerg static void *auxinfo;
116ea816c78Schristos
11741fe218bScgd /*
11841fe218bScgd * Global declarations normally provided by crt0.
11941fe218bScgd */
12041fe218bScgd char *__progname;
12141fe218bScgd char **environ;
12241fe218bScgd
123cb1cd7e8Sjoerg static volatile bool _rtld_mutex_may_recurse;
124cb1cd7e8Sjoerg
125b98cf463Suwe #if defined(RTLD_DEBUG)
126596a371dSscw #ifndef __sh__
1273ecc6b04Stv extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
128b98cf463Suwe #else /* 32-bit SuperH */
129b98cf463Suwe register Elf_Addr *_GLOBAL_OFFSET_TABLE_ asm("r12");
130b98cf463Suwe #endif
131b98cf463Suwe #endif /* RTLD_DEBUG */
13241fe218bScgd extern Elf_Dyn _DYNAMIC;
13341fe218bScgd
1348fa65855Sjoerg static void _rtld_call_fini_functions(sigset_t *, int);
1358fa65855Sjoerg static void _rtld_call_init_functions(sigset_t *);
136558a5ffdSad static void _rtld_initlist_visit(Objlist *, Obj_Entry *, int);
137558a5ffdSad static void _rtld_initlist_tsort(Objlist *, int);
1385f573ab6Sskrll static Obj_Entry *_rtld_dlcheck(void *);
1395f573ab6Sskrll static void _rtld_init_dag(Obj_Entry *);
1405f573ab6Sskrll static void _rtld_init_dag1(Obj_Entry *, Obj_Entry *);
1415f573ab6Sskrll static void _rtld_objlist_remove(Objlist *, Obj_Entry *);
142558a5ffdSad static void _rtld_objlist_clear(Objlist *);
1438fa65855Sjoerg static void _rtld_unload_object(sigset_t *, Obj_Entry *, bool);
1445f573ab6Sskrll static void _rtld_unref_dag(Obj_Entry *);
1455f573ab6Sskrll static Obj_Entry *_rtld_obj_from_addr(const void *);
1463b610a6eSkamil static void _rtld_fill_dl_phdr_info(const Obj_Entry *, struct dl_phdr_info *);
14726475619Schristos
148e3dfed33Smatt static inline void
_rtld_call_initfini_function(fptr_t func,sigset_t * mask)14945f12ca2Sskrll _rtld_call_initfini_function(fptr_t func, sigset_t *mask)
150e3dfed33Smatt {
151e3dfed33Smatt _rtld_exclusive_exit(mask);
15245f12ca2Sskrll (*func)();
153e3dfed33Smatt _rtld_exclusive_enter(mask);
154e3dfed33Smatt }
155e3dfed33Smatt
156e3dfed33Smatt static void
_rtld_call_fini_function(Obj_Entry * obj,sigset_t * mask,u_int cur_objgen)157e3dfed33Smatt _rtld_call_fini_function(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
158e3dfed33Smatt {
15945f12ca2Sskrll if (obj->fini_arraysz == 0 && (obj->fini == NULL || obj->fini_called))
160e3dfed33Smatt return;
1617a1a6f1cSjoerg
16245f12ca2Sskrll if (obj->fini != NULL && !obj->fini_called) {
163e3dfed33Smatt dbg (("calling fini function %s at %p%s", obj->path,
164e3dfed33Smatt (void *)obj->fini,
165e3dfed33Smatt obj->z_initfirst ? " (DF_1_INITFIRST)" : ""));
166e3dfed33Smatt obj->fini_called = 1;
16745f12ca2Sskrll _rtld_call_initfini_function(obj->fini, mask);
168e3dfed33Smatt }
169e3dfed33Smatt #ifdef HAVE_INITFINI_ARRAY
170e3dfed33Smatt /*
171e3dfed33Smatt * Now process the fini_array if it exists. Simply go from
172e3dfed33Smatt * start to end. We need to make restartable so just advance
173e3dfed33Smatt * the array pointer and decrement the size each time through
174e3dfed33Smatt * the loop.
175e3dfed33Smatt */
176e3dfed33Smatt while (obj->fini_arraysz > 0 && _rtld_objgen == cur_objgen) {
17745f12ca2Sskrll fptr_t fini = *obj->fini_array++;
178e3dfed33Smatt obj->fini_arraysz--;
179e3dfed33Smatt dbg (("calling fini array function %s at %p%s", obj->path,
180e3dfed33Smatt (void *)fini,
181e3dfed33Smatt obj->z_initfirst ? " (DF_1_INITFIRST)" : ""));
18245f12ca2Sskrll _rtld_call_initfini_function(fini, mask);
183e3dfed33Smatt }
184e3dfed33Smatt #endif /* HAVE_INITFINI_ARRAY */
185e3dfed33Smatt }
186e3dfed33Smatt
18741fe218bScgd static void
_rtld_call_fini_functions(sigset_t * mask,int force)1888fa65855Sjoerg _rtld_call_fini_functions(sigset_t *mask, int force)
18941fe218bScgd {
190558a5ffdSad Objlist_Entry *elm;
191558a5ffdSad Objlist finilist;
192e49f915cSjoerg u_int cur_objgen;
19341fe218bScgd
194558a5ffdSad dbg(("_rtld_call_fini_functions(%d)", force));
195558a5ffdSad
196e49f915cSjoerg restart:
197e49f915cSjoerg cur_objgen = ++_rtld_objgen;
198558a5ffdSad SIMPLEQ_INIT(&finilist);
199558a5ffdSad _rtld_initlist_tsort(&finilist, 1);
200558a5ffdSad
201558a5ffdSad /* First pass: objects _not_ marked with DF_1_INITFIRST. */
202558a5ffdSad SIMPLEQ_FOREACH(elm, &finilist, link) {
203e3dfed33Smatt Obj_Entry * const obj = elm->obj;
204e3dfed33Smatt if (!obj->z_initfirst) {
205558a5ffdSad if (obj->refcount > 0 && !force) {
206558a5ffdSad continue;
207558a5ffdSad }
208e49f915cSjoerg /*
209e49f915cSjoerg * XXX This can race against a concurrent dlclose().
210e49f915cSjoerg * XXX In that case, the object could be unmapped before
211e3dfed33Smatt * XXX the fini() call or the fini_array has completed.
212e49f915cSjoerg */
213e3dfed33Smatt _rtld_call_fini_function(obj, mask, cur_objgen);
214e49f915cSjoerg if (_rtld_objgen != cur_objgen) {
215e49f915cSjoerg dbg(("restarting fini iteration"));
216e49f915cSjoerg _rtld_objlist_clear(&finilist);
217e49f915cSjoerg goto restart;
218e49f915cSjoerg }
21941fe218bScgd }
220e3dfed33Smatt }
22141fe218bScgd
222558a5ffdSad /* Second pass: objects marked with DF_1_INITFIRST. */
223558a5ffdSad SIMPLEQ_FOREACH(elm, &finilist, link) {
224e3dfed33Smatt Obj_Entry * const obj = elm->obj;
225558a5ffdSad if (obj->refcount > 0 && !force) {
226558a5ffdSad continue;
22741fe218bScgd }
228e49f915cSjoerg /* XXX See above for the race condition here */
229e3dfed33Smatt _rtld_call_fini_function(obj, mask, cur_objgen);
230e49f915cSjoerg if (_rtld_objgen != cur_objgen) {
231e49f915cSjoerg dbg(("restarting fini iteration"));
232e49f915cSjoerg _rtld_objlist_clear(&finilist);
233e49f915cSjoerg goto restart;
234e49f915cSjoerg }
235558a5ffdSad }
236558a5ffdSad
237558a5ffdSad _rtld_objlist_clear(&finilist);
238558a5ffdSad }
239558a5ffdSad
240558a5ffdSad static void
_rtld_call_init_function(Obj_Entry * obj,sigset_t * mask,u_int cur_objgen)241e3dfed33Smatt _rtld_call_init_function(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
242e3dfed33Smatt {
24345f12ca2Sskrll if (obj->init_arraysz == 0 && (obj->init_called || obj->init == NULL))
244e3dfed33Smatt return;
2457a1a6f1cSjoerg
24645f12ca2Sskrll if (!obj->init_called && obj->init != NULL) {
247e3dfed33Smatt dbg (("calling init function %s at %p%s",
248e3dfed33Smatt obj->path, (void *)obj->init,
249e3dfed33Smatt obj->z_initfirst ? " (DF_1_INITFIRST)" : ""));
250e3dfed33Smatt obj->init_called = 1;
25145f12ca2Sskrll _rtld_call_initfini_function(obj->init, mask);
252e3dfed33Smatt }
253e3dfed33Smatt
254e3dfed33Smatt #ifdef HAVE_INITFINI_ARRAY
255e3dfed33Smatt /*
256e3dfed33Smatt * Now process the init_array if it exists. Simply go from
257e3dfed33Smatt * start to end. We need to make restartable so just advance
258e3dfed33Smatt * the array pointer and decrement the size each time through
259e3dfed33Smatt * the loop.
260e3dfed33Smatt */
261e3dfed33Smatt while (obj->init_arraysz > 0 && _rtld_objgen == cur_objgen) {
26245f12ca2Sskrll fptr_t init = *obj->init_array++;
263e3dfed33Smatt obj->init_arraysz--;
264e3dfed33Smatt dbg (("calling init_array function %s at %p%s",
265e3dfed33Smatt obj->path, (void *)init,
266e3dfed33Smatt obj->z_initfirst ? " (DF_1_INITFIRST)" : ""));
26745f12ca2Sskrll _rtld_call_initfini_function(init, mask);
268e3dfed33Smatt }
269e3dfed33Smatt #endif /* HAVE_INITFINI_ARRAY */
270e3dfed33Smatt }
271e3dfed33Smatt
272da03f1efSjoerg static bool
_rtld_call_ifunc_functions(sigset_t * mask,Obj_Entry * obj,u_int cur_objgen)273da03f1efSjoerg _rtld_call_ifunc_functions(sigset_t *mask, Obj_Entry *obj, u_int cur_objgen)
274da03f1efSjoerg {
275da03f1efSjoerg if (obj->ifunc_remaining
276f80c3669Sjoerg #if defined(IFUNC_NONPLT)
277da03f1efSjoerg || obj->ifunc_remaining_nonplt
278da03f1efSjoerg #endif
279da03f1efSjoerg ) {
280da03f1efSjoerg _rtld_call_ifunc(obj, mask, cur_objgen);
281da03f1efSjoerg if (_rtld_objgen != cur_objgen) {
282da03f1efSjoerg return true;
283da03f1efSjoerg }
284da03f1efSjoerg }
285da03f1efSjoerg return false;
286da03f1efSjoerg }
287da03f1efSjoerg
288e3dfed33Smatt static void
_rtld_call_init_functions(sigset_t * mask)2898fa65855Sjoerg _rtld_call_init_functions(sigset_t *mask)
290558a5ffdSad {
291558a5ffdSad Objlist_Entry *elm;
292558a5ffdSad Objlist initlist;
293e49f915cSjoerg u_int cur_objgen;
294558a5ffdSad
295558a5ffdSad dbg(("_rtld_call_init_functions()"));
296e49f915cSjoerg
297e49f915cSjoerg restart:
298e49f915cSjoerg cur_objgen = ++_rtld_objgen;
299558a5ffdSad SIMPLEQ_INIT(&initlist);
300558a5ffdSad _rtld_initlist_tsort(&initlist, 0);
301558a5ffdSad
302e78cfb8eSjoerg /* First pass: objects with IRELATIVE relocations. */
303e78cfb8eSjoerg SIMPLEQ_FOREACH(elm, &initlist, link) {
304da03f1efSjoerg if (_rtld_call_ifunc_functions(mask, elm->obj, cur_objgen)) {
305e78cfb8eSjoerg dbg(("restarting init iteration"));
306e78cfb8eSjoerg _rtld_objlist_clear(&initlist);
307e78cfb8eSjoerg goto restart;
308e78cfb8eSjoerg }
309e78cfb8eSjoerg }
310da03f1efSjoerg /*
311da03f1efSjoerg * XXX: For historic reasons, init/fini of the main object are called
312da03f1efSjoerg * from crt0. Don't introduce that mistake for ifunc, so look at
313da03f1efSjoerg * the head of _rtld_objlist that _rtld_initlist_tsort skipped.
314da03f1efSjoerg */
315da03f1efSjoerg if (_rtld_call_ifunc_functions(mask, _rtld_objlist, cur_objgen)) {
316da03f1efSjoerg dbg(("restarting init iteration"));
317da03f1efSjoerg _rtld_objlist_clear(&initlist);
318da03f1efSjoerg goto restart;
319e78cfb8eSjoerg }
320e78cfb8eSjoerg
321e78cfb8eSjoerg /* Second pass: objects marked with DF_1_INITFIRST. */
322558a5ffdSad SIMPLEQ_FOREACH(elm, &initlist, link) {
323e3dfed33Smatt Obj_Entry * const obj = elm->obj;
324e3dfed33Smatt if (obj->z_initfirst) {
325e3dfed33Smatt _rtld_call_init_function(obj, mask, cur_objgen);
326e49f915cSjoerg if (_rtld_objgen != cur_objgen) {
327e49f915cSjoerg dbg(("restarting init iteration"));
328e49f915cSjoerg _rtld_objlist_clear(&initlist);
329e49f915cSjoerg goto restart;
330e49f915cSjoerg }
331558a5ffdSad }
332e3dfed33Smatt }
333558a5ffdSad
334e78cfb8eSjoerg /* Third pass: all other objects. */
335558a5ffdSad SIMPLEQ_FOREACH(elm, &initlist, link) {
336e3dfed33Smatt _rtld_call_init_function(elm->obj, mask, cur_objgen);
337e49f915cSjoerg if (_rtld_objgen != cur_objgen) {
338e49f915cSjoerg dbg(("restarting init iteration"));
339e49f915cSjoerg _rtld_objlist_clear(&initlist);
340e49f915cSjoerg goto restart;
341e49f915cSjoerg }
342558a5ffdSad }
343558a5ffdSad
344558a5ffdSad _rtld_objlist_clear(&initlist);
34541fe218bScgd }
346a32bf0c1Spk
34741fe218bScgd /*
34841fe218bScgd * Initialize the dynamic linker. The argument is the address at which
34941fe218bScgd * the dynamic linker has been mapped into memory. The primary task of
350701ae47cSskrll * this function is to create an Obj_Entry for the dynamic linker and
351701ae47cSskrll * to resolve the PLT relocation for platforms that need it (those that
352701ae47cSskrll * define __HAVE_FUNCTION_DESCRIPTORS
35341fe218bScgd */
35441fe218bScgd static void
_rtld_init(caddr_t mapbase,caddr_t relocbase,const char * execname)35540cfdb5eSchristos _rtld_init(caddr_t mapbase, caddr_t relocbase, const char *execname)
35641fe218bScgd {
3573b610a6eSkamil const Elf_Ehdr *ehdr;
358a9f5b3f8Ssimonb
35941fe218bScgd /* Conjure up an Obj_Entry structure for the dynamic linker. */
36038bdc895Smatt _rtld_objself.path = __UNCONST(_rtld_path);
36138bdc895Smatt _rtld_objself.pathlen = sizeof(_rtld_path)-1;
36273a80999Smycroft _rtld_objself.rtld = true;
36373a80999Smycroft _rtld_objself.mapbase = mapbase;
36473a80999Smycroft _rtld_objself.relocbase = relocbase;
36573a80999Smycroft _rtld_objself.dynamic = (Elf_Dyn *) &_DYNAMIC;
366d3b459e7Schristos _rtld_objself.strtab = "_rtld_sym_zero";
36741fe218bScgd
36838bdc895Smatt /*
369fa7832d0Sskrll * Set value to -relocbase so that
370fa7832d0Sskrll *
371fa7832d0Sskrll * _rtld_objself.relocbase + _rtld_sym_zero.st_value == 0
372fa7832d0Sskrll *
37338bdc895Smatt * This allows unresolved references to weak symbols to be computed
374fa7832d0Sskrll * to a value of 0.
37538bdc895Smatt */
37638bdc895Smatt _rtld_sym_zero.st_value = -(uintptr_t)relocbase;
37738bdc895Smatt
37804ee85adSchristos _rtld_digest_dynamic(_rtld_path, &_rtld_objself);
379e88c851dSskrll assert(!_rtld_objself.needed);
380701ae47cSskrll #if !defined(__hppa__)
3810556cd7dSskrll assert(!_rtld_objself.pltrel && !_rtld_objself.pltrela);
382701ae47cSskrll #else
383701ae47cSskrll _rtld_relocate_plt_objects(&_rtld_objself);
384701ae47cSskrll #endif
385701ae47cSskrll #if !defined(__mips__) && !defined(__hppa__)
386e88c851dSskrll assert(!_rtld_objself.pltgot);
3870556cd7dSskrll #endif
388b2695e5aSskrll #if !defined(__arm__) && !defined(__mips__) && !defined(__sh__)
389b2695e5aSskrll /* ARM, MIPS and SH{3,5} have a bogus DT_TEXTREL. */
3900556cd7dSskrll assert(!_rtld_objself.textrel);
391082edeccSmhitch #endif
39241fe218bScgd
39340cfdb5eSchristos _rtld_add_paths(execname, &_rtld_default_paths,
39440cfdb5eSchristos RTLD_DEFAULT_LIBRARY_PATH);
39584cb6578Schristos
3968679e335Smrg #ifdef RTLD_ARCH_SUBDIR
3978679e335Smrg _rtld_add_paths(execname, &_rtld_default_paths,
3988679e335Smrg RTLD_DEFAULT_LIBRARY_PATH "/" RTLD_ARCH_SUBDIR);
3998679e335Smrg #endif
4008679e335Smrg
4011f32502bSskrll /* Make the object list empty. */
40241fe218bScgd _rtld_objlist = NULL;
40341fe218bScgd _rtld_objtail = &_rtld_objlist;
404b02ec7e9Sroy _rtld_objcount = 0;
40541fe218bScgd
406f3656ffaSkamil _rtld_debug.r_version = R_DEBUG_VERSION;
40741fe218bScgd _rtld_debug.r_brk = _rtld_debug_state;
40841fe218bScgd _rtld_debug.r_state = RT_CONSISTENT;
409f420d54cSkamil _rtld_debug.r_ldbase = _rtld_objself.relocbase;
4103b610a6eSkamil
4113b610a6eSkamil ehdr = (Elf_Ehdr *)mapbase;
4123b610a6eSkamil _rtld_objself.phdr = (Elf_Phdr *)((char *)mapbase + ehdr->e_phoff);
4133b610a6eSkamil _rtld_objself.phsize = ehdr->e_phnum * sizeof(_rtld_objself.phdr[0]);
414aecd65a0Smartin
415aecd65a0Smartin __libc_atomic_init();
41641fe218bScgd }
417a32bf0c1Spk
41841fe218bScgd /*
41941fe218bScgd * Cleanup procedure. It will be called (by the atexit() mechanism) just
42041fe218bScgd * before the process exits.
42141fe218bScgd */
42241fe218bScgd static void
_rtld_exit(void)4235f573ab6Sskrll _rtld_exit(void)
42441fe218bScgd {
4258fa65855Sjoerg sigset_t mask;
4268fa65855Sjoerg
42726475619Schristos dbg(("rtld_exit()"));
42841fe218bScgd
4298fa65855Sjoerg _rtld_exclusive_enter(&mask);
43023a805d3Sjoerg
4318fa65855Sjoerg _rtld_call_fini_functions(&mask, 1);
43223a805d3Sjoerg
4338fa65855Sjoerg _rtld_exclusive_exit(&mask);
43441fe218bScgd }
435a32bf0c1Spk
43652a4c27eSjoerg __dso_public void *
_dlauxinfo(void)43752a4c27eSjoerg _dlauxinfo(void)
43852a4c27eSjoerg {
43952a4c27eSjoerg return auxinfo;
44052a4c27eSjoerg }
44152a4c27eSjoerg
44241fe218bScgd /*
44341fe218bScgd * Main entry point for dynamic linking. The argument is the stack
44441fe218bScgd * pointer. The stack is expected to be laid out as described in the
44541fe218bScgd * SVR4 ABI specification, Intel 386 Processor Supplement. Specifically,
44641fe218bScgd * the stack pointer points to a word containing ARGC. Following that
44741fe218bScgd * in the stack is a null-terminated sequence of pointers to argument
44841fe218bScgd * strings. Then comes a null-terminated sequence of pointers to
44941fe218bScgd * environment strings. Finally, there is a sequence of "auxiliary
45041fe218bScgd * vector" entries.
45141fe218bScgd *
4523b033844Skleink * This function returns the entry point for the main program, the dynamic
4533b033844Skleink * linker's exit procedure in sp[0], and a pointer to the main object in
4543b033844Skleink * sp[1].
45541fe218bScgd */
45641fe218bScgd Elf_Addr
_rtld(Elf_Addr * sp,Elf_Addr relocbase)4575f573ab6Sskrll _rtld(Elf_Addr *sp, Elf_Addr relocbase)
45841fe218bScgd {
45926475619Schristos const AuxInfo *pAUX_base, *pAUX_entry, *pAUX_execfd, *pAUX_phdr,
46023c83804Schristos *pAUX_phent, *pAUX_phnum, *pAUX_euid, *pAUX_egid,
46123c83804Schristos *pAUX_ruid, *pAUX_rgid;
462bf9c27b8Sws const AuxInfo *pAUX_pagesz;
46367cc8c08Sjoerg char **env, **oenvp;
46441fe218bScgd const AuxInfo *auxp;
465aad59997Sjoerg Obj_Entry *obj;
46643cfeb27Smycroft Elf_Addr *const osp = sp;
46741fe218bScgd bool bind_now = 0;
46867cc8c08Sjoerg const char *ld_bind_now, *ld_preload, *ld_library_path;
46941fe218bScgd const char **argv;
47040cfdb5eSchristos const char *execname;
47194581370Smycroft long argc;
472a497b0d8Serh const char **real___progname;
473baeb94e0Schristos const Obj_Entry **real___mainprog_obj;
474a497b0d8Serh char ***real_environ;
4758fa65855Sjoerg sigset_t mask;
47667cc8c08Sjoerg #ifdef DEBUG
47767cc8c08Sjoerg const char *ld_debug;
478d72f8197Schristos #endif
47933ee52c7Spooka #ifdef RTLD_DEBUG
48033ee52c7Spooka int i = 0;
48133ee52c7Spooka #endif
48241fe218bScgd
48341fe218bScgd /*
48441fe218bScgd * On entry, the dynamic linker itself has not been relocated yet.
48541fe218bScgd * Be very careful not to reference any global data until after
48641fe218bScgd * _rtld_init has returned. It is OK to reference file-scope statics
48741fe218bScgd * and string constants, and to call static and global functions.
48841fe218bScgd */
48941fe218bScgd /* Find the auxiliary vector on the stack. */
49041fe218bScgd /* first Elf_Word reserved to address of exit routine */
491a3b892d1Smycroft #if defined(RTLD_DEBUG)
49285100284Smycroft debug = 1;
4935505734fSfvdl dbg(("sp = %p, argc = %ld, argv = %p <%s> relocbase %p", sp,
4945505734fSfvdl (long)sp[2], &sp[3], (char *) sp[3], (void *)relocbase));
4955aa71ff5Sskrll #ifndef __x86_64__
49685100284Smycroft dbg(("got is at %p, dynamic is at %p", _GLOBAL_OFFSET_TABLE_,
49785100284Smycroft &_DYNAMIC));
49889c8b652Schristos #endif
4995d1d1ac2Smatt #endif
50041fe218bScgd
50141fe218bScgd sp += 2; /* skip over return argument space */
50241fe218bScgd argv = (const char **) &sp[1];
50394581370Smycroft argc = *(long *)sp;
50443cfeb27Smycroft sp += 2 + argc; /* Skip over argc, arguments, and NULL
50526475619Schristos * terminator */
50641fe218bScgd env = (char **) sp;
50741fe218bScgd while (*sp++ != 0) { /* Skip over environment, and NULL terminator */
508a3b892d1Smycroft #if defined(RTLD_DEBUG)
509272e9594Sjunyoung dbg(("env[%d] = %p %s", i++, (void *)sp[-1], (char *)sp[-1]));
51041fe218bScgd #endif
51141fe218bScgd }
51252a4c27eSjoerg auxinfo = (AuxInfo *) sp;
51341fe218bScgd
51426475619Schristos pAUX_base = pAUX_entry = pAUX_execfd = NULL;
515b8997714Sross pAUX_phdr = pAUX_phent = pAUX_phnum = NULL;
51623c83804Schristos pAUX_euid = pAUX_ruid = pAUX_egid = pAUX_rgid = NULL;
517bf9c27b8Sws pAUX_pagesz = NULL;
51838fd9b8fSpk
51940cfdb5eSchristos execname = NULL;
52040cfdb5eSchristos
521c3ece2a4Smycroft /* Digest the auxiliary vector. */
52252a4c27eSjoerg for (auxp = auxinfo; auxp->a_type != AT_NULL; ++auxp) {
523522cbf02Skleink switch (auxp->a_type) {
524522cbf02Skleink case AT_BASE:
52526475619Schristos pAUX_base = auxp;
52626475619Schristos break;
527522cbf02Skleink case AT_ENTRY:
52826475619Schristos pAUX_entry = auxp;
52926475619Schristos break;
530522cbf02Skleink case AT_EXECFD:
53126475619Schristos pAUX_execfd = auxp;
53226475619Schristos break;
533522cbf02Skleink case AT_PHDR:
53426475619Schristos pAUX_phdr = auxp;
53526475619Schristos break;
536522cbf02Skleink case AT_PHENT:
53726475619Schristos pAUX_phent = auxp;
53826475619Schristos break;
539522cbf02Skleink case AT_PHNUM:
54026475619Schristos pAUX_phnum = auxp;
54126475619Schristos break;
542c59ffb0cSmycroft #ifdef AT_EUID
54323c83804Schristos case AT_EUID:
54423c83804Schristos pAUX_euid = auxp;
54523c83804Schristos break;
54623c83804Schristos case AT_RUID:
54723c83804Schristos pAUX_ruid = auxp;
54823c83804Schristos break;
54923c83804Schristos case AT_EGID:
55023c83804Schristos pAUX_egid = auxp;
55123c83804Schristos break;
55223c83804Schristos case AT_RGID:
55323c83804Schristos pAUX_rgid = auxp;
55423c83804Schristos break;
555c59ffb0cSmycroft #endif
55640cfdb5eSchristos #ifdef AT_SUN_EXECNAME
55740cfdb5eSchristos case AT_SUN_EXECNAME:
55840cfdb5eSchristos execname = (const char *)(const void *)auxp->a_v;
55940cfdb5eSchristos break;
56040cfdb5eSchristos #endif
561522cbf02Skleink case AT_PAGESZ:
562bf9c27b8Sws pAUX_pagesz = auxp;
563bf9c27b8Sws break;
564b8997714Sross }
56541fe218bScgd }
56641fe218bScgd
567c3ece2a4Smycroft /* Initialize and relocate ourselves. */
5688707cd2aSjunyoung if (pAUX_base == NULL) {
5698707cd2aSjunyoung _rtld_error("Bad pAUX_base");
5708707cd2aSjunyoung _rtld_die();
5718707cd2aSjunyoung }
572c3ece2a4Smycroft assert(pAUX_pagesz != NULL);
573522cbf02Skleink _rtld_pagesz = (int)pAUX_pagesz->a_v;
57440cfdb5eSchristos _rtld_init((caddr_t)pAUX_base->a_v, (caddr_t)relocbase, execname);
575bf9c27b8Sws
57641fe218bScgd __progname = _rtld_objself.path;
57741fe218bScgd environ = env;
57841fe218bScgd
57923c83804Schristos _rtld_trust = ((pAUX_euid ? (uid_t)pAUX_euid->a_v : geteuid()) ==
58023c83804Schristos (pAUX_ruid ? (uid_t)pAUX_ruid->a_v : getuid())) &&
58123c83804Schristos ((pAUX_egid ? (gid_t)pAUX_egid->a_v : getegid()) ==
58223c83804Schristos (pAUX_rgid ? (gid_t)pAUX_rgid->a_v : getgid()));
58341fe218bScgd
58467cc8c08Sjoerg #ifdef DEBUG
58567cc8c08Sjoerg ld_debug = NULL;
58667cc8c08Sjoerg #endif
58767cc8c08Sjoerg ld_bind_now = NULL;
58867cc8c08Sjoerg ld_library_path = NULL;
58967cc8c08Sjoerg ld_preload = NULL;
59067cc8c08Sjoerg /*
59167cc8c08Sjoerg * Inline avoid using normal getenv/unsetenv here as the libc
59267cc8c08Sjoerg * code is quite a bit more complicated.
59367cc8c08Sjoerg */
59467cc8c08Sjoerg for (oenvp = env; *env != NULL; ++env) {
59567cc8c08Sjoerg static const char bind_var[] = "LD_BIND_NOW=";
59667cc8c08Sjoerg static const char debug_var[] = "LD_DEBUG=";
59767cc8c08Sjoerg static const char path_var[] = "LD_LIBRARY_PATH=";
59867cc8c08Sjoerg static const char preload_var[] = "LD_PRELOAD=";
59967cc8c08Sjoerg #define LEN(x) (sizeof(x) - 1)
60067cc8c08Sjoerg
60167cc8c08Sjoerg if ((*env)[0] != 'L' || (*env)[1] != 'D') {
60267cc8c08Sjoerg /*
60367cc8c08Sjoerg * Special case to skip most entries without
60467cc8c08Sjoerg * the more expensive calls to strncmp.
60567cc8c08Sjoerg */
60667cc8c08Sjoerg *oenvp++ = *env;
60767cc8c08Sjoerg } else if (strncmp(*env, debug_var, LEN(debug_var)) == 0) {
60867cc8c08Sjoerg if (_rtld_trust) {
60967cc8c08Sjoerg #ifdef DEBUG
61067cc8c08Sjoerg ld_debug = *env + LEN(debug_var);
61167cc8c08Sjoerg #endif
61267cc8c08Sjoerg *oenvp++ = *env;
61367cc8c08Sjoerg }
61467cc8c08Sjoerg } else if (strncmp(*env, bind_var, LEN(bind_var)) == 0) {
61547875e29Sjoerg if (_rtld_trust) {
61667cc8c08Sjoerg ld_bind_now = *env + LEN(bind_var);
61747875e29Sjoerg *oenvp++ = *env;
61847875e29Sjoerg }
61967cc8c08Sjoerg } else if (strncmp(*env, path_var, LEN(path_var)) == 0) {
62067cc8c08Sjoerg if (_rtld_trust) {
62167cc8c08Sjoerg ld_library_path = *env + LEN(path_var);
62267cc8c08Sjoerg *oenvp++ = *env;
62367cc8c08Sjoerg }
62467cc8c08Sjoerg } else if (strncmp(*env, preload_var, LEN(preload_var)) == 0) {
62567cc8c08Sjoerg if (_rtld_trust) {
62667cc8c08Sjoerg ld_preload = *env + LEN(preload_var);
62767cc8c08Sjoerg *oenvp++ = *env;
62867cc8c08Sjoerg }
62967cc8c08Sjoerg } else {
63067cc8c08Sjoerg *oenvp++ = *env;
63167cc8c08Sjoerg }
63267cc8c08Sjoerg #undef LEN
63367cc8c08Sjoerg }
63467cc8c08Sjoerg *oenvp++ = NULL;
63567cc8c08Sjoerg
63641fe218bScgd if (ld_bind_now != NULL && *ld_bind_now != '\0')
63741fe218bScgd bind_now = true;
63841fe218bScgd if (_rtld_trust) {
639082edeccSmhitch #ifdef DEBUG
64085100284Smycroft #ifdef RTLD_DEBUG
64185100284Smycroft debug = 0;
64285100284Smycroft #endif
64341fe218bScgd if (ld_debug != NULL && *ld_debug != '\0')
64441fe218bScgd debug = 1;
645082edeccSmhitch #endif
64667cc8c08Sjoerg _rtld_add_paths(execname, &_rtld_paths, ld_library_path);
647aac13425Schristos } else {
6486f875d32Schristos execname = NULL;
64941fe218bScgd }
65040cfdb5eSchristos _rtld_process_hints(execname, &_rtld_paths, &_rtld_xforms,
65104ee85adSchristos _PATH_LD_HINTS);
652930998f8Sjunyoung dbg(("dynamic linker is initialized, mapbase=%p, relocbase=%p",
6532b2534feSmycroft _rtld_objself.mapbase, _rtld_objself.relocbase));
65441fe218bScgd
65541fe218bScgd /*
65641fe218bScgd * Load the main program, or process its program header if it is
65741fe218bScgd * already loaded.
65841fe218bScgd */
659b8997714Sross if (pAUX_execfd != NULL) { /* Load the main program. */
660522cbf02Skleink int fd = pAUX_execfd->a_v;
6610339fe66Schristos const char *obj_name = argv[0] ? argv[0] : "main program";
66226475619Schristos dbg(("loading main program"));
6630339fe66Schristos _rtld_objmain = _rtld_map_object(obj_name, fd, NULL);
66441fe218bScgd close(fd);
66541fe218bScgd if (_rtld_objmain == NULL)
66641fe218bScgd _rtld_die();
66741fe218bScgd } else { /* Main program already loaded. */
66841fe218bScgd const Elf_Phdr *phdr;
66941fe218bScgd int phnum;
67041fe218bScgd caddr_t entry;
67141fe218bScgd
67226475619Schristos dbg(("processing main program's program header"));
673b8997714Sross assert(pAUX_phdr != NULL);
674522cbf02Skleink phdr = (const Elf_Phdr *) pAUX_phdr->a_v;
675b8997714Sross assert(pAUX_phnum != NULL);
676522cbf02Skleink phnum = pAUX_phnum->a_v;
677b8997714Sross assert(pAUX_phent != NULL);
678522cbf02Skleink assert(pAUX_phent->a_v == sizeof(Elf_Phdr));
679b8997714Sross assert(pAUX_entry != NULL);
680522cbf02Skleink entry = (caddr_t) pAUX_entry->a_v;
68141fe218bScgd _rtld_objmain = _rtld_digest_phdr(phdr, phnum, entry);
6827a48cdb8Smycroft _rtld_objmain->path = xstrdup(argv[0] ? argv[0] :
6837a48cdb8Smycroft "main program");
684fd1f5e8fSjunyoung _rtld_objmain->pathlen = strlen(_rtld_objmain->path);
68541fe218bScgd }
68641fe218bScgd
687f57f7ac0Smycroft _rtld_objmain->mainprog = true;
688f57f7ac0Smycroft
689305c9497Smycroft /*
690305c9497Smycroft * Get the actual dynamic linker pathname from the executable if
691305c9497Smycroft * possible. (It should always be possible.) That ensures that
692f3656ffaSkamil * the debugger will find the right dynamic linker even if a
693f3656ffaSkamil * non-standard one is being used.
694305c9497Smycroft */
695305c9497Smycroft if (_rtld_objmain->interp != NULL &&
69603ee7fc3Sskrll strcmp(_rtld_objmain->interp, _rtld_objself.path) != 0) {
697305c9497Smycroft _rtld_objself.path = xstrdup(_rtld_objmain->interp);
69803ee7fc3Sskrll _rtld_objself.pathlen = strlen(_rtld_objself.path);
69903ee7fc3Sskrll }
700930998f8Sjunyoung dbg(("actual dynamic linker is %s", _rtld_objself.path));
701305c9497Smycroft
70240cfdb5eSchristos _rtld_digest_dynamic(execname, _rtld_objmain);
70341fe218bScgd
70441fe218bScgd /* Link the main program into the list of objects. */
70541fe218bScgd *_rtld_objtail = _rtld_objmain;
70641fe218bScgd _rtld_objtail = &_rtld_objmain->next;
707e6cdac9cSskrll _rtld_objcount++;
708e6cdac9cSskrll _rtld_objloads++;
7097a48cdb8Smycroft
7107a48cdb8Smycroft _rtld_linkmap_add(_rtld_objmain);
71156447538Schristos _rtld_objself.path = xstrdup(_rtld_objself.path);
7127a48cdb8Smycroft _rtld_linkmap_add(&_rtld_objself);
7137a48cdb8Smycroft
71441fe218bScgd ++_rtld_objmain->refcount;
7157a48cdb8Smycroft _rtld_objmain->mainref = 1;
716558a5ffdSad _rtld_objlist_push_tail(&_rtld_list_main, _rtld_objmain);
71741fe218bScgd
71867cc8c08Sjoerg if (ld_preload) {
7198b74e6d7Skleink /*
720aac13425Schristos * Pre-load user-specified objects after the main program
721aac13425Schristos * but before any shared object dependencies.
7228b74e6d7Skleink */
7238b74e6d7Skleink dbg(("preloading objects"));
72467cc8c08Sjoerg if (_rtld_preload(ld_preload) == -1)
7258b74e6d7Skleink _rtld_die();
72667cc8c08Sjoerg }
7278b74e6d7Skleink
72826475619Schristos dbg(("loading needed objects"));
729f1d73a2cSskrll if (_rtld_load_needed_objects(_rtld_objmain, _RTLD_MAIN) == -1)
73041fe218bScgd _rtld_die();
73141fe218bScgd
732c52f9a5dSnonaka dbg(("checking for required versions"));
733c52f9a5dSnonaka for (obj = _rtld_objlist; obj != NULL; obj = obj->next) {
734c52f9a5dSnonaka if (_rtld_verify_object_versions(obj) == -1)
735c52f9a5dSnonaka _rtld_die();
736c52f9a5dSnonaka }
737c52f9a5dSnonaka
738aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
739cc2f98ecSjoerg dbg(("initializing initial Thread Local Storage offsets"));
740aad59997Sjoerg /*
741aad59997Sjoerg * All initial objects get the TLS space from the static block.
742aad59997Sjoerg */
743aad59997Sjoerg for (obj = _rtld_objlist; obj != NULL; obj = obj->next)
744aad59997Sjoerg _rtld_tls_offset_allocate(obj);
745aad59997Sjoerg #endif
746aad59997Sjoerg
74726475619Schristos dbg(("relocating objects"));
7481c495430Smycroft if (_rtld_relocate_objects(_rtld_objmain, bind_now) == -1)
74941fe218bScgd _rtld_die();
75041fe218bScgd
75126475619Schristos dbg(("doing copy relocations"));
752a3b892d1Smycroft if (_rtld_do_copy_relocations(_rtld_objmain) == -1)
75341fe218bScgd _rtld_die();
75441fe218bScgd
755cc2f98ecSjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
756cc2f98ecSjoerg dbg(("initializing Thread Local Storage for main thread"));
757cc2f98ecSjoerg /*
758cc2f98ecSjoerg * Set up TLS area for the main thread.
759cc2f98ecSjoerg * This has to be done after all relocations are processed,
760cc2f98ecSjoerg * since .tdata may contain relocations.
761cc2f98ecSjoerg */
762cc2f98ecSjoerg _rtld_tls_initial_allocation();
763cc2f98ecSjoerg #endif
764cc2f98ecSjoerg
765a497b0d8Serh /*
766baeb94e0Schristos * Set the __progname, environ and, __mainprog_obj before
767baeb94e0Schristos * calling anything that might use them.
768a497b0d8Serh */
769a497b0d8Serh real___progname = _rtld_objmain_sym("__progname");
770a497b0d8Serh if (real___progname) {
7716d0f752aSchristos if (argv[0] != NULL) {
772a497b0d8Serh if ((*real___progname = strrchr(argv[0], '/')) == NULL)
773a497b0d8Serh (*real___progname) = argv[0];
774a497b0d8Serh else
775a497b0d8Serh (*real___progname)++;
7766d0f752aSchristos } else {
7776d0f752aSchristos (*real___progname) = NULL;
7786d0f752aSchristos }
779a497b0d8Serh }
780a497b0d8Serh real_environ = _rtld_objmain_sym("environ");
781a497b0d8Serh if (real_environ)
782a497b0d8Serh *real_environ = environ;
783d9007319Sskrll /*
784d9007319Sskrll * Set __mainprog_obj for old binaries.
785d9007319Sskrll */
786baeb94e0Schristos real___mainprog_obj = _rtld_objmain_sym("__mainprog_obj");
787baeb94e0Schristos if (real___mainprog_obj)
788baeb94e0Schristos *real___mainprog_obj = _rtld_objmain;
789a497b0d8Serh
790f3656ffaSkamil _rtld_debug_state(); /* say hello to the debugger! */
791130ea7cfSjoerg
7928fa65855Sjoerg _rtld_exclusive_enter(&mask);
793cb1cd7e8Sjoerg
79426475619Schristos dbg(("calling _init functions"));
7958fa65855Sjoerg _rtld_call_init_functions(&mask);
79641fe218bScgd
79726475619Schristos dbg(("control at program entry point = %p, obj = %p, exit = %p",
79826475619Schristos _rtld_objmain->entry, _rtld_objmain, _rtld_exit));
79941fe218bScgd
8008fa65855Sjoerg _rtld_exclusive_exit(&mask);
801cb1cd7e8Sjoerg
80226475619Schristos /*
80326475619Schristos * Return with the entry point and the exit procedure in at the top
80426475619Schristos * of stack.
80541fe218bScgd */
80641fe218bScgd
80741fe218bScgd ((void **) osp)[0] = _rtld_exit;
8084312beabSjoerg ((void **) osp)[1] = __UNCONST(_rtld_compat_obj);
80941fe218bScgd return (Elf_Addr) _rtld_objmain->entry;
81041fe218bScgd }
81141fe218bScgd
81241fe218bScgd void
_rtld_die(void)8135f573ab6Sskrll _rtld_die(void)
81441fe218bScgd {
815d9007319Sskrll const char *msg = dlerror();
81641fe218bScgd
81741fe218bScgd if (msg == NULL)
81841fe218bScgd msg = "Fatal error";
819642c31b4Ssoren xerrx(1, "%s", msg);
82041fe218bScgd }
82141fe218bScgd
82241fe218bScgd static Obj_Entry *
_rtld_dlcheck(void * handle)8235f573ab6Sskrll _rtld_dlcheck(void *handle)
82441fe218bScgd {
82541fe218bScgd Obj_Entry *obj;
82641fe218bScgd
82741fe218bScgd for (obj = _rtld_objlist; obj != NULL; obj = obj->next)
82841fe218bScgd if (obj == (Obj_Entry *) handle)
82941fe218bScgd break;
83041fe218bScgd
83141fe218bScgd if (obj == NULL || obj->dl_refcount == 0) {
8326f24a346Sriastradh _rtld_error("Invalid shared object handle %p", handle);
83341fe218bScgd return NULL;
83441fe218bScgd }
83541fe218bScgd return obj;
83641fe218bScgd }
83741fe218bScgd
83841fe218bScgd static void
_rtld_initlist_visit(Objlist * list,Obj_Entry * obj,int rev)839558a5ffdSad _rtld_initlist_visit(Objlist* list, Obj_Entry *obj, int rev)
840558a5ffdSad {
841558a5ffdSad Needed_Entry* elm;
842558a5ffdSad
843558a5ffdSad /* dbg(("_rtld_initlist_visit(%s)", obj->path)); */
844558a5ffdSad
845558a5ffdSad if (obj->init_done)
846558a5ffdSad return;
847558a5ffdSad obj->init_done = 1;
848558a5ffdSad
849558a5ffdSad for (elm = obj->needed; elm != NULL; elm = elm->next) {
850558a5ffdSad if (elm->obj != NULL) {
851558a5ffdSad _rtld_initlist_visit(list, elm->obj, rev);
852558a5ffdSad }
853558a5ffdSad }
854558a5ffdSad
855558a5ffdSad if (rev) {
856558a5ffdSad _rtld_objlist_push_head(list, obj);
857558a5ffdSad } else {
858558a5ffdSad _rtld_objlist_push_tail(list, obj);
859558a5ffdSad }
860558a5ffdSad }
861558a5ffdSad
862558a5ffdSad static void
_rtld_initlist_tsort(Objlist * list,int rev)863558a5ffdSad _rtld_initlist_tsort(Objlist* list, int rev)
864558a5ffdSad {
865558a5ffdSad dbg(("_rtld_initlist_tsort"));
866558a5ffdSad
867558a5ffdSad Obj_Entry* obj;
868558a5ffdSad
869ba044c63Schristos /*
870ba044c63Schristos * We don't include objmain here (starting from next)
871ba044c63Schristos * because csu handles it
872ba044c63Schristos */
873558a5ffdSad for (obj = _rtld_objlist->next; obj; obj = obj->next) {
874558a5ffdSad obj->init_done = 0;
875558a5ffdSad }
876558a5ffdSad
877558a5ffdSad for (obj = _rtld_objlist->next; obj; obj = obj->next) {
878558a5ffdSad _rtld_initlist_visit(list, obj, rev);
879558a5ffdSad }
880558a5ffdSad }
881558a5ffdSad
882558a5ffdSad static void
_rtld_init_dag(Obj_Entry * root)8835f573ab6Sskrll _rtld_init_dag(Obj_Entry *root)
884305c9497Smycroft {
885a9f5b3f8Ssimonb
886305c9497Smycroft _rtld_init_dag1(root, root);
887305c9497Smycroft }
888305c9497Smycroft
889305c9497Smycroft static void
_rtld_init_dag1(Obj_Entry * root,Obj_Entry * obj)8905f573ab6Sskrll _rtld_init_dag1(Obj_Entry *root, Obj_Entry *obj)
891305c9497Smycroft {
892305c9497Smycroft const Needed_Entry *needed;
893305c9497Smycroft
8947a48cdb8Smycroft if (!obj->mainref) {
8957a48cdb8Smycroft if (_rtld_objlist_find(&obj->dldags, root))
8967a48cdb8Smycroft return;
897126c5893Sskrll dbg(("add %p (%s) to %p (%s) DAG", obj, obj->path, root,
8987a48cdb8Smycroft root->path));
899558a5ffdSad _rtld_objlist_push_tail(&obj->dldags, root);
900558a5ffdSad _rtld_objlist_push_tail(&root->dagmembers, obj);
9017a48cdb8Smycroft }
902305c9497Smycroft for (needed = obj->needed; needed != NULL; needed = needed->next)
903305c9497Smycroft if (needed->obj != NULL)
904305c9497Smycroft _rtld_init_dag1(root, needed->obj);
905305c9497Smycroft }
906305c9497Smycroft
907305c9497Smycroft /*
908305c9497Smycroft * Note, this is called only for objects loaded by dlopen().
909305c9497Smycroft */
910305c9497Smycroft static void
_rtld_unload_object(sigset_t * mask,Obj_Entry * root,bool do_fini_funcs)9118fa65855Sjoerg _rtld_unload_object(sigset_t *mask, Obj_Entry *root, bool do_fini_funcs)
912305c9497Smycroft {
913a9f5b3f8Ssimonb
914305c9497Smycroft _rtld_unref_dag(root);
915305c9497Smycroft if (root->refcount == 0) { /* We are finished with some objects. */
916305c9497Smycroft Obj_Entry *obj;
917305c9497Smycroft Obj_Entry **linkp;
918305c9497Smycroft Objlist_Entry *elm;
919305c9497Smycroft
920305c9497Smycroft /* Finalize objects that are about to be unmapped. */
921305c9497Smycroft if (do_fini_funcs)
9228fa65855Sjoerg _rtld_call_fini_functions(mask, 0);
923305c9497Smycroft
924305c9497Smycroft /* Remove the DAG from all objects' DAG lists. */
92506de4264Slukem SIMPLEQ_FOREACH(elm, &root->dagmembers, link)
926305c9497Smycroft _rtld_objlist_remove(&elm->obj->dldags, root);
927305c9497Smycroft
928305c9497Smycroft /* Remove the DAG from the RTLD_GLOBAL list. */
9297a48cdb8Smycroft if (root->globalref) {
9307a48cdb8Smycroft root->globalref = 0;
931305c9497Smycroft _rtld_objlist_remove(&_rtld_list_global, root);
9327a48cdb8Smycroft }
933305c9497Smycroft
934305c9497Smycroft /* Unmap all objects that are no longer referenced. */
935305c9497Smycroft linkp = &_rtld_objlist->next;
936305c9497Smycroft while ((obj = *linkp) != NULL) {
937305c9497Smycroft if (obj->refcount == 0) {
938305c9497Smycroft dbg(("unloading \"%s\"", obj->path));
93953c5ea5dSad if (obj->ehdr != MAP_FAILED)
94053c5ea5dSad munmap(obj->ehdr, _rtld_pagesz);
941305c9497Smycroft munmap(obj->mapbase, obj->mapsize);
942f8ad0818Schristos _rtld_objlist_remove(&_rtld_list_global, obj);
943305c9497Smycroft _rtld_linkmap_delete(obj);
944305c9497Smycroft *linkp = obj->next;
945b02ec7e9Sroy _rtld_objcount--;
946305c9497Smycroft _rtld_obj_free(obj);
947305c9497Smycroft } else
948305c9497Smycroft linkp = &obj->next;
949305c9497Smycroft }
950305c9497Smycroft _rtld_objtail = linkp;
951305c9497Smycroft }
952305c9497Smycroft }
953305c9497Smycroft
954f1d73a2cSskrll void
_rtld_ref_dag(Obj_Entry * root)955f1d73a2cSskrll _rtld_ref_dag(Obj_Entry *root)
956f1d73a2cSskrll {
957f1d73a2cSskrll const Needed_Entry *needed;
958f1d73a2cSskrll
959f1d73a2cSskrll assert(root);
960f1d73a2cSskrll
961f1d73a2cSskrll ++root->refcount;
962f1d73a2cSskrll
963f1d73a2cSskrll dbg(("incremented reference on \"%s\" (%d)", root->path,
964f1d73a2cSskrll root->refcount));
965f1d73a2cSskrll for (needed = root->needed; needed != NULL;
966f1d73a2cSskrll needed = needed->next) {
967f1d73a2cSskrll if (needed->obj != NULL)
968f1d73a2cSskrll _rtld_ref_dag(needed->obj);
969f1d73a2cSskrll }
970f1d73a2cSskrll }
971f1d73a2cSskrll
972305c9497Smycroft static void
_rtld_unref_dag(Obj_Entry * root)9735f573ab6Sskrll _rtld_unref_dag(Obj_Entry *root)
97441fe218bScgd {
975a9f5b3f8Ssimonb
9762a05365bSjdolecek assert(root);
97706d16edeSjdolecek assert(root->refcount != 0);
978f1d73a2cSskrll
97941fe218bScgd --root->refcount;
980f1d73a2cSskrll dbg(("decremented reference on \"%s\" (%d)", root->path,
981f1d73a2cSskrll root->refcount));
982f1d73a2cSskrll
98341fe218bScgd if (root->refcount == 0) {
98441fe218bScgd const Needed_Entry *needed;
98541fe218bScgd
98626475619Schristos for (needed = root->needed; needed != NULL;
9872a05365bSjdolecek needed = needed->next) {
9882a05365bSjdolecek if (needed->obj != NULL)
989305c9497Smycroft _rtld_unref_dag(needed->obj);
99041fe218bScgd }
99141fe218bScgd }
9922a05365bSjdolecek }
99341fe218bScgd
__strong_alias(__dlclose,dlclose)9942a63e040Sthorpej __strong_alias(__dlclose,dlclose)
99541fe218bScgd int
996d9007319Sskrll dlclose(void *handle)
99741fe218bScgd {
9980bc9be21Sjoerg Obj_Entry *root;
9998fa65855Sjoerg sigset_t mask;
100041fe218bScgd
10012feaef06Sjoerg dbg(("dlclose of %p", handle));
10020bc9be21Sjoerg
10038fa65855Sjoerg _rtld_exclusive_enter(&mask);
10040bc9be21Sjoerg
10050bc9be21Sjoerg root = _rtld_dlcheck(handle);
10060bc9be21Sjoerg
10070bc9be21Sjoerg if (root == NULL) {
10088fa65855Sjoerg _rtld_exclusive_exit(&mask);
100941fe218bScgd return -1;
10100bc9be21Sjoerg }
101141fe218bScgd
101241fe218bScgd _rtld_debug.r_state = RT_DELETE;
101341fe218bScgd _rtld_debug_state();
101441fe218bScgd
101541fe218bScgd --root->dl_refcount;
10168fa65855Sjoerg _rtld_unload_object(&mask, root, true);
101741fe218bScgd
101841fe218bScgd _rtld_debug.r_state = RT_CONSISTENT;
101941fe218bScgd _rtld_debug_state();
102041fe218bScgd
10218fa65855Sjoerg _rtld_exclusive_exit(&mask);
10220bc9be21Sjoerg
102341fe218bScgd return 0;
102441fe218bScgd }
102541fe218bScgd
__strong_alias(__dlerror,dlerror)10262a63e040Sthorpej __strong_alias(__dlerror,dlerror)
102741fe218bScgd char *
1028d9007319Sskrll dlerror(void)
102941fe218bScgd {
103041fe218bScgd char *msg = error_message;
1031a9f5b3f8Ssimonb
103241fe218bScgd error_message = NULL;
103341fe218bScgd return msg;
103441fe218bScgd }
103541fe218bScgd
__strong_alias(__dlopen,dlopen)10362a63e040Sthorpej __strong_alias(__dlopen,dlopen)
103741fe218bScgd void *
1038d9007319Sskrll dlopen(const char *name, int mode)
103941fe218bScgd {
1040b8bbdc68Sriastradh Obj_Entry **old_obj_tail;
104141fe218bScgd Obj_Entry *obj = NULL;
1042f1d73a2cSskrll int flags = _RTLD_DLOPEN;
1043f1d73a2cSskrll bool nodelete;
1044f1d73a2cSskrll bool now;
10458fa65855Sjoerg sigset_t mask;
1046c52f9a5dSnonaka int result;
1047f1d73a2cSskrll
10481c559787Sriastradh dbg(("dlopen of %s 0x%x", name, mode));
10492feaef06Sjoerg
10508fa65855Sjoerg _rtld_exclusive_enter(&mask);
1051cb1cd7e8Sjoerg
1052b8bbdc68Sriastradh old_obj_tail = _rtld_objtail;
1053b8bbdc68Sriastradh
1054f1d73a2cSskrll flags |= (mode & RTLD_GLOBAL) ? _RTLD_GLOBAL : 0;
1055f1d73a2cSskrll flags |= (mode & RTLD_NOLOAD) ? _RTLD_NOLOAD : 0;
1056f1d73a2cSskrll
1057f1d73a2cSskrll nodelete = (mode & RTLD_NODELETE) ? true : false;
1058f1d73a2cSskrll now = ((mode & RTLD_MODEMASK) == RTLD_NOW) ? true : false;
105941fe218bScgd
106041fe218bScgd _rtld_debug.r_state = RT_ADD;
106141fe218bScgd _rtld_debug_state();
106241fe218bScgd
106341fe218bScgd if (name == NULL) {
106441fe218bScgd obj = _rtld_objmain;
1065305c9497Smycroft obj->refcount++;
106686103e2fSmycroft } else
1067f1d73a2cSskrll obj = _rtld_load_library(name, _rtld_objmain, flags);
1068f1d73a2cSskrll
106941fe218bScgd
107041fe218bScgd if (obj != NULL) {
107141fe218bScgd ++obj->dl_refcount;
107241fe218bScgd if (*old_obj_tail != NULL) { /* We loaded something new. */
107341fe218bScgd assert(*old_obj_tail == obj);
107441fe218bScgd
1075c52f9a5dSnonaka result = _rtld_load_needed_objects(obj, flags);
1076c52f9a5dSnonaka if (result != -1) {
1077c52f9a5dSnonaka Objlist_Entry *entry;
1078c52f9a5dSnonaka _rtld_init_dag(obj);
1079c52f9a5dSnonaka SIMPLEQ_FOREACH(entry, &obj->dagmembers, link) {
1080c52f9a5dSnonaka result = _rtld_verify_object_versions(entry->obj);
1081c52f9a5dSnonaka if (result == -1)
1082c52f9a5dSnonaka break;
1083c52f9a5dSnonaka }
1084c52f9a5dSnonaka }
1085c52f9a5dSnonaka if (result == -1 || _rtld_relocate_objects(obj,
1086c52f9a5dSnonaka (now || obj->z_now)) == -1) {
10878fa65855Sjoerg _rtld_unload_object(&mask, obj, false);
1088305c9497Smycroft obj->dl_refcount--;
108941fe218bScgd obj = NULL;
1090558a5ffdSad } else {
10918fa65855Sjoerg _rtld_call_init_functions(&mask);
1092558a5ffdSad }
109341fe218bScgd }
1094f1d73a2cSskrll if (obj != NULL) {
1095f1d73a2cSskrll if ((nodelete || obj->z_nodelete) && !obj->ref_nodel) {
1096f1d73a2cSskrll dbg(("dlopen obj %s nodelete", obj->path));
1097f1d73a2cSskrll _rtld_ref_dag(obj);
1098f1d73a2cSskrll obj->z_nodelete = obj->ref_nodel = true;
1099f1d73a2cSskrll }
1100f1d73a2cSskrll }
110141fe218bScgd }
110241fe218bScgd _rtld_debug.r_state = RT_CONSISTENT;
110341fe218bScgd _rtld_debug_state();
110441fe218bScgd
11051c559787Sriastradh dbg(("dlopen of %s 0x%x returned %p%s%s%s", name, mode, obj,
11061c559787Sriastradh obj ? "" : " (", obj ? "" : error_message, obj ? "" : ")"));
11071c559787Sriastradh
11088fa65855Sjoerg _rtld_exclusive_exit(&mask);
1109cb1cd7e8Sjoerg
111041fe218bScgd return obj;
111141fe218bScgd }
111241fe218bScgd
1113a497b0d8Serh /*
1114a497b0d8Serh * Find a symbol in the main program.
1115a497b0d8Serh */
1116a497b0d8Serh void *
_rtld_objmain_sym(const char * name)11175f573ab6Sskrll _rtld_objmain_sym(const char *name)
1118a497b0d8Serh {
11194e9bea3dSkamil Elf_Hash hash;
1120a497b0d8Serh const Elf_Sym *def;
1121a497b0d8Serh const Obj_Entry *obj;
1122b02ec7e9Sroy DoneList donelist;
1123a497b0d8Serh
11244e9bea3dSkamil hash.sysv = _rtld_sysv_hash(name);
11254e9bea3dSkamil hash.gnu = _rtld_gnu_hash(name);
1126a497b0d8Serh obj = _rtld_objmain;
1127b02ec7e9Sroy _rtld_donelist_init(&donelist);
1128a497b0d8Serh
11294e9bea3dSkamil def = _rtld_symlook_list(name, &hash, &_rtld_list_main, &obj, 0,
1130c52f9a5dSnonaka NULL, &donelist);
1131b02ec7e9Sroy
1132a497b0d8Serh if (def != NULL)
1133a497b0d8Serh return obj->relocbase + def->st_value;
11341f45aab7Sskrll return NULL;
1135a497b0d8Serh }
1136a497b0d8Serh
1137130482d9Sjoerg #if defined(__powerpc__) && !defined(__clang__)
11381c75bf6fSchs static __noinline void *
hackish_return_address(void)1139982ae38fSmacallan hackish_return_address(void)
1140982ae38fSmacallan {
1141db546b38Sjakllsch #if __GNUC_PREREQ__(6,0)
11421bf47496Smrg #pragma GCC diagnostic push
11431bf47496Smrg #pragma GCC diagnostic ignored "-Wframe-address"
1144db546b38Sjakllsch #endif
1145982ae38fSmacallan return __builtin_return_address(1);
1146db546b38Sjakllsch #if __GNUC_PREREQ__(6,0)
11471bf47496Smrg #pragma GCC diagnostic pop
1148db546b38Sjakllsch #endif
1149982ae38fSmacallan }
1150982ae38fSmacallan #endif
1151982ae38fSmacallan
1152cb1cd7e8Sjoerg #ifdef __HAVE_FUNCTION_DESCRIPTORS
11538fa65855Sjoerg #define lookup_mutex_enter() _rtld_exclusive_enter(&mask)
11548fa65855Sjoerg #define lookup_mutex_exit() _rtld_exclusive_exit(&mask)
1155cb1cd7e8Sjoerg #else
1156cb1cd7e8Sjoerg #define lookup_mutex_enter() _rtld_shared_enter()
1157cb1cd7e8Sjoerg #define lookup_mutex_exit() _rtld_shared_exit()
1158cb1cd7e8Sjoerg #endif
1159cb1cd7e8Sjoerg
1160c52f9a5dSnonaka static void *
do_dlsym(void * handle,const char * name,const Ver_Entry * ventry,void * retaddr)11615b3faf1cSjoerg do_dlsym(void *handle, const char *name, const Ver_Entry *ventry, void *retaddr)
116241fe218bScgd {
1163305c9497Smycroft const Obj_Entry *obj;
11644e9bea3dSkamil Elf_Hash hash;
116541fe218bScgd const Elf_Sym *def;
116641fe218bScgd const Obj_Entry *defobj;
1167b02ec7e9Sroy DoneList donelist;
1168c52f9a5dSnonaka const u_int flags = SYMLOOK_DLSYM | SYMLOOK_IN_PLT;
11698fa65855Sjoerg #ifdef __HAVE_FUNCTION_DESCRIPTORS
11708fa65855Sjoerg sigset_t mask;
11718fa65855Sjoerg #endif
117241fe218bScgd
1173cb1cd7e8Sjoerg lookup_mutex_enter();
1174cb1cd7e8Sjoerg
11754e9bea3dSkamil hash.sysv = _rtld_sysv_hash(name);
11764e9bea3dSkamil hash.gnu = _rtld_gnu_hash(name);
1177f57f7ac0Smycroft def = NULL;
1178f57f7ac0Smycroft defobj = NULL;
1179f57f7ac0Smycroft
118068309260Schristos switch ((intptr_t)handle) {
118168309260Schristos case (intptr_t)NULL:
118268309260Schristos case (intptr_t)RTLD_NEXT:
118368309260Schristos case (intptr_t)RTLD_DEFAULT:
118468309260Schristos case (intptr_t)RTLD_SELF:
1185305c9497Smycroft if ((obj = _rtld_obj_from_addr(retaddr)) == NULL) {
1186305c9497Smycroft _rtld_error("Cannot determine caller's shared object");
1187cb1cd7e8Sjoerg lookup_mutex_exit();
1188305c9497Smycroft return NULL;
1189305c9497Smycroft }
119068309260Schristos
119168309260Schristos switch ((intptr_t)handle) {
119268309260Schristos case (intptr_t)NULL: /* Just the caller's shared object. */
11934e9bea3dSkamil def = _rtld_symlook_obj(name, &hash, obj, flags, ventry);
1194f57f7ac0Smycroft defobj = obj;
119568309260Schristos break;
119668309260Schristos
119768309260Schristos case (intptr_t)RTLD_NEXT: /* Objects after callers */
119868309260Schristos obj = obj->next;
119968309260Schristos /*FALLTHROUGH*/
120068309260Schristos
120168309260Schristos case (intptr_t)RTLD_SELF: /* Caller included */
120268309260Schristos for (; obj; obj = obj->next) {
12034e9bea3dSkamil if ((def = _rtld_symlook_obj(name, &hash, obj,
1204c52f9a5dSnonaka flags, ventry)) != NULL) {
1205f57f7ac0Smycroft defobj = obj;
1206f57f7ac0Smycroft break;
1207f57f7ac0Smycroft }
1208f57f7ac0Smycroft }
1209d32757ceSchristos /*
12101abd6678Schristos * Search the dynamic linker itself, and possibly
12111abd6678Schristos * resolve the symbol from there if it is not defined
1212d32757ceSchristos * already or weak. This is how the application links
1213f9f702d0Schristos * to dynamic linker services such as dlopen.
1214d32757ceSchristos */
1215d32757ceSchristos if (!def || ELF_ST_BIND(def->st_info) == STB_WEAK) {
1216d32757ceSchristos const Elf_Sym *symp = _rtld_symlook_obj(name,
12174e9bea3dSkamil &hash, &_rtld_objself, flags, ventry);
1218f9f702d0Schristos if (symp != NULL) {
1219d32757ceSchristos def = symp;
1220d32757ceSchristos defobj = &_rtld_objself;
1221d32757ceSchristos }
1222d32757ceSchristos }
122368309260Schristos break;
122468309260Schristos
122568309260Schristos case (intptr_t)RTLD_DEFAULT:
12264e9bea3dSkamil def = _rtld_symlook_default(name, &hash, obj, &defobj,
1227c52f9a5dSnonaka flags, ventry);
122868309260Schristos break;
122968309260Schristos
123068309260Schristos default:
123168309260Schristos abort();
1232f57f7ac0Smycroft }
123368309260Schristos break;
123468309260Schristos
123568309260Schristos default:
1236cb1cd7e8Sjoerg if ((obj = _rtld_dlcheck(handle)) == NULL) {
1237cb1cd7e8Sjoerg lookup_mutex_exit();
123841fe218bScgd return NULL;
1239cb1cd7e8Sjoerg }
124041fe218bScgd
1241b02ec7e9Sroy _rtld_donelist_init(&donelist);
1242b02ec7e9Sroy
1243f57f7ac0Smycroft if (obj->mainprog) {
124468309260Schristos /* Search main program and all libraries loaded by it */
12454e9bea3dSkamil def = _rtld_symlook_list(name, &hash, &_rtld_list_main,
1246c52f9a5dSnonaka &defobj, flags, ventry, &donelist);
1247f57f7ac0Smycroft } else {
12488c9056adSskrll Needed_Entry fake;
1249b02ec7e9Sroy DoneList depth;
12508c9056adSskrll
12518c9056adSskrll /* Search the object and all the libraries loaded by it. */
12528c9056adSskrll fake.next = NULL;
1253fa64a5bfSchristos fake.obj = __UNCONST(obj);
12548c9056adSskrll fake.name = 0;
1255b02ec7e9Sroy
1256b02ec7e9Sroy _rtld_donelist_init(&depth);
12574e9bea3dSkamil def = _rtld_symlook_needed(name, &hash, &fake, &defobj,
1258c52f9a5dSnonaka flags, ventry, &donelist, &depth);
1259f57f7ac0Smycroft }
1260b02ec7e9Sroy
126168309260Schristos break;
1262f57f7ac0Smycroft }
1263305c9497Smycroft
1264e4526bd9Sfredette if (def != NULL) {
1265cb1cd7e8Sjoerg void *p;
12667a1a6f1cSjoerg
12677a1a6f1cSjoerg if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
12687a1a6f1cSjoerg #ifdef __HAVE_FUNCTION_DESCRIPTORS
12697a1a6f1cSjoerg lookup_mutex_exit();
12707a1a6f1cSjoerg _rtld_shared_enter();
12717a1a6f1cSjoerg #endif
12727a1a6f1cSjoerg p = (void *)_rtld_resolve_ifunc(defobj, def);
12737a1a6f1cSjoerg _rtld_shared_exit();
12747a1a6f1cSjoerg return p;
12757a1a6f1cSjoerg }
12767a1a6f1cSjoerg
1277e4526bd9Sfredette #ifdef __HAVE_FUNCTION_DESCRIPTORS
1278cb1cd7e8Sjoerg if (ELF_ST_TYPE(def->st_info) == STT_FUNC) {
1279d5a77cf1Sjoerg p = (void *)_rtld_function_descriptor_alloc(defobj,
1280d5a77cf1Sjoerg def, 0);
1281cb1cd7e8Sjoerg lookup_mutex_exit();
1282cb1cd7e8Sjoerg return p;
1283cb1cd7e8Sjoerg }
1284e4526bd9Sfredette #endif /* __HAVE_FUNCTION_DESCRIPTORS */
1285cb1cd7e8Sjoerg p = defobj->relocbase + def->st_value;
1286cb1cd7e8Sjoerg lookup_mutex_exit();
1287cb1cd7e8Sjoerg return p;
1288f57f7ac0Smycroft }
1289f57f7ac0Smycroft
12905bd33bcaSmycroft _rtld_error("Undefined symbol \"%s\"", name);
1291cb1cd7e8Sjoerg lookup_mutex_exit();
129241fe218bScgd return NULL;
129341fe218bScgd }
129441fe218bScgd
__strong_alias(__dlsym,dlsym)1295c52f9a5dSnonaka __strong_alias(__dlsym,dlsym)
1296c52f9a5dSnonaka void *
1297c52f9a5dSnonaka dlsym(void *handle, const char *name)
1298c52f9a5dSnonaka {
12995b3faf1cSjoerg void *retaddr;
1300c52f9a5dSnonaka
1301c52f9a5dSnonaka dbg(("dlsym of %s in %p", name, handle));
1302c52f9a5dSnonaka
1303130482d9Sjoerg #if defined(__powerpc__) && !defined(__clang__)
13045b3faf1cSjoerg retaddr = hackish_return_address();
13055b3faf1cSjoerg #else
13065b3faf1cSjoerg retaddr = __builtin_return_address(0);
13075b3faf1cSjoerg #endif
13085b3faf1cSjoerg return do_dlsym(handle, name, NULL, retaddr);
1309c52f9a5dSnonaka }
1310c52f9a5dSnonaka
__strong_alias(__dlvsym,dlvsym)1311c52f9a5dSnonaka __strong_alias(__dlvsym,dlvsym)
1312c52f9a5dSnonaka void *
1313c52f9a5dSnonaka dlvsym(void *handle, const char *name, const char *version)
1314c52f9a5dSnonaka {
1315c52f9a5dSnonaka Ver_Entry *ventry = NULL;
1316c52f9a5dSnonaka Ver_Entry ver_entry;
13175b3faf1cSjoerg void *retaddr;
1318c52f9a5dSnonaka
1319c52f9a5dSnonaka dbg(("dlvsym of %s@%s in %p", name, version ? version : NULL, handle));
1320c52f9a5dSnonaka
1321c52f9a5dSnonaka if (version != NULL) {
1322c52f9a5dSnonaka ver_entry.name = version;
1323c52f9a5dSnonaka ver_entry.file = NULL;
13244e9bea3dSkamil ver_entry.hash = _rtld_sysv_hash(version);
1325c52f9a5dSnonaka ver_entry.flags = 0;
1326c52f9a5dSnonaka ventry = &ver_entry;
1327c52f9a5dSnonaka }
1328130482d9Sjoerg #if defined(__powerpc__) && !defined(__clang__)
13295b3faf1cSjoerg retaddr = hackish_return_address();
13305b3faf1cSjoerg #else
13315b3faf1cSjoerg retaddr = __builtin_return_address(0);
13325b3faf1cSjoerg #endif
13335b3faf1cSjoerg return do_dlsym(handle, name, ventry, retaddr);
1334c52f9a5dSnonaka }
1335c52f9a5dSnonaka
__strong_alias(__dladdr,dladdr)13362a63e040Sthorpej __strong_alias(__dladdr,dladdr)
1337305c9497Smycroft int
1338d9007319Sskrll dladdr(const void *addr, Dl_info *info)
1339305c9497Smycroft {
1340305c9497Smycroft const Obj_Entry *obj;
1341e4526bd9Sfredette const Elf_Sym *def, *best_def;
1342305c9497Smycroft void *symbol_addr;
1343305c9497Smycroft unsigned long symoffset;
1344241421a0Sjoerg #ifdef __HAVE_FUNCTION_DESCRIPTORS
1345241421a0Sjoerg sigset_t mask;
1346241421a0Sjoerg #endif
1347305c9497Smycroft
13482feaef06Sjoerg dbg(("dladdr of %p", addr));
13492feaef06Sjoerg
1350cb1cd7e8Sjoerg lookup_mutex_enter();
1351cb1cd7e8Sjoerg
1352e4526bd9Sfredette #ifdef __HAVE_FUNCTION_DESCRIPTORS
1353e4526bd9Sfredette addr = _rtld_function_descriptor_function(addr);
1354e4526bd9Sfredette #endif /* __HAVE_FUNCTION_DESCRIPTORS */
1355e4526bd9Sfredette
1356305c9497Smycroft obj = _rtld_obj_from_addr(addr);
1357305c9497Smycroft if (obj == NULL) {
1358305c9497Smycroft _rtld_error("No shared object contains address");
1359e0985a06Sjoerg lookup_mutex_exit();
1360305c9497Smycroft return 0;
1361305c9497Smycroft }
1362305c9497Smycroft info->dli_fname = obj->path;
1363305c9497Smycroft info->dli_fbase = obj->mapbase;
1364305c9497Smycroft info->dli_saddr = (void *)0;
1365305c9497Smycroft info->dli_sname = NULL;
1366305c9497Smycroft
1367305c9497Smycroft /*
1368305c9497Smycroft * Walk the symbol list looking for the symbol whose address is
1369305c9497Smycroft * closest to the address sent in.
1370305c9497Smycroft */
1371e4526bd9Sfredette best_def = NULL;
1372305c9497Smycroft for (symoffset = 0; symoffset < obj->nchains; symoffset++) {
1373305c9497Smycroft def = obj->symtab + symoffset;
1374305c9497Smycroft
1375305c9497Smycroft /*
1376305c9497Smycroft * For skip the symbol if st_shndx is either SHN_UNDEF or
1377305c9497Smycroft * SHN_COMMON.
1378305c9497Smycroft */
1379305c9497Smycroft if (def->st_shndx == SHN_UNDEF || def->st_shndx == SHN_COMMON)
1380305c9497Smycroft continue;
1381305c9497Smycroft
1382305c9497Smycroft /*
1383305c9497Smycroft * If the symbol is greater than the specified address, or if it
1384305c9497Smycroft * is further away from addr than the current nearest symbol,
1385305c9497Smycroft * then reject it.
1386305c9497Smycroft */
1387305c9497Smycroft symbol_addr = obj->relocbase + def->st_value;
1388305c9497Smycroft if (symbol_addr > addr || symbol_addr < info->dli_saddr)
1389305c9497Smycroft continue;
1390305c9497Smycroft
1391305c9497Smycroft /* Update our idea of the nearest symbol. */
1392305c9497Smycroft info->dli_sname = obj->strtab + def->st_name;
1393305c9497Smycroft info->dli_saddr = symbol_addr;
1394e4526bd9Sfredette best_def = def;
1395305c9497Smycroft
1396193b880fSchristos
1397305c9497Smycroft /* Exact match? */
1398305c9497Smycroft if (info->dli_saddr == addr)
1399305c9497Smycroft break;
1400305c9497Smycroft }
1401e4526bd9Sfredette
1402e4526bd9Sfredette #ifdef __HAVE_FUNCTION_DESCRIPTORS
1403e4526bd9Sfredette if (best_def != NULL && ELF_ST_TYPE(best_def->st_info) == STT_FUNC)
1404e4526bd9Sfredette info->dli_saddr = (void *)_rtld_function_descriptor_alloc(obj,
1405e4526bd9Sfredette best_def, 0);
1406193b880fSchristos #else
1407193b880fSchristos __USE(best_def);
1408e4526bd9Sfredette #endif /* __HAVE_FUNCTION_DESCRIPTORS */
1409e4526bd9Sfredette
1410cb1cd7e8Sjoerg lookup_mutex_exit();
1411305c9497Smycroft return 1;
1412305c9497Smycroft }
1413305c9497Smycroft
__strong_alias(__dlinfo,dlinfo)14144c1e54d8Spooka __strong_alias(__dlinfo,dlinfo)
14154c1e54d8Spooka int
14164c1e54d8Spooka dlinfo(void *handle, int req, void *v)
14174c1e54d8Spooka {
14184c1e54d8Spooka const Obj_Entry *obj;
14194c1e54d8Spooka void *retaddr;
14204c1e54d8Spooka
14212feaef06Sjoerg dbg(("dlinfo for %p %d", handle, req));
14222feaef06Sjoerg
1423cb1cd7e8Sjoerg _rtld_shared_enter();
1424cb1cd7e8Sjoerg
14254c1e54d8Spooka if (handle == RTLD_SELF) {
1426130482d9Sjoerg #if defined(__powerpc__) && !defined(__clang__)
14274c1e54d8Spooka retaddr = hackish_return_address();
14284c1e54d8Spooka #else
14294c1e54d8Spooka retaddr = __builtin_return_address(0);
14304c1e54d8Spooka #endif
14314c1e54d8Spooka if ((obj = _rtld_obj_from_addr(retaddr)) == NULL) {
14324c1e54d8Spooka _rtld_error("Cannot determine caller's shared object");
1433cb1cd7e8Sjoerg _rtld_shared_exit();
14344c1e54d8Spooka return -1;
14354c1e54d8Spooka }
14364c1e54d8Spooka } else {
14374c1e54d8Spooka if ((obj = _rtld_dlcheck(handle)) == NULL) {
1438cb1cd7e8Sjoerg _rtld_shared_exit();
14394c1e54d8Spooka return -1;
14404c1e54d8Spooka }
14414c1e54d8Spooka }
14424c1e54d8Spooka
14434c1e54d8Spooka switch (req) {
14444c1e54d8Spooka case RTLD_DI_LINKMAP:
14454c1e54d8Spooka {
14464c1e54d8Spooka const struct link_map **map = v;
14474c1e54d8Spooka
14484c1e54d8Spooka *map = &obj->linkmap;
14494c1e54d8Spooka break;
14504c1e54d8Spooka }
14514c1e54d8Spooka
14524c1e54d8Spooka default:
14534c1e54d8Spooka _rtld_error("Invalid request");
1454cb1cd7e8Sjoerg _rtld_shared_exit();
14554c1e54d8Spooka return -1;
14564c1e54d8Spooka }
14574c1e54d8Spooka
1458cb1cd7e8Sjoerg _rtld_shared_exit();
14594c1e54d8Spooka return 0;
14604c1e54d8Spooka }
14614c1e54d8Spooka
14623b610a6eSkamil static void
_rtld_fill_dl_phdr_info(const Obj_Entry * obj,struct dl_phdr_info * phdr_info)14633b610a6eSkamil _rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info)
14643b610a6eSkamil {
14653b610a6eSkamil
14663b610a6eSkamil phdr_info->dlpi_addr = (Elf_Addr)obj->relocbase;
14673b610a6eSkamil /* XXX: wrong but not fixing it yet */
14683b610a6eSkamil phdr_info->dlpi_name = obj->path;
14693b610a6eSkamil phdr_info->dlpi_phdr = obj->phdr;
14703b610a6eSkamil phdr_info->dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]);
14713b610a6eSkamil #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II)
14723b610a6eSkamil phdr_info->dlpi_tls_modid = obj->tlsindex;
14733b610a6eSkamil phdr_info->dlpi_tls_data = obj->tlsinit;
14743b610a6eSkamil #else
14753b610a6eSkamil phdr_info->dlpi_tls_modid = 0;
14763b610a6eSkamil phdr_info->dlpi_tls_data = 0;
14773b610a6eSkamil #endif
14783b610a6eSkamil phdr_info->dlpi_adds = _rtld_objloads;
14793b610a6eSkamil phdr_info->dlpi_subs = _rtld_objloads - _rtld_objcount;
14803b610a6eSkamil }
14813b610a6eSkamil
1482e6cdac9cSskrll __strong_alias(__dl_iterate_phdr,dl_iterate_phdr);
1483e6cdac9cSskrll int
dl_iterate_phdr(int (* callback)(struct dl_phdr_info *,size_t,void *),void * param)1484e6cdac9cSskrll dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *param)
1485e6cdac9cSskrll {
1486e6cdac9cSskrll struct dl_phdr_info phdr_info;
1487e6cdac9cSskrll const Obj_Entry *obj;
1488e6cdac9cSskrll int error = 0;
1489e6cdac9cSskrll
14902feaef06Sjoerg dbg(("dl_iterate_phdr"));
14912feaef06Sjoerg
1492cb1cd7e8Sjoerg _rtld_shared_enter();
1493cb1cd7e8Sjoerg
1494e6cdac9cSskrll for (obj = _rtld_objlist; obj != NULL; obj = obj->next) {
14953b610a6eSkamil _rtld_fill_dl_phdr_info(obj, &phdr_info);
1496e6cdac9cSskrll
1497cb1cd7e8Sjoerg /* XXXlocking: exit point */
1498e6cdac9cSskrll error = callback(&phdr_info, sizeof(phdr_info), param);
1499e6cdac9cSskrll if (error)
1500e6cdac9cSskrll break;
1501e6cdac9cSskrll }
1502e6cdac9cSskrll
15033b610a6eSkamil if (error == 0) {
15043b610a6eSkamil _rtld_fill_dl_phdr_info(&_rtld_objself, &phdr_info);
15053b610a6eSkamil
15063b610a6eSkamil /* XXXlocking: exit point */
15073b610a6eSkamil error = callback(&phdr_info, sizeof(phdr_info), param);
15083b610a6eSkamil }
15093b610a6eSkamil
1510cb1cd7e8Sjoerg _rtld_shared_exit();
1511e6cdac9cSskrll return error;
1512e6cdac9cSskrll }
1513e6cdac9cSskrll
1514e5678be8Sjoerg void
__dl_cxa_refcount(void * addr,ssize_t delta)1515e5678be8Sjoerg __dl_cxa_refcount(void *addr, ssize_t delta)
1516e5678be8Sjoerg {
1517e5678be8Sjoerg sigset_t mask;
1518e5678be8Sjoerg Obj_Entry *obj;
1519e5678be8Sjoerg
1520e5678be8Sjoerg if (delta == 0)
1521e5678be8Sjoerg return;
1522e5678be8Sjoerg
1523e5678be8Sjoerg dbg(("__dl_cxa_refcount of %p with %zd", addr, delta));
1524e5678be8Sjoerg
1525e5678be8Sjoerg _rtld_exclusive_enter(&mask);
1526e5678be8Sjoerg obj = _rtld_obj_from_addr(addr);
1527e5678be8Sjoerg
1528e5678be8Sjoerg if (obj == NULL) {
1529e5678be8Sjoerg dbg(("__dl_cxa_refcont: address not found"));
1530e5678be8Sjoerg _rtld_error("No shared object contains address");
1531e5678be8Sjoerg _rtld_exclusive_exit(&mask);
1532e5678be8Sjoerg return;
1533e5678be8Sjoerg }
1534e5678be8Sjoerg if (delta > 0 && obj->cxa_refcount > SIZE_MAX - delta)
1535e5678be8Sjoerg _rtld_error("Reference count overflow");
1536e5678be8Sjoerg else if (delta < 0 && obj->cxa_refcount < -1 + (size_t)-(delta + 1))
1537e5678be8Sjoerg _rtld_error("Reference count underflow");
1538e5678be8Sjoerg else {
1539e5678be8Sjoerg if (obj->cxa_refcount == 0)
1540e5678be8Sjoerg ++obj->refcount;
1541e5678be8Sjoerg obj->cxa_refcount += delta;
1542e5678be8Sjoerg dbg(("new reference count: %zu", obj->cxa_refcount));
1543e5678be8Sjoerg if (obj->cxa_refcount == 0) {
1544e5678be8Sjoerg --obj->refcount;
1545e5678be8Sjoerg if (obj->refcount == 0)
1546e5678be8Sjoerg _rtld_unload_object(&mask, obj, true);
1547e5678be8Sjoerg }
1548e5678be8Sjoerg }
1549e5678be8Sjoerg
1550e5678be8Sjoerg _rtld_exclusive_exit(&mask);
1551e5678be8Sjoerg }
1552e5678be8Sjoerg
155311954c74Sjoerg __dso_public pid_t
__locked_fork(int * my_errno)155425a494ecSjoerg __locked_fork(int *my_errno)
155511954c74Sjoerg {
155611954c74Sjoerg pid_t result;
155711954c74Sjoerg
155865715c61Sriastradh _rtld_shared_enter();
155911954c74Sjoerg result = __fork();
156025a494ecSjoerg if (result == -1)
156125a494ecSjoerg *my_errno = errno;
156265715c61Sriastradh _rtld_shared_exit();
156311954c74Sjoerg
156411954c74Sjoerg return result;
156511954c74Sjoerg }
156611954c74Sjoerg
156741fe218bScgd /*
156841fe218bScgd * Error reporting function. Use it like printf. If formats the message
156941fe218bScgd * into a buffer, and sets things up so that the next call to dlerror()
157041fe218bScgd * will return the message.
157141fe218bScgd */
157241fe218bScgd void
_rtld_error(const char * fmt,...)157326475619Schristos _rtld_error(const char *fmt,...)
157441fe218bScgd {
157541fe218bScgd static char buf[512];
157641fe218bScgd va_list ap;
157726475619Schristos
1578bf840df2Swiz va_start(ap, fmt);
157941fe218bScgd xvsnprintf(buf, sizeof buf, fmt, ap);
15801c559787Sriastradh dbg(("%s: %s", __func__, buf));
158141fe218bScgd error_message = buf;
158241fe218bScgd va_end(ap);
158341fe218bScgd }
1584a32bf0c1Spk
158541fe218bScgd void
_rtld_debug_state(void)15865f573ab6Sskrll _rtld_debug_state(void)
158741fe218bScgd {
158870289840Sskrll #if defined(__hppa__)
158970289840Sskrll __asm volatile("nop" ::: "memory");
159070289840Sskrll #endif
1591a9f5b3f8Ssimonb
15928ae54e93Sjoerg /* Prevent optimizer from removing calls to this function */
15938ae54e93Sjoerg __insn_barrier();
159441fe218bScgd }
159541fe218bScgd
159641fe218bScgd void
_rtld_linkmap_add(Obj_Entry * obj)15975f573ab6Sskrll _rtld_linkmap_add(Obj_Entry *obj)
159841fe218bScgd {
159941fe218bScgd struct link_map *l = &obj->linkmap;
160041fe218bScgd struct link_map *prev;
160141fe218bScgd
160241fe218bScgd obj->linkmap.l_name = obj->path;
1603d1351c62Smycroft obj->linkmap.l_addr = obj->relocbase;
160441fe218bScgd obj->linkmap.l_ld = obj->dynamic;
1605082edeccSmhitch #ifdef __mips__
160672b0e512Smycroft /* XXX This field is not standard and will be removed eventually. */
1607082edeccSmhitch obj->linkmap.l_offs = obj->relocbase;
1608082edeccSmhitch #endif
160941fe218bScgd
161041fe218bScgd if (_rtld_debug.r_map == NULL) {
161141fe218bScgd _rtld_debug.r_map = l;
161241fe218bScgd return;
161341fe218bScgd }
161439aae79dSskrll
161539aae79dSskrll /*
161639aae79dSskrll * Scan to the end of the list, but not past the entry for the
161739aae79dSskrll * dynamic linker, which we want to keep at the very end.
161839aae79dSskrll */
161939aae79dSskrll for (prev = _rtld_debug.r_map;
162039aae79dSskrll prev->l_next != NULL && prev->l_next != &_rtld_objself.linkmap;
162139aae79dSskrll prev = prev->l_next);
162239aae79dSskrll
162341fe218bScgd l->l_prev = prev;
162439aae79dSskrll l->l_next = prev->l_next;
162539aae79dSskrll if (l->l_next != NULL)
162639aae79dSskrll l->l_next->l_prev = l;
162741fe218bScgd prev->l_next = l;
162841fe218bScgd }
162941fe218bScgd
163041fe218bScgd void
_rtld_linkmap_delete(Obj_Entry * obj)16315f573ab6Sskrll _rtld_linkmap_delete(Obj_Entry *obj)
163241fe218bScgd {
163341fe218bScgd struct link_map *l = &obj->linkmap;
163441fe218bScgd
163541fe218bScgd if (l->l_prev == NULL) {
163641fe218bScgd if ((_rtld_debug.r_map = l->l_next) != NULL)
163741fe218bScgd l->l_next->l_prev = NULL;
163841fe218bScgd return;
163941fe218bScgd }
164041fe218bScgd if ((l->l_prev->l_next = l->l_next) != NULL)
164141fe218bScgd l->l_next->l_prev = l->l_prev;
164241fe218bScgd }
1643305c9497Smycroft
1644305c9497Smycroft static Obj_Entry *
_rtld_obj_from_addr(const void * addr)1645305c9497Smycroft _rtld_obj_from_addr(const void *addr)
1646305c9497Smycroft {
1647305c9497Smycroft Obj_Entry *obj;
1648305c9497Smycroft
1649305c9497Smycroft for (obj = _rtld_objlist; obj != NULL; obj = obj->next) {
1650305c9497Smycroft if (addr < (void *) obj->mapbase)
1651305c9497Smycroft continue;
165286103e2fSmycroft if (addr < (void *) (obj->mapbase + obj->mapsize))
1653305c9497Smycroft return obj;
1654305c9497Smycroft }
1655305c9497Smycroft return NULL;
1656305c9497Smycroft }
1657305c9497Smycroft
1658305c9497Smycroft static void
_rtld_objlist_clear(Objlist * list)1659558a5ffdSad _rtld_objlist_clear(Objlist *list)
1660558a5ffdSad {
1661558a5ffdSad while (!SIMPLEQ_EMPTY(list)) {
1662558a5ffdSad Objlist_Entry* elm = SIMPLEQ_FIRST(list);
1663558a5ffdSad SIMPLEQ_REMOVE_HEAD(list, link);
1664558a5ffdSad xfree(elm);
1665558a5ffdSad }
1666558a5ffdSad }
1667558a5ffdSad
1668558a5ffdSad static void
_rtld_objlist_remove(Objlist * list,Obj_Entry * obj)16695f573ab6Sskrll _rtld_objlist_remove(Objlist *list, Obj_Entry *obj)
1670305c9497Smycroft {
1671305c9497Smycroft Objlist_Entry *elm;
1672305c9497Smycroft
1673305c9497Smycroft if ((elm = _rtld_objlist_find(list, obj)) != NULL) {
167406de4264Slukem SIMPLEQ_REMOVE(list, elm, Struct_Objlist_Entry, link);
1675bf4b000dSad xfree(elm);
1676305c9497Smycroft }
1677305c9497Smycroft }
1678cb1cd7e8Sjoerg
1679cb1cd7e8Sjoerg #define RTLD_EXCLUSIVE_MASK 0x80000000U
1680cb1cd7e8Sjoerg static volatile unsigned int _rtld_mutex;
1681cb1cd7e8Sjoerg static volatile unsigned int _rtld_waiter_exclusive;
1682cb1cd7e8Sjoerg static volatile unsigned int _rtld_waiter_shared;
1683cb1cd7e8Sjoerg
1684cb1cd7e8Sjoerg void
_rtld_shared_enter(void)1685cb1cd7e8Sjoerg _rtld_shared_enter(void)
1686cb1cd7e8Sjoerg {
1687cb1cd7e8Sjoerg unsigned int cur;
1688cb1cd7e8Sjoerg lwpid_t waiter, self = 0;
1689cb1cd7e8Sjoerg
1690cb1cd7e8Sjoerg for (;;) {
1691cb1cd7e8Sjoerg cur = _rtld_mutex;
1692cb1cd7e8Sjoerg /*
1693cb1cd7e8Sjoerg * First check if we are currently not exclusively locked.
1694cb1cd7e8Sjoerg */
1695cb1cd7e8Sjoerg if ((cur & RTLD_EXCLUSIVE_MASK) == 0) {
1696cb1cd7e8Sjoerg /* Yes, so increment use counter */
1697cb1cd7e8Sjoerg if (atomic_cas_uint(&_rtld_mutex, cur, cur + 1) != cur)
1698cb1cd7e8Sjoerg continue;
1699207d8867Sriastradh membar_acquire();
1700cb1cd7e8Sjoerg return;
1701cb1cd7e8Sjoerg }
1702cb1cd7e8Sjoerg /*
1703cb1cd7e8Sjoerg * Someone has an exclusive lock. Puts us on the waiter list.
1704cb1cd7e8Sjoerg */
1705cb1cd7e8Sjoerg if (!self)
1706cb1cd7e8Sjoerg self = _lwp_self();
1707cb1cd7e8Sjoerg if (cur == (self | RTLD_EXCLUSIVE_MASK)) {
1708cb1cd7e8Sjoerg if (_rtld_mutex_may_recurse)
1709cb1cd7e8Sjoerg return;
17107519806bSchristos _rtld_error("%s: dead lock detected", __func__);
1711cb1cd7e8Sjoerg _rtld_die();
1712cb1cd7e8Sjoerg }
1713cb1cd7e8Sjoerg waiter = atomic_swap_uint(&_rtld_waiter_shared, self);
1714cb1cd7e8Sjoerg /*
1715cb1cd7e8Sjoerg * Check for race against _rtld_exclusive_exit before sleeping.
1716cb1cd7e8Sjoerg */
1717b549d229Syamt membar_sync();
1718cb1cd7e8Sjoerg if ((_rtld_mutex & RTLD_EXCLUSIVE_MASK) ||
1719cb1cd7e8Sjoerg _rtld_waiter_exclusive)
172015f3733bSjoerg _lwp_park(CLOCK_REALTIME, 0, NULL, 0,
172115f3733bSjoerg __UNVOLATILE(&_rtld_mutex), NULL);
1722cb1cd7e8Sjoerg /* Try to remove us from the waiter list. */
1723cb1cd7e8Sjoerg atomic_cas_uint(&_rtld_waiter_shared, self, 0);
1724cb1cd7e8Sjoerg if (waiter)
1725cb1cd7e8Sjoerg _lwp_unpark(waiter, __UNVOLATILE(&_rtld_mutex));
1726cb1cd7e8Sjoerg }
1727cb1cd7e8Sjoerg }
1728cb1cd7e8Sjoerg
1729cb1cd7e8Sjoerg void
_rtld_shared_exit(void)1730cb1cd7e8Sjoerg _rtld_shared_exit(void)
1731cb1cd7e8Sjoerg {
1732cb1cd7e8Sjoerg lwpid_t waiter;
1733cb1cd7e8Sjoerg
1734cb1cd7e8Sjoerg /*
1735cb1cd7e8Sjoerg * Shared lock taken after an exclusive lock.
1736cb1cd7e8Sjoerg * Just assume this is a partial recursion.
1737cb1cd7e8Sjoerg */
1738cb1cd7e8Sjoerg if (_rtld_mutex & RTLD_EXCLUSIVE_MASK)
1739cb1cd7e8Sjoerg return;
1740cb1cd7e8Sjoerg
1741cb1cd7e8Sjoerg /*
1742cb1cd7e8Sjoerg * Wakeup LWPs waiting for an exclusive lock if this is the last
1743cb1cd7e8Sjoerg * LWP on the shared lock.
1744cb1cd7e8Sjoerg */
1745207d8867Sriastradh membar_release();
1746cb1cd7e8Sjoerg if (atomic_dec_uint_nv(&_rtld_mutex))
1747cb1cd7e8Sjoerg return;
1748b549d229Syamt membar_sync();
1749cb1cd7e8Sjoerg if ((waiter = _rtld_waiter_exclusive) != 0)
1750cb1cd7e8Sjoerg _lwp_unpark(waiter, __UNVOLATILE(&_rtld_mutex));
1751cb1cd7e8Sjoerg }
1752cb1cd7e8Sjoerg
1753cb1cd7e8Sjoerg void
_rtld_exclusive_enter(sigset_t * mask)17548fa65855Sjoerg _rtld_exclusive_enter(sigset_t *mask)
1755cb1cd7e8Sjoerg {
1756cb1cd7e8Sjoerg lwpid_t waiter, self = _lwp_self();
1757cb1cd7e8Sjoerg unsigned int locked_value = (unsigned int)self | RTLD_EXCLUSIVE_MASK;
1758cb1cd7e8Sjoerg unsigned int cur;
17598fa65855Sjoerg sigset_t blockmask;
17608fa65855Sjoerg
17618fa65855Sjoerg sigfillset(&blockmask);
17621f1b5ad0Schristos sigdelset(&blockmask, SIGTRAP); /* Allow the debugger */
17638fa65855Sjoerg sigprocmask(SIG_BLOCK, &blockmask, mask);
1764cb1cd7e8Sjoerg
1765cb1cd7e8Sjoerg for (;;) {
1766b549d229Syamt if (atomic_cas_uint(&_rtld_mutex, 0, locked_value) == 0) {
1767207d8867Sriastradh membar_acquire();
1768cb1cd7e8Sjoerg break;
1769b549d229Syamt }
1770cb1cd7e8Sjoerg waiter = atomic_swap_uint(&_rtld_waiter_exclusive, self);
1771b549d229Syamt membar_sync();
1772cb1cd7e8Sjoerg cur = _rtld_mutex;
1773cb1cd7e8Sjoerg if (cur == locked_value) {
17747519806bSchristos _rtld_error("%s: dead lock detected", __func__);
1775cb1cd7e8Sjoerg _rtld_die();
1776cb1cd7e8Sjoerg }
1777cb1cd7e8Sjoerg if (cur)
177815f3733bSjoerg _lwp_park(CLOCK_REALTIME, 0, NULL, 0,
177915f3733bSjoerg __UNVOLATILE(&_rtld_mutex), NULL);
1780cb1cd7e8Sjoerg atomic_cas_uint(&_rtld_waiter_exclusive, self, 0);
1781cb1cd7e8Sjoerg if (waiter)
1782cb1cd7e8Sjoerg _lwp_unpark(waiter, __UNVOLATILE(&_rtld_mutex));
1783cb1cd7e8Sjoerg }
1784cb1cd7e8Sjoerg }
1785cb1cd7e8Sjoerg
1786cb1cd7e8Sjoerg void
_rtld_exclusive_exit(sigset_t * mask)17878fa65855Sjoerg _rtld_exclusive_exit(sigset_t *mask)
1788cb1cd7e8Sjoerg {
1789cb1cd7e8Sjoerg lwpid_t waiter;
1790cb1cd7e8Sjoerg
1791207d8867Sriastradh membar_release();
1792cb1cd7e8Sjoerg _rtld_mutex = 0;
1793b549d229Syamt membar_sync();
1794cb1cd7e8Sjoerg if ((waiter = _rtld_waiter_exclusive) != 0)
1795cb1cd7e8Sjoerg _lwp_unpark(waiter, __UNVOLATILE(&_rtld_mutex));
1796cb1cd7e8Sjoerg
1797cb1cd7e8Sjoerg if ((waiter = _rtld_waiter_shared) != 0)
1798cb1cd7e8Sjoerg _lwp_unpark(waiter, __UNVOLATILE(&_rtld_mutex));
1799cb1cd7e8Sjoerg
18008fa65855Sjoerg sigprocmask(SIG_SETMASK, mask, NULL);
1801cb1cd7e8Sjoerg }
180261268f23Schristos
180361268f23Schristos int
_rtld_relro(const Obj_Entry * obj,bool wantmain)180461268f23Schristos _rtld_relro(const Obj_Entry *obj, bool wantmain)
180561268f23Schristos {
180661268f23Schristos #ifdef GNU_RELRO
1807da570a62Sthorpej /*
1808da570a62Sthorpej * If our VM page size is larger than the page size used by the
1809da570a62Sthorpej * linker when laying out the object, we could end up making data
1810da570a62Sthorpej * read-only that is unintended. Detect and avoid this situation.
1811da570a62Sthorpej * It may mean we are unable to protect everything we'd like, but
1812da570a62Sthorpej * it's better than crashing.
1813da570a62Sthorpej */
1814da570a62Sthorpej uintptr_t relro_end = (uintptr_t)obj->relro_page + obj->relro_size;
1815da570a62Sthorpej uintptr_t relro_start = round_down((uintptr_t)obj->relro_page);
1816da570a62Sthorpej assert(relro_end >= relro_start);
1817da570a62Sthorpej size_t relro_size = round_down(relro_end) - relro_start;
1818da570a62Sthorpej
1819da570a62Sthorpej if (relro_size == 0)
182061268f23Schristos return 0;
182161268f23Schristos if (wantmain != (obj ==_rtld_objmain))
182261268f23Schristos return 0;
182361268f23Schristos
1824da570a62Sthorpej dbg(("RELRO %s %p %zx\n", obj->path, (void *)relro_start, relro_size));
1825da570a62Sthorpej if (mprotect((void *)relro_start, relro_size, PROT_READ) == -1) {
182661268f23Schristos _rtld_error("%s: Cannot enforce relro " "protection: %s",
182761268f23Schristos obj->path, xstrerror(errno));
182861268f23Schristos return -1;
182961268f23Schristos }
183061268f23Schristos #endif
183161268f23Schristos return 0;
183261268f23Schristos }
1833