xref: /netbsd-src/libexec/ld.elf_so/rtld.c (revision b1f732fc60792de259eb7f9541f25956a5e85d5a)
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