xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/util.c (revision 67d74cc3)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55aefb655Srie  * Common Development and Distribution License (the "License").
65aefb655Srie  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
227257d1b4Sraf /*
237257d1b4Sraf  *	Copyright (c) 1988 AT&T
247257d1b4Sraf  *	  All Rights Reserved
25f441771bSRod Evans  *
26f441771bSRod Evans  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
277257d1b4Sraf  */
290f6d88adSAlex Reece /*
300f6d88adSAlex Reece  * Copyright (c) 2014 by Delphix. All rights reserved.
310f6d88adSAlex Reece  */
320f6d88adSAlex Reece 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate  * Utility routines for run-time linker.  some are duplicated here from libc
357c478bd9Sstevel@tonic-gate  * (with different names) to avoid name space collisions.
367c478bd9Sstevel@tonic-gate  */
3708278a5eSRod Evans #include	<sys/systeminfo.h>
387c478bd9Sstevel@tonic-gate #include	<stdio.h>
392017c965SRod Evans #include	<sys/time.h>
407c478bd9Sstevel@tonic-gate #include	<sys/types.h>
417c478bd9Sstevel@tonic-gate #include	<sys/mman.h>
427c478bd9Sstevel@tonic-gate #include	<sys/lwp.h>
437c478bd9Sstevel@tonic-gate #include	<sys/debug.h>
447c478bd9Sstevel@tonic-gate #include	<stdarg.h>
457c478bd9Sstevel@tonic-gate #include	<fcntl.h>
467c478bd9Sstevel@tonic-gate #include	<string.h>
477c478bd9Sstevel@tonic-gate #include	<dlfcn.h>
487c478bd9Sstevel@tonic-gate #include	<unistd.h>
497c478bd9Sstevel@tonic-gate #include	<stdlib.h>
507c478bd9Sstevel@tonic-gate #include	<sys/auxv.h>
5156deab07SRod Evans #include	<limits.h>
525aefb655Srie #include	<debug.h>
535aefb655Srie #include	<conv.h>
547c478bd9Sstevel@tonic-gate #include	"_rtld.h"
557c478bd9Sstevel@tonic-gate #include	"_audit.h"
5610a4fa49Srie #include	"_elf.h"
577c478bd9Sstevel@tonic-gate #include	"msg.h"
597c478bd9Sstevel@tonic-gate /*
607c478bd9Sstevel@tonic-gate  * Null function used as place where a debugger can set a breakpoint.
617c478bd9Sstevel@tonic-gate  */
627c478bd9Sstevel@tonic-gate void
rtld_db_dlactivity(Lm_list * lml)635aefb655Srie rtld_db_dlactivity(Lm_list *lml)
647c478bd9Sstevel@tonic-gate {
655aefb655Srie 	DBG_CALL(Dbg_util_dbnotify(lml, r_debug.rtd_rdebug.r_rdevent,
665aefb655Srie 	    r_debug.rtd_rdebug.r_state));
677c478bd9Sstevel@tonic-gate }
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * Null function used as place where debugger can set a pre .init
717c478bd9Sstevel@tonic-gate  * processing breakpoint.
727c478bd9Sstevel@tonic-gate  */
737c478bd9Sstevel@tonic-gate void
rtld_db_preinit(Lm_list * lml)745aefb655Srie rtld_db_preinit(Lm_list *lml)
757c478bd9Sstevel@tonic-gate {
765aefb655Srie 	DBG_CALL(Dbg_util_dbnotify(lml, r_debug.rtd_rdebug.r_rdevent,
775aefb655Srie 	    r_debug.rtd_rdebug.r_state));
787c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate /*
817c478bd9Sstevel@tonic-gate  * Null function used as place where debugger can set a post .init
827c478bd9Sstevel@tonic-gate  * processing breakpoint.
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate void
rtld_db_postinit(Lm_list * lml)855aefb655Srie rtld_db_postinit(Lm_list *lml)
867c478bd9Sstevel@tonic-gate {
875aefb655Srie 	DBG_CALL(Dbg_util_dbnotify(lml, r_debug.rtd_rdebug.r_rdevent,
885aefb655Srie 	    r_debug.rtd_rdebug.r_state));
897c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate /*
927c478bd9Sstevel@tonic-gate  * Debugger Event Notification
937c478bd9Sstevel@tonic-gate  *
947c478bd9Sstevel@tonic-gate  * This function centralizes all debugger event notification (ala rtld_db).
957c478bd9Sstevel@tonic-gate  *
967c478bd9Sstevel@tonic-gate  * There's a simple intent, focused on insuring the primary link-map control
977c478bd9Sstevel@tonic-gate  * list (or each link-map list) is consistent, and the indication that objects
987c478bd9Sstevel@tonic-gate  * have been added or deleted from this list.  Although an RD_ADD and RD_DELETE
997c478bd9Sstevel@tonic-gate  * event are posted for each of these, most debuggers don't care, as their
1007c478bd9Sstevel@tonic-gate  * view is that these events simply convey an "inconsistent" state.
1017c478bd9Sstevel@tonic-gate  *
1027c478bd9Sstevel@tonic-gate  * We also don't want to trigger multiple RD_ADD/RD_DELETE events any time we
1037c478bd9Sstevel@tonic-gate  * enter ld.so.1.
1047c478bd9Sstevel@tonic-gate  *
1057c478bd9Sstevel@tonic-gate  * Set an RD_ADD/RD_DELETE event and indicate that an RD_CONSISTENT event is
1062020b2b6SRod Evans  * required later (RT_FL_DBNOTIF):
1077c478bd9Sstevel@tonic-gate  *
1082020b2b6SRod Evans  *  i.	the first time we add or delete an object to the primary link-map
1097c478bd9Sstevel@tonic-gate  *	control list.
1102020b2b6SRod Evans  *  ii.	the first time we move a secondary link-map control list to the primary
1117c478bd9Sstevel@tonic-gate  *	link-map control list (effectively, this is like adding a group of
1127c478bd9Sstevel@tonic-gate  *	objects to the primary link-map control list).
1137c478bd9Sstevel@tonic-gate  *
1142020b2b6SRod Evans  * Set an RD_CONSISTENT event when it is required (RT_FL_DBNOTIF is set):
1157c478bd9Sstevel@tonic-gate  *
1162020b2b6SRod Evans  *  i.	each time we leave the runtime linker.
1177c478bd9Sstevel@tonic-gate  */
1187c478bd9Sstevel@tonic-gate void
rd_event(Lm_list * lml,rd_event_e event,r_state_e state)1197c478bd9Sstevel@tonic-gate rd_event(Lm_list *lml, rd_event_e event, r_state_e state)
1207c478bd9Sstevel@tonic-gate {
12110a4fa49Srie 	void	(*fptr)(Lm_list *);
1237c478bd9Sstevel@tonic-gate 	switch (event) {
1247c478bd9Sstevel@tonic-gate 	case RD_PREINIT:
1257c478bd9Sstevel@tonic-gate 		fptr = rtld_db_preinit;
1267c478bd9Sstevel@tonic-gate 		break;
1277c478bd9Sstevel@tonic-gate 	case RD_POSTINIT:
1287c478bd9Sstevel@tonic-gate 		fptr = rtld_db_postinit;
1297c478bd9Sstevel@tonic-gate 		break;
1307c478bd9Sstevel@tonic-gate 	case RD_DLACTIVITY:
1317c478bd9Sstevel@tonic-gate 		switch (state) {
1327c478bd9Sstevel@tonic-gate 		case RT_CONSISTENT:
1337c478bd9Sstevel@tonic-gate 			/*
1347c478bd9Sstevel@tonic-gate 			 * Do we need to send a notification?
1357c478bd9Sstevel@tonic-gate 			 */
1367c478bd9Sstevel@tonic-gate 			if ((rtld_flags & RT_FL_DBNOTIF) == 0)
1377c478bd9Sstevel@tonic-gate 				return;
1387c478bd9Sstevel@tonic-gate 			rtld_flags &= ~RT_FL_DBNOTIF;
1397c478bd9Sstevel@tonic-gate 			break;
1407c478bd9Sstevel@tonic-gate 		case RT_ADD:
1417c478bd9Sstevel@tonic-gate 		case RT_DELETE:
1427c478bd9Sstevel@tonic-gate 			/*
1437c478bd9Sstevel@tonic-gate 			 * If we are already in an inconsistent state, no
1447c478bd9Sstevel@tonic-gate 			 * notification is required.
1457c478bd9Sstevel@tonic-gate 			 */
1467c478bd9Sstevel@tonic-gate 			if (rtld_flags & RT_FL_DBNOTIF)
1477c478bd9Sstevel@tonic-gate 				return;
1487c478bd9Sstevel@tonic-gate 			rtld_flags |= RT_FL_DBNOTIF;
1497c478bd9Sstevel@tonic-gate 			break;
1507c478bd9Sstevel@tonic-gate 		};
1517c478bd9Sstevel@tonic-gate 		fptr = rtld_db_dlactivity;
1527c478bd9Sstevel@tonic-gate 		break;
1537c478bd9Sstevel@tonic-gate 	default:
1547c478bd9Sstevel@tonic-gate 		/*
1557c478bd9Sstevel@tonic-gate 		 * RD_NONE - do nothing
1567c478bd9Sstevel@tonic-gate 		 */
1577c478bd9Sstevel@tonic-gate 		break;
1587c478bd9Sstevel@tonic-gate 	};
1607c478bd9Sstevel@tonic-gate 	/*
1617c478bd9Sstevel@tonic-gate 	 * Set event state and call 'notification' function.
1627c478bd9Sstevel@tonic-gate 	 *
1637c478bd9Sstevel@tonic-gate 	 * The debugging clients have previously been told about these
1647c478bd9Sstevel@tonic-gate 	 * notification functions and have set breakpoints on them if they
1657c478bd9Sstevel@tonic-gate 	 * are interested in the notification.
1667c478bd9Sstevel@tonic-gate 	 */
1677c478bd9Sstevel@tonic-gate 	r_debug.rtd_rdebug.r_state = state;
1687c478bd9Sstevel@tonic-gate 	r_debug.rtd_rdebug.r_rdevent = event;
1695aefb655Srie 	fptr(lml);
1707c478bd9Sstevel@tonic-gate 	r_debug.rtd_rdebug.r_rdevent = RD_NONE;
1717c478bd9Sstevel@tonic-gate }
17302ca3e02Srie #if	defined(__sparc) || defined(__x86)
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Stack Cleanup.
1767c478bd9Sstevel@tonic-gate  *
1777c478bd9Sstevel@tonic-gate  * This function is invoked to 'remove' arguments that were passed in on the
1787c478bd9Sstevel@tonic-gate  * stack.  This is most likely if ld.so.1 was invoked directly.  In that case
1797c478bd9Sstevel@tonic-gate  * we want to remove ld.so.1 as well as it's arguments from the argv[] array.
1807c478bd9Sstevel@tonic-gate  * Which means we then need to slide everything above it on the stack down
1817c478bd9Sstevel@tonic-gate  * accordingly.
1827c478bd9Sstevel@tonic-gate  *
18302ca3e02Srie  * While the stack layout is platform specific - it just so happens that __x86,
18402ca3e02Srie  * and __sparc platforms share the following initial stack layout.
1857c478bd9Sstevel@tonic-gate  *
1867c478bd9Sstevel@tonic-gate  *	!_______________________!  high addresses
1877c478bd9Sstevel@tonic-gate  *	!			!
1887c478bd9Sstevel@tonic-gate  *	!	Information	!
1897c478bd9Sstevel@tonic-gate  *	!	Block		!
1907c478bd9Sstevel@tonic-gate  *	!	(size varies)	!
1917c478bd9Sstevel@tonic-gate  *	!_______________________!
1927c478bd9Sstevel@tonic-gate  *	!	0 word		!
1937c478bd9Sstevel@tonic-gate  *	!_______________________!
1947c478bd9Sstevel@tonic-gate  *	!	Auxiliary	!
1957c478bd9Sstevel@tonic-gate  *	!	vector		!
1967c478bd9Sstevel@tonic-gate  *	!	2 word entries	!
1977c478bd9Sstevel@tonic-gate  *	!			!
1987c478bd9Sstevel@tonic-gate  *	!_______________________!
1997c478bd9Sstevel@tonic-gate  *	!	0 word		!
2007c478bd9Sstevel@tonic-gate  *	!_______________________!
2017c478bd9Sstevel@tonic-gate  *	!	Environment	!
2027c478bd9Sstevel@tonic-gate  *	!	pointers	!
2037c478bd9Sstevel@tonic-gate  *	!	...		!
2047c478bd9Sstevel@tonic-gate  *	!	(one word each)	!
2057c478bd9Sstevel@tonic-gate  *	!_______________________!
2067c478bd9Sstevel@tonic-gate  *	!	0 word		!
2077c478bd9Sstevel@tonic-gate  *	!_______________________!
2087c478bd9Sstevel@tonic-gate  *	!	Argument	! low addresses
2097c478bd9Sstevel@tonic-gate  *	!	pointers	!
2107c478bd9Sstevel@tonic-gate  *	!	Argc words	!
2117c478bd9Sstevel@tonic-gate  *	!_______________________!
2127c478bd9Sstevel@tonic-gate  *	!			!
2137c478bd9Sstevel@tonic-gate  *	!	Argc		!
2147c478bd9Sstevel@tonic-gate  *	!_______________________!
2157c478bd9Sstevel@tonic-gate  *	!	...		!
2167c478bd9Sstevel@tonic-gate  *
2177c478bd9Sstevel@tonic-gate  */
2187c478bd9Sstevel@tonic-gate static void
stack_cleanup(char ** argv,char *** envp,auxv_t ** auxv,int rmcnt)21941072f3cSrie stack_cleanup(char **argv, char ***envp, auxv_t **auxv, int rmcnt)
2207c478bd9Sstevel@tonic-gate {
22141072f3cSrie 	int		ndx;
2227c478bd9Sstevel@tonic-gate 	long		*argc;
22341072f3cSrie 	char		**oargv, **nargv;
22441072f3cSrie 	char		**oenvp, **nenvp;
22541072f3cSrie 	auxv_t		*oauxv, *nauxv;
2277c478bd9Sstevel@tonic-gate 	/*
22841072f3cSrie 	 * Slide ARGV[] and update argc.  The argv pointer remains the same,
22941072f3cSrie 	 * however slide the applications arguments over the arguments to
23041072f3cSrie 	 * ld.so.1.
2317c478bd9Sstevel@tonic-gate 	 */
23241072f3cSrie 	nargv = &argv[0];
23341072f3cSrie 	oargv = &argv[rmcnt];
23541072f3cSrie 	for (ndx = 0; oargv[ndx]; ndx++)
23641072f3cSrie 		nargv[ndx] = oargv[ndx];
23741072f3cSrie 	nargv[ndx] = oargv[ndx];
23941072f3cSrie 	argc = (long *)((uintptr_t)argv - sizeof (long *));
2407c478bd9Sstevel@tonic-gate 	*argc -= rmcnt;
2427c478bd9Sstevel@tonic-gate 	/*
24341072f3cSrie 	 * Slide ENVP[], and update the environment array pointer.
2447c478bd9Sstevel@tonic-gate 	 */
24541072f3cSrie 	ndx++;
24641072f3cSrie 	nenvp = &nargv[ndx];
24741072f3cSrie 	oenvp = &oargv[ndx];
24841072f3cSrie 	*envp = nenvp;
25041072f3cSrie 	for (ndx = 0; oenvp[ndx]; ndx++)
25141072f3cSrie 		nenvp[ndx] = oenvp[ndx];
25241072f3cSrie 	nenvp[ndx] = oenvp[ndx];
2547c478bd9Sstevel@tonic-gate 	/*
25541072f3cSrie 	 * Slide AUXV[], and update the aux vector pointer.
2567c478bd9Sstevel@tonic-gate 	 */
25741072f3cSrie 	ndx++;
25841072f3cSrie 	nauxv = (auxv_t *)&nenvp[ndx];
25941072f3cSrie 	oauxv = (auxv_t *)&oenvp[ndx];
26041072f3cSrie 	*auxv = nauxv;
26241072f3cSrie 	for (ndx = 0; (oauxv[ndx].a_type != AT_NULL); ndx++)
26341072f3cSrie 		nauxv[ndx] = oauxv[ndx];
26441072f3cSrie 	nauxv[ndx] = oauxv[ndx];
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate #else
2677c478bd9Sstevel@tonic-gate /*
2687c478bd9Sstevel@tonic-gate  * Verify that the above routine is appropriate for any new platforms.
2697c478bd9Sstevel@tonic-gate  */
2707c478bd9Sstevel@tonic-gate #error	unsupported architecture!
2717c478bd9Sstevel@tonic-gate #endif
2737c478bd9Sstevel@tonic-gate /*
2749aa23310Srie  * Compare function for PathNode AVL tree.
2757c478bd9Sstevel@tonic-gate  */
2767c478bd9Sstevel@tonic-gate static int
pnavl_compare(const void * n1,const void * n2)2779aa23310Srie pnavl_compare(const void *n1, const void *n2)
2787c478bd9Sstevel@tonic-gate {
2797c478bd9Sstevel@tonic-gate 	uint_t		hash1, hash2;
2807c478bd9Sstevel@tonic-gate 	const char	*st1, *st2;
2817c478bd9Sstevel@tonic-gate 	int		rc;
2839aa23310Srie 	hash1 = ((PathNode *)n1)->pn_hash;
2849aa23310Srie 	hash2 = ((PathNode *)n2)->pn_hash;
2867c478bd9Sstevel@tonic-gate 	if (hash1 > hash2)
2877c478bd9Sstevel@tonic-gate 		return (1);
2887c478bd9Sstevel@tonic-gate 	if (hash1 < hash2)
2897c478bd9Sstevel@tonic-gate 		return (-1);
2919aa23310Srie 	st1 = ((PathNode *)n1)->pn_name;
2929aa23310Srie 	st2 = ((PathNode *)n2)->pn_name;
2947c478bd9Sstevel@tonic-gate 	rc = strcmp(st1, st2);
2957c478bd9Sstevel@tonic-gate 	if (rc > 0)
2967c478bd9Sstevel@tonic-gate 		return (1);
2977c478bd9Sstevel@tonic-gate 	if (rc < 0)
2987c478bd9Sstevel@tonic-gate 		return (-1);
2997c478bd9Sstevel@tonic-gate 	return (0);
3007c478bd9Sstevel@tonic-gate }
3023dbfc803SRod Evans /*
3033dbfc803SRod Evans  * Create an AVL tree.
3043dbfc803SRod Evans  */
3053dbfc803SRod Evans static avl_tree_t *
pnavl_create(size_t size)3063dbfc803SRod Evans pnavl_create(size_t size)
3073dbfc803SRod Evans {
3083dbfc803SRod Evans 	avl_tree_t	*avlt;
3093dbfc803SRod Evans 
3103dbfc803SRod Evans 	if ((avlt = malloc(sizeof (avl_tree_t))) == NULL)
3113dbfc803SRod Evans 		return (NULL);
3123dbfc803SRod Evans 	avl_create(avlt, pnavl_compare, size, SGSOFFSETOF(PathNode, pn_avl));
3133dbfc803SRod Evans 	return (avlt);
3143dbfc803SRod Evans }
3153dbfc803SRod Evans 
31608278a5eSRod Evans /*
31708278a5eSRod Evans  * Determine whether a PathNode is recorded.
31808278a5eSRod Evans  */
31908278a5eSRod Evans int
pnavl_recorded(avl_tree_t ** pnavl,const char * name,uint_t hash,avl_index_t * where)32008278a5eSRod Evans pnavl_recorded(avl_tree_t **pnavl, const char *name, uint_t hash,
32108278a5eSRod Evans     avl_index_t *where)
32208278a5eSRod Evans {
32308278a5eSRod Evans 	PathNode	pn;
32408278a5eSRod Evans 
32508278a5eSRod Evans 	/*
32608278a5eSRod Evans 	 * Create the avl tree if required.
32708278a5eSRod Evans 	 */
32808278a5eSRod Evans 	if ((*pnavl == NULL) &&
32908278a5eSRod Evans 	    ((*pnavl = pnavl_create(sizeof (PathNode))) == NULL))
33008278a5eSRod Evans 		return (0);
33108278a5eSRod Evans 
33208278a5eSRod Evans 	pn.pn_name = name;
33308278a5eSRod Evans 	if ((pn.pn_hash = hash) == 0)
33408278a5eSRod Evans 		pn.pn_hash = sgs_str_hash(name);
33508278a5eSRod Evans 
33608278a5eSRod Evans 	if (avl_find(*pnavl, &pn, where) == NULL)
33708278a5eSRod Evans 		return (0);
33808278a5eSRod Evans 
33908278a5eSRod Evans 	return (1);
34008278a5eSRod Evans }
34108278a5eSRod Evans 
3427c478bd9Sstevel@tonic-gate /*
3439aa23310Srie  * Determine if a pathname has already been recorded on the full path name
3449aa23310Srie  * AVL tree.  This tree maintains a node for each path name that ld.so.1 has
3459aa23310Srie  * successfully loaded.  If the path name does not exist in this AVL tree, then
3469aa23310Srie  * the next insertion point is deposited in "where".  This value can be used by
3479aa23310Srie  * fpavl_insert() to expedite the insertion.
3487c478bd9Sstevel@tonic-gate  */
3497c478bd9Sstevel@tonic-gate Rt_map *
fpavl_recorded(Lm_list * lml,const char * name,uint_t hash,avl_index_t * where)35056deab07SRod Evans fpavl_recorded(Lm_list *lml, const char *name, uint_t hash, avl_index_t *where)
3517c478bd9Sstevel@tonic-gate {
3529aa23310Srie 	FullPathNode	fpn, *fpnp;
3547c478bd9Sstevel@tonic-gate 	/*
3557c478bd9Sstevel@tonic-gate 	 * Create the avl tree if required.
3567c478bd9Sstevel@tonic-gate 	 */
3573dbfc803SRod Evans 	if ((lml->lm_fpavl == NULL) &&
3583dbfc803SRod Evans 	    ((lml->lm_fpavl = pnavl_create(sizeof (FullPathNode))) == NULL))
3593dbfc803SRod Evans 		return (NULL);
3619aa23310Srie 	fpn.fpn_node.pn_name = name;
36256deab07SRod Evans 	if ((fpn.fpn_node.pn_hash = hash) == 0)
36356deab07SRod Evans 		fpn.fpn_node.pn_hash = sgs_str_hash(name);
3657c478bd9Sstevel@tonic-gate 	if ((fpnp = avl_find(lml->lm_fpavl, &fpn, where)) == NULL)
3667c478bd9Sstevel@tonic-gate 		return (NULL);
3687c478bd9Sstevel@tonic-gate 	return (fpnp->fpn_lmp);
3697c478bd9Sstevel@tonic-gate }
3717c478bd9Sstevel@tonic-gate /*
3729aa23310Srie  * Insert a name into the FullPathNode AVL tree for the link-map list.  The
37324a6229eSrie  * objects NAME() is the path that would have originally been searched for, and
37424a6229eSrie  * is therefore the name to associate with any "where" value.  If the object has
3757c478bd9Sstevel@tonic-gate  * a different PATHNAME(), perhaps because it has resolved to a different file
3769aa23310Srie  * (see fullpath()), then this name will be recorded as a separate FullPathNode
3779aa23310Srie  * (see load_file()).
3787c478bd9Sstevel@tonic-gate  */
3797c478bd9Sstevel@tonic-gate int
fpavl_insert(Lm_list * lml,Rt_map * lmp,const char * name,avl_index_t where)3807c478bd9Sstevel@tonic-gate fpavl_insert(Lm_list *lml, Rt_map *lmp, const char *name, avl_index_t where)
3817c478bd9Sstevel@tonic-gate {
3829aa23310Srie 	FullPathNode	*fpnp;
38356deab07SRod Evans 	uint_t		hash = sgs_str_hash(name);
3857c478bd9Sstevel@tonic-gate 	if (where == 0) {
3867c478bd9Sstevel@tonic-gate 		/* LINTED */
38756deab07SRod Evans 		Rt_map	*_lmp = fpavl_recorded(lml, name, hash, &where);
3897c478bd9Sstevel@tonic-gate 		/*
3907c478bd9Sstevel@tonic-gate 		 * We better not get a hit now, we do not want duplicates in
3917c478bd9Sstevel@tonic-gate 		 * the tree.
3927c478bd9Sstevel@tonic-gate 		 */
39343d7826aSRod Evans 		ASSERT(_lmp == NULL);
3947c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 	/*
3979aa23310Srie 	 * Insert new node in tree.
3987c478bd9Sstevel@tonic-gate 	 */
3993dbfc803SRod Evans 	if ((fpnp = calloc(sizeof (FullPathNode), 1)) == NULL)
4007c478bd9Sstevel@tonic-gate 		return (0);
4029aa23310Srie 	fpnp->fpn_node.pn_name = name;
40356deab07SRod Evans 	fpnp->fpn_node.pn_hash = hash;
4047c478bd9Sstevel@tonic-gate 	fpnp->fpn_lmp = lmp;
406cce0e03bSab 	if (aplist_append(&FPNODE(lmp), fpnp, AL_CNT_FPNODE) == NULL) {
4077c478bd9Sstevel@tonic-gate 		free(fpnp);
4087c478bd9Sstevel@tonic-gate 		return (0);
4097c478bd9Sstevel@tonic-gate 	}
4117c478bd9Sstevel@tonic-gate 	ASSERT(lml->lm_fpavl != NULL);
4127c478bd9Sstevel@tonic-gate 	avl_insert(lml->lm_fpavl, fpnp, where);
4137c478bd9Sstevel@tonic-gate 	return (1);
4147c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate /*
41756deab07SRod Evans  * Remove an object from the FullPathNode AVL tree.
4187c478bd9Sstevel@tonic-gate  */
4197c478bd9Sstevel@tonic-gate void
fpavl_remove(Rt_map * lmp)4207c478bd9Sstevel@tonic-gate fpavl_remove(Rt_map *lmp)
4217c478bd9Sstevel@tonic-gate {
4229aa23310Srie 	FullPathNode	*fpnp;
423cce0e03bSab 	Aliste		idx;
425cce0e03bSab 	for (APLIST_TRAVERSE(FPNODE(lmp), idx, fpnp)) {
4267c478bd9Sstevel@tonic-gate 		avl_remove(LIST(lmp)->lm_fpavl, fpnp);
4277c478bd9Sstevel@tonic-gate 		free(fpnp);
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 	free(FPNODE(lmp));
430cce0e03bSab 	FPNODE(lmp) = NULL;
4317c478bd9Sstevel@tonic-gate }
4339aa23310Srie /*
43408278a5eSRod Evans  * Insert a path name into the not-found AVL tree.
43508278a5eSRod Evans  *
4369aa23310Srie  * This tree maintains a node for each path name that ld.so.1 has explicitly
4379aa23310Srie  * inspected, but has failed to load during a single ld.so.1 operation.  If the
4389aa23310Srie  * path name does not exist in this AVL tree, then the next insertion point is
4399aa23310Srie  * deposited in "where".  This value can be used by nfavl_insert() to expedite
4409aa23310Srie  * the insertion.
4419aa23310Srie  */
4429aa23310Srie void
nfavl_insert(const char * name,avl_index_t where)4439aa23310Srie nfavl_insert(const char *name, avl_index_t where)
4449aa23310Srie {
4459aa23310Srie 	PathNode	*pnp;
44656deab07SRod Evans 	uint_t		hash = sgs_str_hash(name);
4489aa23310Srie 	if (where == 0) {
4499aa23310Srie 		/* LINTED */
45008278a5eSRod Evans 		int	in_nfavl = pnavl_recorded(&nfavl, name, hash, &where);
4529aa23310Srie 		/*
4539aa23310Srie 		 * We better not get a hit now, we do not want duplicates in
4549aa23310Srie 		 * the tree.
4559aa23310Srie 		 */
4569aa23310Srie 		ASSERT(in_nfavl == 0);
4579aa23310Srie 	}
4599aa23310Srie 	/*
4609aa23310Srie 	 * Insert new node in tree.
4619aa23310Srie 	 */
46256deab07SRod Evans 	if ((pnp = calloc(sizeof (PathNode), 1)) != NULL) {
4639aa23310Srie 		pnp->pn_name = name;
46456deab07SRod Evans 		pnp->pn_hash = hash;
4659aa23310Srie 		avl_insert(nfavl, pnp, where);
4669aa23310Srie 	}
4679aa23310Srie }
4693dbfc803SRod Evans /*
47008278a5eSRod Evans  * Insert the directory name, of a full path name, into the secure path AVL
4713dbfc803SRod Evans  * tree.
47208278a5eSRod Evans  *
47308278a5eSRod Evans  * This tree is used to maintain a list of directories in which the dependencies
47408278a5eSRod Evans  * of a secure process have been found.  This list provides a fall-back in the
47508278a5eSRod Evans  * case that a $ORIGIN expansion is deemed insecure, when the expansion results
47608278a5eSRod Evans  * in a path name that has already provided dependencies.
4773dbfc803SRod Evans  */
4783dbfc803SRod Evans void
spavl_insert(const char * name)4793dbfc803SRod Evans spavl_insert(const char *name)
4803dbfc803SRod Evans {
4813dbfc803SRod Evans 	char		buffer[PATH_MAX], *str;
4823dbfc803SRod Evans 	size_t		size;
4833dbfc803SRod Evans 	avl_index_t	where;
4843dbfc803SRod Evans 	PathNode	*pnp;
48508278a5eSRod Evans 	uint_t		hash;
4863dbfc803SRod Evans 
4873dbfc803SRod Evans 	/*
4883dbfc803SRod Evans 	 * Separate the directory name from the path name.
4893dbfc803SRod Evans 	 */
4903dbfc803SRod Evans 	if ((str = strrchr(name, '/')) == name)
4913dbfc803SRod Evans 		size = 1;
4923dbfc803SRod Evans 	else
4933dbfc803SRod Evans 		size = str - name;
4943dbfc803SRod Evans 
4953dbfc803SRod Evans 	(void) strncpy(buffer, name, size);
4963dbfc803SRod Evans 	buffer[size] = '\0';
49708278a5eSRod Evans 	hash = sgs_str_hash(buffer);
4983dbfc803SRod Evans 
4993dbfc803SRod Evans 	/*
5003dbfc803SRod Evans 	 * Determine whether this directory name is already recorded, or if
5013dbfc803SRod Evans 	 * not, 'where" will provide the insertion point for the new string.
5023dbfc803SRod Evans 	 */
50308278a5eSRod Evans 	if (pnavl_recorded(&spavl, buffer, hash, &where))
5043dbfc803SRod Evans 		return;
5053dbfc803SRod Evans 
5063dbfc803SRod Evans 	/*
5073dbfc803SRod Evans 	 * Insert new node in tree.
5083dbfc803SRod Evans 	 */
50943d7826aSRod Evans 	if ((pnp = calloc(sizeof (PathNode), 1)) != NULL) {
5103dbfc803SRod Evans 		pnp->pn_name = strdup(buffer);
51108278a5eSRod Evans 		pnp->pn_hash = hash;
5123dbfc803SRod Evans 		avl_insert(spavl, pnp, where);
5133dbfc803SRod Evans 	}
5143dbfc803SRod Evans }
5153dbfc803SRod Evans 
51656deab07SRod Evans /*
51756deab07SRod Evans  * Inspect the generic string AVL tree for the given string.  If the string is
51856deab07SRod Evans  * not present, duplicate it, and insert the string in the AVL tree.  Return the
51956deab07SRod Evans  * duplicated string to the caller.
52056deab07SRod Evans  *
52156deab07SRod Evans  * These strings are maintained for the life of ld.so.1 and represent path
52256deab07SRod Evans  * names, file names, and search paths.  All other AVL trees that maintain
52356deab07SRod Evans  * FullPathNode and not-found path names use the same string pointer
52456deab07SRod Evans  * established for this string.
52556deab07SRod Evans  */
52656deab07SRod Evans static avl_tree_t	*stravl = NULL;
52756deab07SRod Evans static char		*strbuf = NULL;
52856deab07SRod Evans static PathNode		*pnbuf = NULL;
52956deab07SRod Evans static size_t		strsize = 0, pnsize = 0;
53056deab07SRod Evans 
53156deab07SRod Evans const char *
stravl_insert(const char * name,uint_t hash,size_t nsize,int substr)53256deab07SRod Evans stravl_insert(const char *name, uint_t hash, size_t nsize, int substr)
53356deab07SRod Evans {
53456deab07SRod Evans 	char		str[PATH_MAX];
53556deab07SRod Evans 	PathNode	*pnp;
53656deab07SRod Evans 	avl_index_t	where;
53756deab07SRod Evans 
53856deab07SRod Evans 	/*
53956deab07SRod Evans 	 * Create the avl tree if required.
54056deab07SRod Evans 	 */
54156deab07SRod Evans 	if ((stravl == NULL) &&
54256deab07SRod Evans 	    ((stravl = pnavl_create(sizeof (PathNode))) == NULL))
54356deab07SRod Evans 		return (NULL);
54456deab07SRod Evans 
54556deab07SRod Evans 	/*
54656deab07SRod Evans 	 * Determine the string size if not provided by the caller.
54756deab07SRod Evans 	 */
54856deab07SRod Evans 	if (nsize == 0)
54956deab07SRod Evans 		nsize = strlen(name) + 1;
55056deab07SRod Evans 	else if (substr) {
55156deab07SRod Evans 		/*
55256deab07SRod Evans 		 * The string passed to us may be a multiple path string for
55356deab07SRod Evans 		 * which we only need the first component.  Using the provided
55456deab07SRod Evans 		 * size, strip out the required string.
55556deab07SRod Evans 		 */
55656deab07SRod Evans 		(void) strncpy(str, name, nsize);
55756deab07SRod Evans 		str[nsize - 1] = '\0';
55856deab07SRod Evans 		name = str;
55956deab07SRod Evans 	}
56056deab07SRod Evans 
56156deab07SRod Evans 	/*
56256deab07SRod Evans 	 * Allocate a PathNode buffer if one doesn't exist, or any existing
56356deab07SRod Evans 	 * buffer has been used up.
56456deab07SRod Evans 	 */
56556deab07SRod Evans 	if ((pnbuf == NULL) || (sizeof (PathNode) > pnsize)) {
56656deab07SRod Evans 		pnsize = syspagsz;
56756deab07SRod Evans 		if ((pnbuf = dz_map(0, 0, pnsize, (PROT_READ | PROT_WRITE),
56856deab07SRod Evans 		    MAP_PRIVATE)) == MAP_FAILED)
56956deab07SRod Evans 			return (NULL);
57056deab07SRod Evans 	}
57156deab07SRod Evans 	/*
57256deab07SRod Evans 	 * Determine whether this string already exists.
57356deab07SRod Evans 	 */
57456deab07SRod Evans 	pnbuf->pn_name = name;
57556deab07SRod Evans 	if ((pnbuf->pn_hash = hash) == 0)
57656deab07SRod Evans 		pnbuf->pn_hash = sgs_str_hash(name);
57756deab07SRod Evans 
57856deab07SRod Evans 	if ((pnp = avl_find(stravl, pnbuf, &where)) != NULL)
57956deab07SRod Evans 		return (pnp->pn_name);
58056deab07SRod Evans 
58156deab07SRod Evans 	/*
58256deab07SRod Evans 	 * Allocate a string buffer if one does not exist, or if there is
58356deab07SRod Evans 	 * insufficient space for the new string in any existing buffer.
58456deab07SRod Evans 	 */
58556deab07SRod Evans 	if ((strbuf == NULL) || (nsize > strsize)) {
58656deab07SRod Evans 		strsize = S_ROUND(nsize, syspagsz);
58756deab07SRod Evans 
58856deab07SRod Evans 		if ((strbuf = dz_map(0, 0, strsize, (PROT_READ | PROT_WRITE),
58956deab07SRod Evans 		    MAP_PRIVATE)) == MAP_FAILED)
59056deab07SRod Evans 			return (NULL);
59156deab07SRod Evans 	}
59256deab07SRod Evans 
59356deab07SRod Evans 	(void) memcpy(strbuf, name, nsize);
59456deab07SRod Evans 	pnp = pnbuf;
59556deab07SRod Evans 	pnp->pn_name = strbuf;
59656deab07SRod Evans 	avl_insert(stravl, pnp, where);
59756deab07SRod Evans 
59856deab07SRod Evans 	strbuf += nsize;
59956deab07SRod Evans 	strsize -= nsize;
60056deab07SRod Evans 	pnbuf++;
60156deab07SRod Evans 	pnsize -= sizeof (PathNode);
60256deab07SRod Evans 	return (pnp->pn_name);
60356deab07SRod Evans }
60456deab07SRod Evans 
6057c478bd9Sstevel@tonic-gate /*
6067c478bd9Sstevel@tonic-gate  * Prior to calling an object, either via a .plt or through dlsym(), make sure
6077c478bd9Sstevel@tonic-gate  * its .init has fired.  Through topological sorting, ld.so.1 attempts to fire
6087c478bd9Sstevel@tonic-gate  * init's in the correct order, however, this order is typically based on needed
6097c478bd9Sstevel@tonic-gate  * dependencies and non-lazy relocation bindings.  Lazy relocations (.plts) can
6107c478bd9Sstevel@tonic-gate  * still occur and result in bindings that were not captured during topological
6117c478bd9Sstevel@tonic-gate  * sorting.  This routine compensates for this lack of binding information, and
6127c478bd9Sstevel@tonic-gate  * provides for dynamic .init firing.
6137c478bd9Sstevel@tonic-gate  */
6147c478bd9Sstevel@tonic-gate void
is_dep_init(Rt_map * dlmp,Rt_map * clmp)6159aa23310Srie is_dep_init(Rt_map *dlmp, Rt_map *clmp)
6167c478bd9Sstevel@tonic-gate {
6179aa23310Srie 	Rt_map	**tobj;
6197c478bd9Sstevel@tonic-gate 	/*
6207c478bd9Sstevel@tonic-gate 	 * If the caller is an auditor, and the destination isn't, then don't
6217c478bd9Sstevel@tonic-gate 	 * run any .inits (see comments in load_completion()).
6227c478bd9Sstevel@tonic-gate 	 */
6232020b2b6SRod Evans 	if ((LIST(clmp)->lm_tflags & LML_TFLG_NOAUDIT) &&
6242020b2b6SRod Evans 	    ((LIST(dlmp)->lm_tflags & LML_TFLG_NOAUDIT) == 0))
6257c478bd9Sstevel@tonic-gate 		return;
62756deab07SRod Evans 	if ((dlmp == clmp) || (rtld_flags & RT_FL_INITFIRST))
6287c478bd9Sstevel@tonic-gate 		return;
63048125817SKeith M Wesolowski 	(void) rt_mutex_lock(&dlmp->rt_lock);
63148125817SKeith M Wesolowski 	while (dlmp->rt_init_thread != rt_thr_self() && (FLAGS(dlmp) &
63248125817SKeith M Wesolowski 	    (FLG_RT_RELOCED | FLG_RT_INITCALL | FLG_RT_INITDONE)) ==
63348125817SKeith M Wesolowski 	    (FLG_RT_RELOCED | FLG_RT_INITCALL)) {
63448125817SKeith M Wesolowski 		leave(LIST(dlmp), 0);
63548125817SKeith M Wesolowski 		(void) _lwp_cond_wait(&dlmp->rt_cv, (mutex_t *)&dlmp->rt_lock);
63648125817SKeith M Wesolowski 		(void) rt_mutex_unlock(&dlmp->rt_lock);
63748125817SKeith M Wesolowski 		(void) enter(0);
63848125817SKeith M Wesolowski 		(void) rt_mutex_lock(&dlmp->rt_lock);
63948125817SKeith M Wesolowski 	}
64048125817SKeith M Wesolowski 	(void) rt_mutex_unlock(&dlmp->rt_lock);
64148125817SKeith M Wesolowski 
6427c478bd9Sstevel@tonic-gate 	if ((FLAGS(dlmp) & (FLG_RT_RELOCED | FLG_RT_INITDONE)) ==
6437c478bd9Sstevel@tonic-gate 	    (FLG_RT_RELOCED | FLG_RT_INITDONE))
6447c478bd9Sstevel@tonic-gate 		return;
6467c478bd9Sstevel@tonic-gate 	if ((tobj = calloc(2, sizeof (Rt_map *))) != NULL) {
6477c478bd9Sstevel@tonic-gate 		tobj[0] = dlmp;
6487c478bd9Sstevel@tonic-gate 		call_init(tobj, DBG_INIT_DYN);
6497c478bd9Sstevel@tonic-gate 	}
6507c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate  * Execute .{preinit|init|fini}array sections
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate void
call_array(Addr * array,uint_t arraysz,Rt_map * lmp,Word shtype)6565aefb655Srie call_array(Addr *array, uint_t arraysz, Rt_map *lmp, Word shtype)
6577c478bd9Sstevel@tonic-gate {
6585aefb655Srie 	int	start, stop, incr, ndx;
6597c478bd9Sstevel@tonic-gate 	uint_t	arraycnt = (uint_t)(arraysz / sizeof (Addr));
6617c478bd9Sstevel@tonic-gate 	if (array == NULL)
6627c478bd9Sstevel@tonic-gate 		return;
6647c478bd9Sstevel@tonic-gate 	/*
6657c478bd9Sstevel@tonic-gate 	 * initarray & preinitarray are walked from beginning to end - while
6667c478bd9Sstevel@tonic-gate 	 * finiarray is walked from end to beginning.
6677c478bd9Sstevel@tonic-gate 	 */
6687c478bd9Sstevel@tonic-gate 	if (shtype == SHT_FINI_ARRAY) {
6697c478bd9Sstevel@tonic-gate 		start = arraycnt - 1;
6707c478bd9Sstevel@tonic-gate 		stop = incr = -1;
6717c478bd9Sstevel@tonic-gate 	} else {
6727c478bd9Sstevel@tonic-gate 		start = 0;
6737c478bd9Sstevel@tonic-gate 		stop = arraycnt;
6747c478bd9Sstevel@tonic-gate 		incr = 1;
6757c478bd9Sstevel@tonic-gate 	}
6777c478bd9Sstevel@tonic-gate 	/*
6787c478bd9Sstevel@tonic-gate 	 * Call the .*array[] entries
6797c478bd9Sstevel@tonic-gate 	 */
6805aefb655Srie 	for (ndx = start; ndx != stop; ndx += incr) {
6812020b2b6SRod Evans 		uint_t	rtldflags;
6822020b2b6SRod Evans 		void	(*fptr)(void) = (void(*)())array[ndx];
6845aefb655Srie 		DBG_CALL(Dbg_util_call_array(lmp, (void *)fptr, ndx, shtype));
6862020b2b6SRod Evans 		APPLICATION_ENTER(rtldflags);
687eccf73c8Srie 		leave(LIST(lmp), 0);
6887c478bd9Sstevel@tonic-gate 		(*fptr)();
689eccf73c8Srie 		(void) enter(0);
6902020b2b6SRod Evans 		APPLICATION_RETURN(rtldflags);
6917c478bd9Sstevel@tonic-gate 	}
6927c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate /*
6957c478bd9Sstevel@tonic-gate  * Execute any .init sections.  These are passed to us in an lmp array which
6967c478bd9Sstevel@tonic-gate  * (by default) will have been sorted.
6977c478bd9Sstevel@tonic-gate  */
6987c478bd9Sstevel@tonic-gate void
call_init(Rt_map ** tobj,int flag)6999aa23310Srie call_init(Rt_map **tobj, int flag)
7007c478bd9Sstevel@tonic-gate {
7019aa23310Srie 	Rt_map		**_tobj, **_nobj;
70257ef7aa9SRod Evans 	static APlist	*pending = NULL;
7047c478bd9Sstevel@tonic-gate 	/*
7057c478bd9Sstevel@tonic-gate 	 * If we're in the middle of an INITFIRST, this must complete before
7067c478bd9Sstevel@tonic-gate 	 * any new init's are fired.  In this case add the object list to the
7077c478bd9Sstevel@tonic-gate 	 * pending queue and return.  We'll pick up the queue after any
7087c478bd9Sstevel@tonic-gate 	 * INITFIRST objects have their init's fired.
7097c478bd9Sstevel@tonic-gate 	 */
7107c478bd9Sstevel@tonic-gate 	if (rtld_flags & RT_FL_INITFIRST) {
71157ef7aa9SRod Evans 		(void) aplist_append(&pending, tobj, AL_CNT_PENDING);
7127c478bd9Sstevel@tonic-gate 		return;
7137c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 	/*
7167c478bd9Sstevel@tonic-gate 	 * Traverse the tobj array firing each objects init.
7177c478bd9Sstevel@tonic-gate 	 */
7187c478bd9Sstevel@tonic-gate 	for (_tobj = _nobj = tobj, _nobj++; *_tobj != NULL; _tobj++, _nobj++) {
7199aa23310Srie 		Rt_map	*lmp = *_tobj;
7209aa23310Srie 		void	(*iptr)() = INIT(lmp);
7227c478bd9Sstevel@tonic-gate 		if (FLAGS(lmp) & FLG_RT_INITCALL)
7237c478bd9Sstevel@tonic-gate 			continue;
7257c478bd9Sstevel@tonic-gate 		FLAGS(lmp) |= FLG_RT_INITCALL;
72648125817SKeith M Wesolowski 		lmp->rt_init_thread = rt_thr_self();
7287c478bd9Sstevel@tonic-gate 		/*
7297c478bd9Sstevel@tonic-gate 		 * Establish an initfirst state if necessary - no other inits
730dffec89cSrie 		 * will be fired (because of additional relocation bindings)
731dffec89cSrie 		 * when in this state.
7327c478bd9Sstevel@tonic-gate 		 */
7337c478bd9Sstevel@tonic-gate 		if (FLAGS(lmp) & FLG_RT_INITFRST)
7347c478bd9Sstevel@tonic-gate 			rtld_flags |= RT_FL_INITFIRST;
73656deab07SRod Evans 		if (INITARRAY(lmp) || iptr)
7375aefb655Srie 			DBG_CALL(Dbg_util_call_init(lmp, flag));
7397c478bd9Sstevel@tonic-gate 		if (iptr) {
7402020b2b6SRod Evans 			uint_t	rtldflags;
7412020b2b6SRod Evans 
7422020b2b6SRod Evans 			APPLICATION_ENTER(rtldflags);
743eccf73c8Srie 			leave(LIST(lmp), 0);
7447c478bd9Sstevel@tonic-gate 			(*iptr)();
745eccf73c8Srie 			(void) enter(0);
7462020b2b6SRod Evans 			APPLICATION_RETURN(rtldflags);
7477c478bd9Sstevel@tonic-gate 		}
7497c478bd9Sstevel@tonic-gate 		call_array(INITARRAY(lmp), INITARRAYSZ(lmp), lmp,
7507c478bd9Sstevel@tonic-gate 		    SHT_INIT_ARRAY);
7527c478bd9Sstevel@tonic-gate 		if (INITARRAY(lmp) || iptr)
7535aefb655Srie 			DBG_CALL(Dbg_util_call_init(lmp, DBG_INIT_DONE));
7557c478bd9Sstevel@tonic-gate 		/*
7567c478bd9Sstevel@tonic-gate 		 * Set the initdone flag regardless of whether this object
7577c478bd9Sstevel@tonic-gate 		 * actually contains an .init section.  This flag prevents us
7587c478bd9Sstevel@tonic-gate 		 * from processing this section again for an .init and also
7597c478bd9Sstevel@tonic-gate 		 * signifies that a .fini must be called should it exist.
7607c478bd9Sstevel@tonic-gate 		 * Clear the sort field for use in later .fini processing.
7617c478bd9Sstevel@tonic-gate 		 */
76248125817SKeith M Wesolowski 		(void) rt_mutex_lock(&lmp->rt_lock);
7637c478bd9Sstevel@tonic-gate 		FLAGS(lmp) |= FLG_RT_INITDONE;
76448125817SKeith M Wesolowski 		lmp->rt_init_thread = (thread_t)0;
76548125817SKeith M Wesolowski 		(void) _lwp_cond_broadcast(&lmp->rt_cv);
76648125817SKeith M Wesolowski 		(void) rt_mutex_unlock(&lmp->rt_lock);
767dffec89cSrie 		SORTVAL(lmp) = -1;
7697c478bd9Sstevel@tonic-gate 		/*
7707c478bd9Sstevel@tonic-gate 		 * If we're firing an INITFIRST object, and other objects must
7717c478bd9Sstevel@tonic-gate 		 * be fired which are not INITFIRST, make sure we grab any
7727c478bd9Sstevel@tonic-gate 		 * pending objects that might have been delayed as this
7737c478bd9Sstevel@tonic-gate 		 * INITFIRST was processed.
7747c478bd9Sstevel@tonic-gate 		 */
7757c478bd9Sstevel@tonic-gate 		if ((rtld_flags & RT_FL_INITFIRST) &&
7767c478bd9Sstevel@tonic-gate 		    ((*_nobj == NULL) || !(FLAGS(*_nobj) & FLG_RT_INITFRST))) {
77757ef7aa9SRod Evans 			Aliste	idx;
77857ef7aa9SRod Evans 			Rt_map	**pobj;
7807c478bd9Sstevel@tonic-gate 			rtld_flags &= ~RT_FL_INITFIRST;
78257ef7aa9SRod Evans 			for (APLIST_TRAVERSE(pending, idx, pobj)) {
78357ef7aa9SRod Evans 				aplist_delete(pending, &idx);
7847c478bd9Sstevel@tonic-gate 				call_init(pobj, DBG_INIT_PEND);
7857c478bd9Sstevel@tonic-gate 			}
7867c478bd9Sstevel@tonic-gate 		}
7877c478bd9Sstevel@tonic-gate 	}
7887c478bd9Sstevel@tonic-gate 	free(tobj);
7897c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate /*
7922020b2b6SRod Evans  * Call .fini sections for the topologically sorted list of objects.  This
7932020b2b6SRod Evans  * routine is called from remove_hdl() for any objects being torn down as part
7942020b2b6SRod Evans  * of a dlclose() operation, and from atexit() processing for all the remaining
7952020b2b6SRod Evans  * objects within the process.
7967c478bd9Sstevel@tonic-gate  */
7977c478bd9Sstevel@tonic-gate void
call_fini(Lm_list * lml,Rt_map ** tobj,Rt_map * clmp)7982020b2b6SRod Evans call_fini(Lm_list *lml, Rt_map **tobj, Rt_map *clmp)
7997c478bd9Sstevel@tonic-gate {
800dffec89cSrie 	Rt_map **_tobj;
8027c478bd9Sstevel@tonic-gate 	for (_tobj = tobj; *_tobj != NULL; _tobj++) {
8032020b2b6SRod Evans 		Rt_map		*lmp = *_tobj;
8057c478bd9Sstevel@tonic-gate 		/*
80656deab07SRod Evans 		 * Only fire a .fini if the objects corresponding .init has
80756deab07SRod Evans 		 * completed.  We collect all .fini sections of objects that
80856deab07SRod Evans 		 * had their .init collected, but that doesn't mean that at
80956deab07SRod Evans 		 * the time of collection, that the .init had completed.
8107c478bd9Sstevel@tonic-gate 		 */
81156deab07SRod Evans 		if (FLAGS(lmp) & FLG_RT_INITDONE) {
81210a4fa49Srie 			void	(*fptr)(void) = FINI(lmp);
81456deab07SRod Evans 			if (FINIARRAY(lmp) || fptr)
8155aefb655Srie 				DBG_CALL(Dbg_util_call_fini(lmp));
8175aefb655Srie 			call_array(FINIARRAY(lmp), FINIARRAYSZ(lmp), lmp,
8185aefb655Srie 			    SHT_FINI_ARRAY);
8207c478bd9Sstevel@tonic-gate 			if (fptr) {
8212020b2b6SRod Evans 				uint_t	rtldflags;
8222020b2b6SRod Evans 
8232020b2b6SRod Evans 				APPLICATION_ENTER(rtldflags);
8242020b2b6SRod Evans 				leave(lml, 0);
8257c478bd9Sstevel@tonic-gate 				(*fptr)();
826eccf73c8Srie 				(void) enter(0);
8272020b2b6SRod Evans 				APPLICATION_RETURN(rtldflags);
8287c478bd9Sstevel@tonic-gate 			}
8297c478bd9Sstevel@tonic-gate 		}
831dffec89cSrie 		/*
832dffec89cSrie 		 * Skip main, this is explicitly called last in atexit_fini().
833dffec89cSrie 		 */
834dffec89cSrie 		if (FLAGS(lmp) & FLG_RT_ISMAIN)
835dffec89cSrie 			continue;
8377c478bd9Sstevel@tonic-gate 		/*
8382020b2b6SRod Evans 		 * This object has exercised its last instructions (regardless
8392020b2b6SRod Evans 		 * of whether it will be unmapped or not).  Audit this closure.
8407c478bd9Sstevel@tonic-gate 		 */
8412020b2b6SRod Evans 		if ((lml->lm_tflags & LML_TFLG_NOAUDIT) == 0)
8422020b2b6SRod Evans 			audit_objclose(lmp, clmp);
8437c478bd9Sstevel@tonic-gate 	}
8442020b2b6SRod Evans 
8455aefb655Srie 	DBG_CALL(Dbg_bind_plt_summary(lml, M_MACH, pltcnt21d, pltcnt24d,
8465aefb655Srie 	    pltcntu32, pltcntu44, pltcntfull, pltcntfar));
8487c478bd9Sstevel@tonic-gate 	free(tobj);
8497c478bd9Sstevel@tonic-gate }
8512020b2b6SRod Evans /*
8522020b2b6SRod Evans  * Function called by atexit(3C).  Calls all .fini sections within the objects
8532020b2b6SRod Evans  * that make up the process.  As .fini processing is the last opportunity for
8542020b2b6SRod Evans  * any new bindings to be established, this is also a convenient location to
8552020b2b6SRod Evans  * check for unused objects.
8562020b2b6SRod Evans  */
8577c478bd9Sstevel@tonic-gate void
atexit_fini()8587c478bd9Sstevel@tonic-gate atexit_fini()
8597c478bd9Sstevel@tonic-gate {
86057ef7aa9SRod Evans 	Rt_map	**tobj, *lmp;
86157ef7aa9SRod Evans 	Lm_list	*lml;
86257ef7aa9SRod Evans 	Aliste	idx;
8648cd45542Sraf 	(void) enter(0);
8667c478bd9Sstevel@tonic-gate 	rtld_flags |= RT_FL_ATEXIT;
8687c478bd9Sstevel@tonic-gate 	lml = &lml_main;
869dffec89cSrie 	lml->lm_flags |= LML_FLG_ATEXIT;
870883c6d49Srie 	lml->lm_flags &= ~LML_FLG_INTRPOSETSORT;
8717c478bd9Sstevel@tonic-gate 	lmp = (Rt_map *)lml->lm_head;
8737c478bd9Sstevel@tonic-gate 	/*
8747c478bd9Sstevel@tonic-gate 	 * Reverse topologically sort the main link-map for .fini execution.
8757c478bd9Sstevel@tonic-gate 	 */
87643d7826aSRod Evans 	if (((tobj = tsort(lmp, lml->lm_obj, RT_SORT_FWD)) != NULL) &&
8777c478bd9Sstevel@tonic-gate 	    (tobj != (Rt_map **)S_ERROR))
8782020b2b6SRod Evans 		call_fini(lml, tobj, NULL);
8792020b2b6SRod Evans 
8802020b2b6SRod Evans 	/*
8812020b2b6SRod Evans 	 * Now that all .fini code has been run, see what unreferenced objects
8822020b2b6SRod Evans 	 * remain.
8832020b2b6SRod Evans 	 */
8842020b2b6SRod Evans 	unused(lml);
8852020b2b6SRod Evans 
8862020b2b6SRod Evans 	/*
8872020b2b6SRod Evans 	 * Traverse any alternative link-map lists, looking for non-auditors.
8882020b2b6SRod Evans 	 */
8892020b2b6SRod Evans 	for (APLIST_TRAVERSE(dynlm_list, idx, lml)) {
8902020b2b6SRod Evans 		/*
8912020b2b6SRod Evans 		 * Ignore the base-link-map list, which has already been
8922020b2b6SRod Evans 		 * processed, the runtime linkers link-map list, which is
8932020b2b6SRod Evans 		 * processed last, and any auditors.
8942020b2b6SRod Evans 		 */
8952020b2b6SRod Evans 		if ((lml->lm_flags & (LML_FLG_BASELM | LML_FLG_RTLDLM)) ||
8962020b2b6SRod Evans 		    (lml->lm_tflags & LML_TFLG_AUD_MASK) ||
8972020b2b6SRod Evans 		    ((lmp = (Rt_map *)lml->lm_head) == NULL))
8982020b2b6SRod Evans 			continue;
8992020b2b6SRod Evans 
9002020b2b6SRod Evans 		lml->lm_flags |= LML_FLG_ATEXIT;
9012020b2b6SRod Evans 		lml->lm_flags &= ~LML_FLG_INTRPOSETSORT;
9022020b2b6SRod Evans 
9032020b2b6SRod Evans 		/*
9042020b2b6SRod Evans 		 * Reverse topologically sort the link-map for .fini execution.
9052020b2b6SRod Evans 		 */
9062020b2b6SRod Evans 		if (((tobj = tsort(lmp, lml->lm_obj, RT_SORT_FWD)) != NULL) &&
9072020b2b6SRod Evans 		    (tobj != (Rt_map **)S_ERROR))
9082020b2b6SRod Evans 			call_fini(lml, tobj, NULL);
9092020b2b6SRod Evans 
9102020b2b6SRod Evans 		unused(lml);
9112020b2b6SRod Evans 	}
9137c478bd9Sstevel@tonic-gate 	/*
914dffec89cSrie 	 * Add an explicit close to main and ld.so.1.  Although main's .fini is
915dffec89cSrie 	 * collected in call_fini() to provide for FINITARRAY processing, its
916dffec89cSrie 	 * audit_objclose is explicitly skipped.  This provides for it to be
917dffec89cSrie 	 * called last, here.  This is the reverse of the explicit calls to
918dffec89cSrie 	 * audit_objopen() made in setup().
9197c478bd9Sstevel@tonic-gate 	 */
9202020b2b6SRod Evans 	lml = &lml_main;
9212020b2b6SRod Evans 	lmp = (Rt_map *)lml->lm_head;
9222020b2b6SRod Evans 
92356deab07SRod Evans 	if ((lml->lm_tflags | AFLAGS(lmp)) & LML_TFLG_AUD_MASK) {
9242020b2b6SRod Evans 		audit_objclose((Rt_map *)lml_rtld.lm_head, lmp);
925dffec89cSrie 		audit_objclose(lmp, lmp);
9267c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 	/*
9292020b2b6SRod Evans 	 * Traverse any alternative link-map lists, looking for non-auditors.
9307c478bd9Sstevel@tonic-gate 	 */
93157ef7aa9SRod Evans 	for (APLIST_TRAVERSE(dynlm_list, idx, lml)) {
93210a4fa49Srie 		/*
93310a4fa49Srie 		 * Ignore the base-link-map list, which has already been
9342020b2b6SRod Evans 		 * processed, the runtime linkers link-map list, which is
9352020b2b6SRod Evans 		 * processed last, and any non-auditors.
93610a4fa49Srie 		 */
9372020b2b6SRod Evans 		if ((lml->lm_flags & (LML_FLG_BASELM | LML_FLG_RTLDLM)) ||
9382020b2b6SRod Evans 		    ((lml->lm_tflags & LML_TFLG_AUD_MASK) == 0) ||
9392020b2b6SRod Evans 		    ((lmp = (Rt_map *)lml->lm_head) == NULL))
9407c478bd9Sstevel@tonic-gate 			continue;
942dffec89cSrie 		lml->lm_flags |= LML_FLG_ATEXIT;
943883c6d49Srie 		lml->lm_flags &= ~LML_FLG_INTRPOSETSORT;
9457c478bd9Sstevel@tonic-gate 		/*
9467c478bd9Sstevel@tonic-gate 		 * Reverse topologically sort the link-map for .fini execution.
9477c478bd9Sstevel@tonic-gate 		 */
94843d7826aSRod Evans 		if (((tobj = tsort(lmp, lml->lm_obj, RT_SORT_FWD)) != NULL) &&
9497c478bd9Sstevel@tonic-gate 		    (tobj != (Rt_map **)S_ERROR))
9502020b2b6SRod Evans 			call_fini(lml, tobj, NULL);
9527c478bd9Sstevel@tonic-gate 		unused(lml);
9537c478bd9Sstevel@tonic-gate 	}
9557c478bd9Sstevel@tonic-gate 	/*
9567c478bd9Sstevel@tonic-gate 	 * Finally reverse topologically sort the runtime linkers link-map for
9577c478bd9Sstevel@tonic-gate 	 * .fini execution.
9587c478bd9Sstevel@tonic-gate 	 */
9597c478bd9Sstevel@tonic-gate 	lml = &lml_rtld;
960dffec89cSrie 	lml->lm_flags |= LML_FLG_ATEXIT;
961883c6d49Srie 	lml->lm_flags &= ~LML_FLG_INTRPOSETSORT;
9627c478bd9Sstevel@tonic-gate 	lmp = (Rt_map *)lml->lm_head;
96443d7826aSRod Evans 	if (((tobj = tsort(lmp, lml->lm_obj, RT_SORT_FWD)) != NULL) &&
9657c478bd9Sstevel@tonic-gate 	    (tobj != (Rt_map **)S_ERROR))
9662020b2b6SRod Evans 		call_fini(lml, tobj, NULL);
9688cd45542Sraf 	leave(&lml_main, 0);
9697c478bd9Sstevel@tonic-gate }
9717c478bd9Sstevel@tonic-gate /*
9727c478bd9Sstevel@tonic-gate  * This routine is called to complete any runtime linker activity which may have
9737c478bd9Sstevel@tonic-gate  * resulted in objects being loaded.  This is called from all user entry points
9747c478bd9Sstevel@tonic-gate  * and from any internal dl*() requests.
9757c478bd9Sstevel@tonic-gate  */
9767c478bd9Sstevel@tonic-gate void
load_completion(Rt_map * nlmp)9777247f888Srie load_completion(Rt_map *nlmp)
9787c478bd9Sstevel@tonic-gate {
97943d7826aSRod Evans 	Rt_map	**tobj = NULL;
9807247f888Srie 	Lm_list	*nlml;
9827c478bd9Sstevel@tonic-gate 	/*
9837c478bd9Sstevel@tonic-gate 	 * Establish any .init processing.  Note, in a world of lazy loading,
9847c478bd9Sstevel@tonic-gate 	 * objects may have been loaded regardless of whether the users request
9857c478bd9Sstevel@tonic-gate 	 * was fulfilled (i.e., a dlsym() request may have failed to find a
9867c478bd9Sstevel@tonic-gate 	 * symbol but objects might have been loaded during its search).  Thus,
9877c478bd9Sstevel@tonic-gate 	 * any tsorting starts from the nlmp (new link-maps) pointer and not
9887c478bd9Sstevel@tonic-gate 	 * necessarily from the link-map that may have satisfied the request.
9897c478bd9Sstevel@tonic-gate 	 *
99010a4fa49Srie 	 * Note, the primary link-map has an initialization phase where dynamic
99110a4fa49Srie 	 * .init firing is suppressed.  This provides for a simple and clean
99210a4fa49Srie 	 * handshake with the primary link-maps libc, which is important for
99310a4fa49Srie 	 * establishing uberdata.  In addition, auditors often obtain handles
99410a4fa49Srie 	 * to primary link-map objects as the objects are loaded, so as to
99510a4fa49Srie 	 * inspect the link-map for symbols.  This inspection is allowed without
99610a4fa49Srie 	 * running any code on the primary link-map, as running this code may
99710a4fa49Srie 	 * reenter the auditor, who may not yet have finished its own
9987c478bd9Sstevel@tonic-gate 	 * initialization.
9997c478bd9Sstevel@tonic-gate 	 */
100010a4fa49Srie 	if (nlmp)
100110a4fa49Srie 		nlml = LIST(nlmp);
1003b71d513aSedp 	if (