xref: /titanic_44/usr/src/cmd/sgs/rtld/common/remove.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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
207257d1b4Sraf  */
217257d1b4Sraf 
227257d1b4Sraf /*
23*2020b2b6SRod Evans  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
247257d1b4Sraf  */
257257d1b4Sraf 
267257d1b4Sraf /*
277c478bd9Sstevel@tonic-gate  * Remove objects.  Objects need removal from a process as part of:
287c478bd9Sstevel@tonic-gate  *
292017c965SRod Evans  *  -	a dlclose() request
307c478bd9Sstevel@tonic-gate  *
312017c965SRod Evans  *  -	tearing down a dlopen(), lazy-load, or filter hierarchy that failed to
327c478bd9Sstevel@tonic-gate  *	completely load
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * Any other failure condition will result in process exit (in which case all
357c478bd9Sstevel@tonic-gate  * we have to do is execute the fini's - tear down is unnecessary).
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * Any removal of objects is therefore associated with a dlopen() handle.  There
387c478bd9Sstevel@tonic-gate  * is a small window between creation of the first dlopen() object and creating
397c478bd9Sstevel@tonic-gate  * its handle (in which case remove_so() can get rid of the new link-map if
407c478bd9Sstevel@tonic-gate  * necessary), but other than this all object removal is driven by inspecting
417c478bd9Sstevel@tonic-gate  * the components of a handle.
427c478bd9Sstevel@tonic-gate  *
437c478bd9Sstevel@tonic-gate  * Things to note.  The creation of a link-map, and its addition to the link-map
447c478bd9Sstevel@tonic-gate  * list occurs in {elf|aout}_new_lm(), if this returns success the link-map is
457c478bd9Sstevel@tonic-gate  * valid and added, otherwise any steps (allocations) in the process of creating
467c478bd9Sstevel@tonic-gate  * the link-map would have been undone.  If a failure occurs between creating
477c478bd9Sstevel@tonic-gate  * the link-map and adding it to a handle, remove_so() is called to remove the
487c478bd9Sstevel@tonic-gate  * link-map.  If a failures occurs after a handle have been created,
497c478bd9Sstevel@tonic-gate  * remove_hdl() is called to remove the handle and the link-map.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include	<string.h>
537c478bd9Sstevel@tonic-gate #include	<stdio.h>
547c478bd9Sstevel@tonic-gate #include	<unistd.h>
557c478bd9Sstevel@tonic-gate #include	<dlfcn.h>
567c478bd9Sstevel@tonic-gate #include	<sys/debug.h>
577c478bd9Sstevel@tonic-gate #include	<sys/avl.h>
585aefb655Srie #include	<libc_int.h>
595aefb655Srie #include	<debug.h>
607c478bd9Sstevel@tonic-gate #include	"_rtld.h"
617c478bd9Sstevel@tonic-gate #include	"_audit.h"
627c478bd9Sstevel@tonic-gate #include	"_elf.h"
637c478bd9Sstevel@tonic-gate #include	"msg.h"
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate  * Atexit callback provided by libc.  As part of dlclose() determine the address
677c478bd9Sstevel@tonic-gate  * ranges of all objects that are to be deleted.  Pass this information to
687c478bd9Sstevel@tonic-gate  * libc's pre-atexit routine.  Libc will purge any registered atexit() calls
697c478bd9Sstevel@tonic-gate  * related to those objects about to be deleted.
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate static int
purge_exit_handlers(Lm_list * lml,Rt_map ** tobj)727c478bd9Sstevel@tonic-gate purge_exit_handlers(Lm_list *lml, Rt_map **tobj)
737c478bd9Sstevel@tonic-gate {
747c478bd9Sstevel@tonic-gate 	uint_t			num;
757c478bd9Sstevel@tonic-gate 	Rt_map			**_tobj;
767c478bd9Sstevel@tonic-gate 	Lc_addr_range_t		*addr, *_addr;
777c478bd9Sstevel@tonic-gate 	int			error;
7810a4fa49Srie 	int			(*fptr)(Lc_addr_range_t *, uint_t);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	/*
817c478bd9Sstevel@tonic-gate 	 * Has a callback been established?
827c478bd9Sstevel@tonic-gate 	 */
8310a4fa49Srie 	if ((fptr = lml->lm_lcs[CI_ATEXIT].lc_un.lc_func) == NULL)
847c478bd9Sstevel@tonic-gate 		return (0);
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 	/*
877c478bd9Sstevel@tonic-gate 	 * Determine the total number of mapped segments that will be unloaded.
887c478bd9Sstevel@tonic-gate 	 */
897c478bd9Sstevel@tonic-gate 	for (num = 0, _tobj = tobj; *_tobj != NULL; _tobj++) {
907c478bd9Sstevel@tonic-gate 		Rt_map	*lmp = *_tobj;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 		num += MMAPCNT(lmp);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	/*
967c478bd9Sstevel@tonic-gate 	 * Account for a null entry at the end of the address range array.
977c478bd9Sstevel@tonic-gate 	 */
987c478bd9Sstevel@tonic-gate 	if (num++ == 0)
997c478bd9Sstevel@tonic-gate 		return (0);
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	/*
1027c478bd9Sstevel@tonic-gate 	 * Allocate an array for the address range.
1037c478bd9Sstevel@tonic-gate 	 */
10456deab07SRod Evans 	if ((addr = malloc(num * sizeof (Lc_addr_range_t))) == NULL)
1057c478bd9Sstevel@tonic-gate 		return (1);
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	/*
1087c478bd9Sstevel@tonic-gate 	 * Fill the address range with each loadable segments size and address.
1097c478bd9Sstevel@tonic-gate 	 */
1107c478bd9Sstevel@tonic-gate 	for (_tobj = tobj, _addr = addr; *_tobj != NULL; _tobj++) {
1117c478bd9Sstevel@tonic-gate 		Rt_map			*lmp = *_tobj;
11256deab07SRod Evans 		mmapobj_result_t	*mpp = MMAPS(lmp);
11356deab07SRod Evans 		uint_t			ndx;
1147c478bd9Sstevel@tonic-gate 
11556deab07SRod Evans 		for (ndx = 0; ndx < MMAPCNT(lmp); ndx++, mpp++) {
11656deab07SRod Evans 			_addr->lb = (void *)(uintptr_t)(mpp->mr_addr +
11756deab07SRod Evans 			    mpp->mr_offset);
11856deab07SRod Evans 			_addr->ub = (void *)(uintptr_t)(mpp->mr_addr +
11956deab07SRod Evans 			    mpp->mr_msize);
1207c478bd9Sstevel@tonic-gate 			_addr++;
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	_addr->lb = _addr->ub = 0;
1247c478bd9Sstevel@tonic-gate 
12574a8d72aSrie 	leave(LIST(*tobj), 0);
12610a4fa49Srie 	error = (*fptr)(addr, (num - 1));
12774a8d72aSrie 	(void) enter(0);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	/*
1307c478bd9Sstevel@tonic-gate 	 * If we fail to converse with libc, generate an error message to
1317c478bd9Sstevel@tonic-gate 	 * satisfy any dlerror() usage.
1327c478bd9Sstevel@tonic-gate 	 */
1337c478bd9Sstevel@tonic-gate 	if (error)
1345aefb655Srie 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ATEXIT), error);
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	free(addr);
1377c478bd9Sstevel@tonic-gate 	return (error);
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*
14156deab07SRod Evans  * Break down an Alist containing pathname descriptors.  In most instances, the
142*2020b2b6SRod Evans  * Alist is removed completely.  However, in some instances the alist is cleaned
143*2020b2b6SRod Evans  * of all entries, but retained for later use.
1447c478bd9Sstevel@tonic-gate  */
1457c478bd9Sstevel@tonic-gate void
remove_alist(Alist ** alpp,int complete)146*2020b2b6SRod Evans remove_alist(Alist **alpp, int complete)
1477c478bd9Sstevel@tonic-gate {
14856deab07SRod Evans 	Alist	*alp = *alpp;
1497c478bd9Sstevel@tonic-gate 
15056deab07SRod Evans 	if (alp) {
15156deab07SRod Evans 		if (complete) {
15256deab07SRod Evans 			free((void *)alp);
15356deab07SRod Evans 			*alpp = NULL;
15456deab07SRod Evans 		} else {
155*2020b2b6SRod Evans 			alist_reset(alp);
1567c478bd9Sstevel@tonic-gate 		}
1577c478bd9Sstevel@tonic-gate 	}
15856deab07SRod Evans }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate /*
1617c478bd9Sstevel@tonic-gate  * Remove a link-map list descriptor.  This is called to finalize the removal
1627c478bd9Sstevel@tonic-gate  * of an entire link-map list, after all link-maps have been removed, or none
16312b8e62eSrie  * got added.  As load_one() can process a list of potential candidate objects,
1647c478bd9Sstevel@tonic-gate  * the link-map descriptor must be maintained as each object is processed.  Only
1657c478bd9Sstevel@tonic-gate  * after all objects have been processed can a failure condition finally tear
1667c478bd9Sstevel@tonic-gate  * down the link-map list descriptor.
1677c478bd9Sstevel@tonic-gate  */
1687c478bd9Sstevel@tonic-gate void
remove_lml(Lm_list * lml)1697c478bd9Sstevel@tonic-gate remove_lml(Lm_list *lml)
1707c478bd9Sstevel@tonic-gate {
171b4059b01SRod Evans 	if (lml && (lml->lm_head == NULL)) {
1725aefb655Srie 		if (lml->lm_lmidstr)
1735aefb655Srie 			free(lml->lm_lmidstr);
1747c478bd9Sstevel@tonic-gate 		if (lml->lm_alp)
1757c478bd9Sstevel@tonic-gate 			free(lml->lm_alp);
1767c478bd9Sstevel@tonic-gate 		if (lml->lm_lists)
1777c478bd9Sstevel@tonic-gate 			free(lml->lm_lists);
178*2020b2b6SRod Evans 		if (lml->lm_aud_cookies)
179*2020b2b6SRod Evans 			free(lml->lm_aud_cookies);
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 		/*
18210a4fa49Srie 		 * Cleanup any pending RTLDINFO in the case where it was
18310a4fa49Srie 		 * allocated but not called (see _relocate_lmc()).
1847c478bd9Sstevel@tonic-gate 		 */
18510a4fa49Srie 		if (lml->lm_rti)
18610a4fa49Srie 			free(lml->lm_rti);
1877c478bd9Sstevel@tonic-gate 		if (lml->lm_fpavl) {
1887c478bd9Sstevel@tonic-gate 			/*
1897c478bd9Sstevel@tonic-gate 			 * As we are freeing the link-map list, all nodes must
1907c478bd9Sstevel@tonic-gate 			 * have previously been removed.
1917c478bd9Sstevel@tonic-gate 			 */
1927c478bd9Sstevel@tonic-gate 			ASSERT(avl_numnodes(lml->lm_fpavl) == 0);
1937c478bd9Sstevel@tonic-gate 			free(lml->lm_fpavl);
1947c478bd9Sstevel@tonic-gate 		}
19557ef7aa9SRod Evans 		(void) aplist_delete_value(dynlm_list, lml);
1967c478bd9Sstevel@tonic-gate 		free(lml);
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate  * Remove a link-map.  This removes a link-map from its associated list and
2027c478bd9Sstevel@tonic-gate  * free's up the link-map itself.  Note, all components that are freed are local
2037c478bd9Sstevel@tonic-gate  * to the link-map, no inter-link-map lists are operated on as these are all
2047c478bd9Sstevel@tonic-gate  * broken down by dlclose() while all objects are still mapped.
2057c478bd9Sstevel@tonic-gate  *
2067c478bd9Sstevel@tonic-gate  * This routine is called from dlclose() to zap individual link-maps after their
2077c478bd9Sstevel@tonic-gate  * interdependencies (DEPENDS(), CALLER(), handles, etc.) have been removed.
2087c478bd9Sstevel@tonic-gate  * This routine is also called from the bowels of load_one() in the case of a
2097c478bd9Sstevel@tonic-gate  * link-map creation failure.
2107c478bd9Sstevel@tonic-gate  */
2117c478bd9Sstevel@tonic-gate void
remove_so(Lm_list * lml,Rt_map * lmp,Rt_map * clmp)212*2020b2b6SRod Evans remove_so(Lm_list *lml, Rt_map *lmp, Rt_map *clmp)
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	Dyninfo	*dip;
2157c478bd9Sstevel@tonic-gate 
216b4059b01SRod Evans 	if (lmp == NULL)
2177c478bd9Sstevel@tonic-gate 		return;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	/*
2207c478bd9Sstevel@tonic-gate 	 * Unlink the link map from the link-map list.
2217c478bd9Sstevel@tonic-gate 	 */
2227c478bd9Sstevel@tonic-gate 	if (lml && lmp)
223*2020b2b6SRod Evans 		lm_delete(lml, lmp, clmp);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/*
22610a4fa49Srie 	 * If this object contributed any local external vectors for the current
22710a4fa49Srie 	 * link-map list, remove the vectors.  If this object contributed any
22810a4fa49Srie 	 * global external vectors we should find some new candidates, or leave
22910a4fa49Srie 	 * this object lying around.
2307c478bd9Sstevel@tonic-gate 	 */
23110a4fa49Srie 	if (lml) {
23210a4fa49Srie 		int	tag;
23310a4fa49Srie 
23410a4fa49Srie 		for (tag = 0; tag < CI_MAX; tag++) {
23510a4fa49Srie 			if (lml->lm_lcs[tag].lc_lmp == lmp) {
236b4059b01SRod Evans 				lml->lm_lcs[tag].lc_lmp = NULL;
23710a4fa49Srie 				lml->lm_lcs[tag].lc_un.lc_val = 0;
23810a4fa49Srie 			}
23910a4fa49Srie 			if (glcs[tag].lc_lmp == lmp) {
240b4059b01SRod Evans 				ASSERT(glcs[tag].lc_lmp != NULL);
241b4059b01SRod Evans 				glcs[tag].lc_lmp = NULL;
24210a4fa49Srie 				glcs[tag].lc_un.lc_val = 0;
24310a4fa49Srie 			}
24410a4fa49Srie 		}
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 
2475aefb655Srie 	DBG_CALL(Dbg_file_delete(lmp));
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	/*
250*2020b2b6SRod Evans 	 * If this object is an auditor, determine whether any link-map lists
251*2020b2b6SRod Evans 	 * are maintaining cookies to represent this auditor.  These cookies
252*2020b2b6SRod Evans 	 * are established for local auditing preinit and activity events.
253*2020b2b6SRod Evans 	 */
254*2020b2b6SRod Evans 	if (FLAGS(lmp) & FLG_RT_AUDIT) {
255*2020b2b6SRod Evans 		Lm_list	*nlml;
256*2020b2b6SRod Evans 		Aliste	idx1;
257*2020b2b6SRod Evans 
258*2020b2b6SRod Evans 		for (APLIST_TRAVERSE(dynlm_list, idx1, nlml)) {
259*2020b2b6SRod Evans 			Rt_map  	*hlmp = nlml->lm_head;
260*2020b2b6SRod Evans 			Audit_client	*acp;
261*2020b2b6SRod Evans 			Aliste		idx2;
262*2020b2b6SRod Evans 
263*2020b2b6SRod Evans 			if ((hlmp == NULL) || (FLAGS(hlmp) & FLG_RT_AUDIT))
264*2020b2b6SRod Evans 				continue;
265*2020b2b6SRod Evans 
266*2020b2b6SRod Evans 			for (ALIST_TRAVERSE(nlml->lm_aud_cookies, idx2, acp)) {
267*2020b2b6SRod Evans 				if (acp->ac_lmp != lmp) {
268*2020b2b6SRod Evans 					alist_delete(nlml->lm_aud_cookies,
269*2020b2b6SRod Evans 					    &idx2);
270*2020b2b6SRod Evans 					break;
271*2020b2b6SRod Evans 				}
272*2020b2b6SRod Evans 			}
273*2020b2b6SRod Evans 		}
274*2020b2b6SRod Evans 	}
275*2020b2b6SRod Evans 
276*2020b2b6SRod Evans 	/*
277c1c6f601Srie 	 * If this is a temporary link-map, put in place to facilitate the
278c1c6f601Srie 	 * link-edit or a relocatable object, then the link-map contains no
279c1c6f601Srie 	 * information that needs to be cleaned up.
280c1c6f601Srie 	 */
281c1c6f601Srie 	if (FLAGS(lmp) & FLG_RT_OBJECT)
282c1c6f601Srie 		return;
283c1c6f601Srie 
284c1c6f601Srie 	/*
2857c478bd9Sstevel@tonic-gate 	 * Remove any FullpathNode AVL names if they still exist.
2867c478bd9Sstevel@tonic-gate 	 */
2877c478bd9Sstevel@tonic-gate 	if (FPNODE(lmp))
2887c478bd9Sstevel@tonic-gate 		fpavl_remove(lmp);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	/*
2917c478bd9Sstevel@tonic-gate 	 * Remove any alias names.
2927c478bd9Sstevel@tonic-gate 	 */
29356deab07SRod Evans 	if (ALIAS(lmp))
2947c478bd9Sstevel@tonic-gate 		free(ALIAS(lmp));
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/*
2977c478bd9Sstevel@tonic-gate 	 * Remove any of this objects filtee infrastructure.  The filtees them-
2987c478bd9Sstevel@tonic-gate 	 * selves have already been removed.
2997c478bd9Sstevel@tonic-gate 	 */
30056deab07SRod Evans 	if (((dip = DYNINFO(lmp)) != NULL) && (FLAGS1(lmp) & MSK_RT_FILTER)) {
3017c478bd9Sstevel@tonic-gate 		uint_t	cnt, max = DYNINFOCNT(lmp);
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 		for (cnt = 0; cnt < max; cnt++, dip++) {
30456deab07SRod Evans 			if ((dip->di_info == NULL) ||
30556deab07SRod Evans 			    ((dip->di_flags & MSK_DI_FILTER) == 0))
30656deab07SRod Evans 				continue;
30756deab07SRod Evans 
308*2020b2b6SRod Evans 			remove_alist((Alist **)&(dip->di_info), 1);
3097c478bd9Sstevel@tonic-gate 		}
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	/*
3137c478bd9Sstevel@tonic-gate 	 * Deallocate any remaining cruft and free the link-map.
3147c478bd9Sstevel@tonic-gate 	 */
3157c478bd9Sstevel@tonic-gate 	if (RLIST(lmp))
316*2020b2b6SRod Evans 		remove_alist(&RLIST(lmp), 1);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	if (AUDITORS(lmp))
3197247f888Srie 		audit_desc_cleanup(lmp);
3207c478bd9Sstevel@tonic-gate 	if (AUDINFO(lmp))
3217247f888Srie 		audit_info_cleanup(lmp);
3227c478bd9Sstevel@tonic-gate 
323cce0e03bSab196087 	/*
324cce0e03bSab196087 	 * Note that COPY_R() and COPY_S() reference the same memory
325cce0e03bSab196087 	 * location, and that we want to release the memory referenced
326cce0e03bSab196087 	 * without regard to which list it logically belongs to. We can
327cce0e03bSab196087 	 * use either pointer to do this.
328cce0e03bSab196087 	 */
329cce0e03bSab196087 	if (COPY_R(lmp))
330cce0e03bSab196087 		free(COPY_R(lmp));
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/*
3337c478bd9Sstevel@tonic-gate 	 * During a dlclose() any groups this object was a part of will have
3347c478bd9Sstevel@tonic-gate 	 * been torn down.  However, we can get here to remove an object that
3357c478bd9Sstevel@tonic-gate 	 * has failed to load, perhaps because its addition to a handle failed.
3367c478bd9Sstevel@tonic-gate 	 * Therefore if this object indicates that its part of a group tear
3377c478bd9Sstevel@tonic-gate 	 * these associations down.
3387c478bd9Sstevel@tonic-gate 	 */
339cce0e03bSab196087 	if (GROUPS(lmp) != NULL) {
340cce0e03bSab196087 		Aliste	idx1;
341cce0e03bSab196087 		Grp_hdl	*ghp;
3427c478bd9Sstevel@tonic-gate 
343cce0e03bSab196087 		for (APLIST_TRAVERSE(GROUPS(lmp), idx1, ghp)) {
3447c478bd9Sstevel@tonic-gate 			Grp_desc	*gdp;
345cce0e03bSab196087 			Aliste		idx2;
3467c478bd9Sstevel@tonic-gate 
347cce0e03bSab196087 			for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
3487c478bd9Sstevel@tonic-gate 				if (gdp->gd_depend != lmp)
3497c478bd9Sstevel@tonic-gate 					continue;
3507c478bd9Sstevel@tonic-gate 
351cce0e03bSab196087 				alist_delete(ghp->gh_depends, &idx2);
3527c478bd9Sstevel@tonic-gate 				break;
3537c478bd9Sstevel@tonic-gate 			}
3547c478bd9Sstevel@tonic-gate 		}
3557c478bd9Sstevel@tonic-gate 		free(GROUPS(lmp));
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 	if (HANDLES(lmp))
3587c478bd9Sstevel@tonic-gate 		free(HANDLES(lmp));
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	/*
3617c478bd9Sstevel@tonic-gate 	 * Clean up reglist if needed
3627c478bd9Sstevel@tonic-gate 	 */
36356deab07SRod Evans 	if (reglist) {
3647c478bd9Sstevel@tonic-gate 		Reglist	*cur, *prv, *del;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 		cur = prv = reglist;
36756deab07SRod Evans 		while (cur) {
3687c478bd9Sstevel@tonic-gate 			if (cur->rl_lmp == lmp) {
3697c478bd9Sstevel@tonic-gate 				del = cur;
3707c478bd9Sstevel@tonic-gate 				if (cur == reglist) {
3717c478bd9Sstevel@tonic-gate 					reglist = cur->rl_next;
3727c478bd9Sstevel@tonic-gate 					cur = prv = reglist;
3737c478bd9Sstevel@tonic-gate 				} else {
3747c478bd9Sstevel@tonic-gate 					prv->rl_next = cur->rl_next;
3757c478bd9Sstevel@tonic-gate 					cur = cur->rl_next;
3767c478bd9Sstevel@tonic-gate 				}
3777c478bd9Sstevel@tonic-gate 				free(del);
3787c478bd9Sstevel@tonic-gate 			} else {
3797c478bd9Sstevel@tonic-gate 				prv = cur;
3807c478bd9Sstevel@tonic-gate 				cur = cur->rl_next;
3817c478bd9Sstevel@tonic-gate 			}
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 	}
3847c478bd9Sstevel@tonic-gate 
3857247f888Srie 	/*
38656deab07SRod Evans 	 * If this link map represents a relocatable object concatenation, then
38756deab07SRod Evans 	 * the image was simply generated in allocated memory.  Free the memory.
38856deab07SRod Evans 	 * Note: memory maps were fabricated for the relocatable object, and
38956deab07SRod Evans 	 * the mapping infrastructure must be free'd, but there are no address
39056deab07SRod Evans 	 * mappings that must be unmapped.
3917247f888Srie 	 *
39256deab07SRod Evans 	 * Otherwise, unmap the object.
3937247f888Srie 	 */
39456deab07SRod Evans 	if (FLAGS(lmp) & FLG_RT_IMGALLOC)
39556deab07SRod Evans 		free((void *)ADDR(lmp));
39656deab07SRod Evans 
39708278a5eSRod Evans 	if (CAPCHAIN(lmp))
39808278a5eSRod Evans 		free((void *)CAPCHAIN(lmp));
39908278a5eSRod Evans 
40056deab07SRod Evans 	if (MMAPS(lmp)) {
40156deab07SRod Evans 		if ((FLAGS(lmp) & FLG_RT_IMGALLOC) == 0)
40256deab07SRod Evans 			unmap_obj(MMAPS(lmp), MMAPCNT(lmp));
40356deab07SRod Evans 		free(MMAPS(lmp));
40456deab07SRod Evans 	}
4057247f888Srie 
4067c478bd9Sstevel@tonic-gate 	free(lmp);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate /*
4107c478bd9Sstevel@tonic-gate  * Traverse an objects dependency list removing callers and dependencies.
4117c478bd9Sstevel@tonic-gate  * There's a chicken and egg problem with tearing down link-maps.  Any
41275e7992aSrie  * relationship between link-maps is maintained on a DEPENDS list, and an
41375e7992aSrie  * associated CALLERS list.  These lists can't be broken down at the time a
41475e7992aSrie  * single link-map is removed, as any related link-map may have already been
41575e7992aSrie  * removed.  Thus, lists between link-maps must be broken down before the
41675e7992aSrie  * individual link-maps themselves.
4177c478bd9Sstevel@tonic-gate  */
41856deab07SRod Evans static void
remove_lists(Rt_map * lmp,int lazy)4197c478bd9Sstevel@tonic-gate remove_lists(Rt_map *lmp, int lazy)
4207c478bd9Sstevel@tonic-gate {
421cce0e03bSab196087 	Aliste		idx1;
422cce0e03bSab196087 	Bnd_desc	*bdp;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	/*
4257c478bd9Sstevel@tonic-gate 	 * First, traverse this objects dependencies.
4267c478bd9Sstevel@tonic-gate 	 */
427cce0e03bSab196087 	for (APLIST_TRAVERSE(DEPENDS(lmp), idx1, bdp)) {
4287c478bd9Sstevel@tonic-gate 		Rt_map		*dlmp = bdp->b_depend;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 		/*
4317c478bd9Sstevel@tonic-gate 		 * Remove this object from the dependencies callers.
4327c478bd9Sstevel@tonic-gate 		 */
433cce0e03bSab196087 		(void) aplist_delete_value(CALLERS(dlmp), bdp);
4347c478bd9Sstevel@tonic-gate 		free(bdp);
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate 	if (DEPENDS(lmp)) {
4377c478bd9Sstevel@tonic-gate 		free(DEPENDS(lmp));
438cce0e03bSab196087 		DEPENDS(lmp) = NULL;
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	/*
4427c478bd9Sstevel@tonic-gate 	 * Second, traverse this objects callers.
4437c478bd9Sstevel@tonic-gate 	 */
444cce0e03bSab196087 	for (APLIST_TRAVERSE(CALLERS(lmp), idx1,  bdp)) {
4457c478bd9Sstevel@tonic-gate 		Rt_map		*clmp = bdp->b_caller;
44675e7992aSrie 		Dyninfo		*dip;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 		/*
4497c478bd9Sstevel@tonic-gate 		 * If we're removing an object that was triggered by a lazyload,
4507c478bd9Sstevel@tonic-gate 		 * remove the callers DYNINFO() entry and bump the lazy counts.
4517c478bd9Sstevel@tonic-gate 		 * This reinitialization of the lazy information allows a lazy
4527c478bd9Sstevel@tonic-gate 		 * object to be reloaded again later.  Although we may be
4537c478bd9Sstevel@tonic-gate 		 * breaking down a group of lazyloaded objects because one has
4547c478bd9Sstevel@tonic-gate 		 * failed to relocate, it's possible that one or more of the
4557c478bd9Sstevel@tonic-gate 		 * individual objects can be reloaded without a problem.
4567c478bd9Sstevel@tonic-gate 		 */
45775e7992aSrie 		if (lazy && ((dip = DYNINFO(clmp)) != NULL)) {
4587c478bd9Sstevel@tonic-gate 			uint_t	cnt, max = DYNINFOCNT(clmp);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 			for (cnt = 0; cnt < max; cnt++, dip++) {
46175e7992aSrie 				if ((dip->di_flags & FLG_DI_LAZY) == 0)
4627c478bd9Sstevel@tonic-gate 					continue;
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 				if (dip->di_info == (void *)lmp) {
465b4059b01SRod Evans 					dip->di_info = NULL;
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 					if (LAZY(clmp)++ == 0)
4687c478bd9Sstevel@tonic-gate 						LIST(clmp)->lm_lazy++;
4697c478bd9Sstevel@tonic-gate 				}
4707c478bd9Sstevel@tonic-gate 			}
4717c478bd9Sstevel@tonic-gate 		}
4727c478bd9Sstevel@tonic-gate 
473cce0e03bSab196087 		(void) aplist_delete_value(DEPENDS(clmp), bdp);
4747c478bd9Sstevel@tonic-gate 		free(bdp);
4757c478bd9Sstevel@tonic-gate 	}
4767c478bd9Sstevel@tonic-gate 	if (CALLERS(lmp)) {
4777c478bd9Sstevel@tonic-gate 		free(CALLERS(lmp));
478cce0e03bSab196087 		CALLERS(lmp) = NULL;
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate }
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate /*
4837c478bd9Sstevel@tonic-gate  * Delete any temporary link-map control list.
4847c478bd9Sstevel@tonic-gate  */
4857c478bd9Sstevel@tonic-gate void
remove_cntl(Lm_list * lml,Aliste lmco)4867c478bd9Sstevel@tonic-gate remove_cntl(Lm_list *lml, Aliste lmco)
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	Aliste	_lmco = lmco;
4897c478bd9Sstevel@tonic-gate #if	DEBUG
490cce0e03bSab196087 	Lm_cntl	*lmc;
491cce0e03bSab196087 
492cce0e03bSab196087 	lmc = (Lm_cntl *)alist_item_by_offset(lml->lm_lists, lmco);
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	/*
4957c478bd9Sstevel@tonic-gate 	 * This element should be empty.
4967c478bd9Sstevel@tonic-gate 	 */
497b4059b01SRod Evans 	ASSERT(lmc->lc_head == NULL);
4987c478bd9Sstevel@tonic-gate #endif
499cce0e03bSab196087 	alist_delete_by_offset(lml->lm_lists, &_lmco);
5007c478bd9Sstevel@tonic-gate }
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate /*
5037c478bd9Sstevel@tonic-gate  * If a lazy loaded object, or filtee fails to load, possibly because it, or
5047c478bd9Sstevel@tonic-gate  * one of its dependencies can't be relocated, then tear down any objects
5057c478bd9Sstevel@tonic-gate  * that are apart of this link-map control list.
5067c478bd9Sstevel@tonic-gate  */
50756deab07SRod Evans static void
remove_incomplete(Lm_list * lml,Aliste lmco,Rt_map * clmp)508*2020b2b6SRod Evans remove_incomplete(Lm_list *lml, Aliste lmco, Rt_map *clmp)
5097c478bd9Sstevel@tonic-gate {
5107c478bd9Sstevel@tonic-gate 	Rt_map	*lmp;
5117c478bd9Sstevel@tonic-gate 	Lm_cntl	*lmc;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/* LINTED */
514cce0e03bSab196087 	lmc = (Lm_cntl *)alist_item_by_offset(lml->lm_lists, lmco);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/*
517*2020b2b6SRod Evans 	 * If auditing is in effect, the loading of these objects might have
518*2020b2b6SRod Evans 	 * resulted in la_objopen() events being posted.  Normally, an
519*2020b2b6SRod Evans 	 * la_objclose() event is posted after an object's .fini is executed,
520*2020b2b6SRod Evans 	 * just before the objects are unloaded.  These failed objects do not
521*2020b2b6SRod Evans 	 * have their .fini's executed, but an la_objclose() event should still
522*2020b2b6SRod Evans 	 * be posted to any auditors.
523*2020b2b6SRod Evans 	 */
524*2020b2b6SRod Evans 	if ((lml->lm_tflags | AFLAGS(clmp)) & LML_TFLG_AUD_OBJCLOSE) {
525*2020b2b6SRod Evans 		for (lmp = lmc->lc_head; lmp; lmp = NEXT_RT_MAP(lmp))
526*2020b2b6SRod Evans 			audit_objclose(lmp, clmp);
527*2020b2b6SRod Evans 	}
528*2020b2b6SRod Evans 
529*2020b2b6SRod Evans 	/*
530*2020b2b6SRod Evans 	 * Remove any lists that may point between objects.
5317c478bd9Sstevel@tonic-gate 	 */
532cb511613SAli Bahrami 	for (lmp = lmc->lc_head; lmp; lmp = NEXT_RT_MAP(lmp))
5337c478bd9Sstevel@tonic-gate 		remove_lists(lmp, 1);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	/*
5367c478bd9Sstevel@tonic-gate 	 * Finally, remove each object.  remove_so() calls lm_delete(), thus
5377c478bd9Sstevel@tonic-gate 	 * effectively the link-map control head gets updated to point to the
5387c478bd9Sstevel@tonic-gate 	 * next link-map.
5397c478bd9Sstevel@tonic-gate 	 */
540b4059b01SRod Evans 	while ((lmp = lmc->lc_head) != NULL)
541*2020b2b6SRod Evans 		remove_so(lml, lmp, clmp);
5427c478bd9Sstevel@tonic-gate 
543b4059b01SRod Evans 	lmc->lc_head = lmc->lc_tail = NULL;
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate /*
5477c478bd9Sstevel@tonic-gate  * Determine whether an object is deletable.
5487c478bd9Sstevel@tonic-gate  */
549cce0e03bSab196087 static int
is_deletable(APlist ** lmalp,APlist ** ghalp,Rt_map * lmp)550cce0e03bSab196087 is_deletable(APlist **lmalp, APlist **ghalp, Rt_map *lmp)
5517c478bd9Sstevel@tonic-gate {
552cce0e03bSab196087 	Aliste		idx;
553cce0e03bSab196087 	Bnd_desc	*bdp;
554cce0e03bSab196087 	Grp_hdl		*ghp;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	/*
5577c478bd9Sstevel@tonic-gate 	 * If the object hasn't yet been relocated take this as a sign that
5587c478bd9Sstevel@tonic-gate 	 * it's loading failed, thus we're here to cleanup.  If the object is
5597c478bd9Sstevel@tonic-gate 	 * relocated it will only be retained if it was marked non-deletable,
5607c478bd9Sstevel@tonic-gate 	 * and exists on the main link-map control list.
5617c478bd9Sstevel@tonic-gate 	 */
5627c478bd9Sstevel@tonic-gate 	if ((FLAGS(lmp) & FLG_RT_RELOCED) &&
563cce0e03bSab196087 	    (MODE(lmp) & RTLD_NODELETE) && (CNTL(lmp) == ALIST_OFF_DATA))
5647c478bd9Sstevel@tonic-gate 		return (0);
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	/*
5677c478bd9Sstevel@tonic-gate 	 * If this object is the head of a handle that has not been captured as
5687c478bd9Sstevel@tonic-gate 	 * a candidate for deletion, then this object is in use from a dlopen()
5697c478bd9Sstevel@tonic-gate 	 * outside of the scope of this dlclose() family.  Dlopen'ed objects,
5707c478bd9Sstevel@tonic-gate 	 * and filtees, have group descriptors for their callers.  Typically
5717c478bd9Sstevel@tonic-gate 	 * this parent will have callers that are not apart of this dlclose()
5727c478bd9Sstevel@tonic-gate 	 * family, and thus would be caught by the CALLERS test below.  However,
5737c478bd9Sstevel@tonic-gate 	 * if the caller had itself been dlopen'ed, it may not have any explicit
574*2020b2b6SRod Evans 	 * callers registered for itself.  Thus, by looking for objects with
5757c478bd9Sstevel@tonic-gate 	 * handles we can ferret out these outsiders.
5767c478bd9Sstevel@tonic-gate 	 */
577cce0e03bSab196087 	for (APLIST_TRAVERSE(HANDLES(lmp), idx, ghp)) {
5782017c965SRod Evans 		/*
5792017c965SRod Evans 		 * If this is a private handle, then the handle isn't referenced
5802017c965SRod Evans 		 * from outside of the group of objects being deleted, and can
5812017c965SRod Evans 		 * be ignored when evaluating objects for deletion.
5822017c965SRod Evans 		 */
5832017c965SRod Evans 		if (ghp->gh_flags & GPH_PRIVATE)
5842017c965SRod Evans 			continue;
585cce0e03bSab196087 		if (aplist_test(ghalp, ghp, 0) != ALE_EXISTS)
5867c478bd9Sstevel@tonic-gate 			return (0);
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	/*
5907c478bd9Sstevel@tonic-gate 	 * If this object is called by any object outside of the family of
5917c478bd9Sstevel@tonic-gate 	 * objects selected for deletion, it can't be deleted.
5927c478bd9Sstevel@tonic-gate 	 */
593cce0e03bSab196087 	for (APLIST_TRAVERSE(CALLERS(lmp), idx, bdp)) {
5942017c965SRod Evans 		if (aplist_test(lmalp, bdp->b_caller, 0) != ALE_EXISTS)
5957c478bd9Sstevel@tonic-gate 			return (0);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/*
5997c478bd9Sstevel@tonic-gate 	 * This object is a candidate for deletion.
6007c478bd9Sstevel@tonic-gate 	 */
6017c478bd9Sstevel@tonic-gate 	return (1);
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate /*
6057c478bd9Sstevel@tonic-gate  * Collect the groups (handles) and associated objects that are candidates for
6067c478bd9Sstevel@tonic-gate  * deletion.  The criteria for deleting an object is whether it is only refer-
6077c478bd9Sstevel@tonic-gate  * enced from the objects within the groups that are candidates for deletion.
6087c478bd9Sstevel@tonic-gate  */
6097c478bd9Sstevel@tonic-gate static int
gdp_collect(APlist ** ghalpp,APlist ** lmalpp,Grp_hdl * ghp1)610cce0e03bSab196087 gdp_collect(APlist **ghalpp, APlist **lmalpp, Grp_hdl *ghp1)
6117c478bd9Sstevel@tonic-gate {
61256deab07SRod Evans 	Aliste		idx1;
6137c478bd9Sstevel@tonic-gate 	Grp_desc	*gdp;
6147c478bd9Sstevel@tonic-gate 	int		action;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	/*
6177c478bd9Sstevel@tonic-gate 	 * Add this group to our group collection.  If it isn't added either an
6187c478bd9Sstevel@tonic-gate 	 * allocation has failed, or it already exists.
6197c478bd9Sstevel@tonic-gate 	 */
620cce0e03bSab196087 	if ((action = aplist_test(ghalpp, ghp1, AL_CNT_GRPCLCT)) !=
621cce0e03bSab196087 	    ALE_CREATE)
6227c478bd9Sstevel@tonic-gate 		return (action);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	/*
6257c478bd9Sstevel@tonic-gate 	 * Traverse the dependencies of the group and collect the associated
6267c478bd9Sstevel@tonic-gate 	 * objects.
6277c478bd9Sstevel@tonic-gate 	 */
62856deab07SRod Evans 	for (ALIST_TRAVERSE(ghp1->gh_depends, idx1, gdp)) {
6297c478bd9Sstevel@tonic-gate 		Rt_map	*lmp = gdp->gd_depend;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 		/*
6327c478bd9Sstevel@tonic-gate 		 * We only want to process dependencies for deletion.  Although
6337c478bd9Sstevel@tonic-gate 		 * we want to purge group descriptors for parents, we don't want
6347c478bd9Sstevel@tonic-gate 		 * to analyze the parent itself for additional filters or
6357c478bd9Sstevel@tonic-gate 		 * deletion.
6367c478bd9Sstevel@tonic-gate 		 */
637efb9e8b8Srie 		if ((gdp->gd_flags & GPD_PARENT) ||
638efb9e8b8Srie 		    ((gdp->gd_flags & GPD_ADDEPS) == 0))
6397c478bd9Sstevel@tonic-gate 			continue;
6407c478bd9Sstevel@tonic-gate 
641cce0e03bSab196087 		if ((action = aplist_test(lmalpp, lmp, AL_CNT_GRPCLCT)) ==
642cce0e03bSab196087 		    ALE_ALLOCFAIL)
6437c478bd9Sstevel@tonic-gate 			return (0);
6447c478bd9Sstevel@tonic-gate 		if (action == ALE_EXISTS)
6457c478bd9Sstevel@tonic-gate 			continue;
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 		/*
6481c272b97Srie 		 * If this object is a candidate for deletion, determine if the
6491c272b97Srie 		 * object provides any filtees.  If so, the filter groups are
6501c272b97Srie 		 * added to the group collection.
6511c272b97Srie 		 *
6521c272b97Srie 		 * An object is a candidate for deletion if:
6531c272b97Srie 		 *
6542017c965SRod Evans 		 *  -	the object hasn't yet been relocated, in which case
6551c272b97Srie 		 *	we're here to clean up a failed load, or
6562017c965SRod Evans 		 *  -	the object doesn't reside on the base link-map control
6571c272b97Srie 		 *	list, in which case a group of objects, typically
6581c272b97Srie 		 *	lazily loaded, or filtees, need cleaning up, or
6592017c965SRod Evans 		 *  -	the object isn't tagged as non-deletable.
6607c478bd9Sstevel@tonic-gate 		 */
6617c478bd9Sstevel@tonic-gate 		if ((((FLAGS(lmp) & FLG_RT_RELOCED) == 0) ||
662cce0e03bSab196087 		    (CNTL(lmp) != ALIST_OFF_DATA) ||
6637c478bd9Sstevel@tonic-gate 		    ((MODE(lmp) & RTLD_NODELETE) == 0)) &&
6647c478bd9Sstevel@tonic-gate 		    (FLAGS1(lmp) & MSK_RT_FILTER)) {
6657c478bd9Sstevel@tonic-gate 			Dyninfo	*dip = DYNINFO(lmp);
6667c478bd9Sstevel@tonic-gate 			uint_t	cnt, max = DYNINFOCNT(lmp);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 			for (cnt = 0; cnt < max; cnt++, dip++) {
66956deab07SRod Evans 				Alist	*falp;
67056deab07SRod Evans 				Aliste	idx2;
67156deab07SRod Evans 				Pdesc	*pdp;
6727c478bd9Sstevel@tonic-gate 
67356deab07SRod Evans 				if (((falp = (Alist *)dip->di_info) == NULL) ||
6747c478bd9Sstevel@tonic-gate 				    ((dip->di_flags & MSK_DI_FILTER) == 0))
6757c478bd9Sstevel@tonic-gate 					continue;
6767c478bd9Sstevel@tonic-gate 
67756deab07SRod Evans 				for (ALIST_TRAVERSE(falp, idx2, pdp)) {
6787c478bd9Sstevel@tonic-gate 					Grp_hdl	*ghp2;
6797c478bd9Sstevel@tonic-gate 
68056deab07SRod Evans 					if ((pdp->pd_plen == 0) || ((ghp2 =
68156deab07SRod Evans 					    (Grp_hdl *)pdp->pd_info) == NULL))
6827c478bd9Sstevel@tonic-gate 						continue;
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 					if (gdp_collect(ghalpp, lmalpp,
6857c478bd9Sstevel@tonic-gate 					    ghp2) == 0)
6867c478bd9Sstevel@tonic-gate 						return (0);
6877c478bd9Sstevel@tonic-gate 				}
6887c478bd9Sstevel@tonic-gate 			}
6897c478bd9Sstevel@tonic-gate 		}
6907c478bd9Sstevel@tonic-gate 	}
6917c478bd9Sstevel@tonic-gate 	return (1);
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate /*
6957c478bd9Sstevel@tonic-gate  * Traverse the list of deletable candidates.  If an object can't be deleted
6967c478bd9Sstevel@tonic-gate  * then neither can its dependencies or filtees.  Any object that is cleared
6977c478bd9Sstevel@tonic-gate  * from being deleted drops the deletion count, plus, if there are no longer
6987c478bd9Sstevel@tonic-gate  * any deletions pending we can discontinue any further processing.
6997c478bd9Sstevel@tonic-gate  */
7007c478bd9Sstevel@tonic-gate static int
remove_rescan(APlist * lmalp,APlist * ghalp,int * delcnt)701cce0e03bSab196087 remove_rescan(APlist *lmalp, APlist *ghalp, int *delcnt)
7027c478bd9Sstevel@tonic-gate {
703cce0e03bSab196087 	Aliste		idx1;
704cce0e03bSab196087 	Rt_map		*lmp;
7057c478bd9Sstevel@tonic-gate 	int		rescan = 0;
7067c478bd9Sstevel@tonic-gate 
707cce0e03bSab196087 	for (APLIST_TRAVERSE(lmalp, idx1, lmp)) {
708cce0e03bSab196087 		Aliste		idx2;
709cce0e03bSab196087 		Bnd_desc	*bdp;
7107c478bd9Sstevel@tonic-gate 		Dyninfo		*dip;
7117c478bd9Sstevel@tonic-gate 		uint_t		cnt, max;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 		if (FLAGS(lmp) & FLG_RT_DELETE)
7147c478bd9Sstevel@tonic-gate 			continue;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 		/*
7177c478bd9Sstevel@tonic-gate 		 * As this object can't be deleted, make sure its dependencies
7187c478bd9Sstevel@tonic-gate 		 * aren't deleted either.
7197c478bd9Sstevel@tonic-gate 		 */
720cce0e03bSab196087 		for (APLIST_TRAVERSE(DEPENDS(lmp), idx2, bdp)) {
721cce0e03bSab196087 			Rt_map	*dlmp = bdp->b_depend;
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 			if (FLAGS(dlmp) & FLG_RT_DELETE) {
7247c478bd9Sstevel@tonic-gate 				FLAGS(dlmp) &= ~FLG_RT_DELETE;
7257c478bd9Sstevel@tonic-gate 				if (--(*delcnt) == 0)
7267c478bd9Sstevel@tonic-gate 					return (0);
7277c478bd9Sstevel@tonic-gate 				rescan = 1;
7287c478bd9Sstevel@tonic-gate 			}
7297c478bd9Sstevel@tonic-gate 		}
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 		/*
7327c478bd9Sstevel@tonic-gate 		 * If this object is a filtee and one of its filters is outside
7337c478bd9Sstevel@tonic-gate 		 * of this dlclose family, then it can't be deleted either.
7347c478bd9Sstevel@tonic-gate 		 */
7357c478bd9Sstevel@tonic-gate 		if ((FLAGS1(lmp) & MSK_RT_FILTER) == 0)
7367c478bd9Sstevel@tonic-gate 			continue;
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 		dip = DYNINFO(lmp);
7397c478bd9Sstevel@tonic-gate 		max = DYNINFOCNT(lmp);
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		for (cnt = 0; cnt < max; cnt++, dip++) {
74256deab07SRod Evans 			Alist	*falp;
74356deab07SRod Evans 			Pdesc	*pdp;
7447c478bd9Sstevel@tonic-gate 
74556deab07SRod Evans 			if (((falp = (Alist *)dip->di_info) == NULL) ||
7467c478bd9Sstevel@tonic-gate 			    ((dip->di_flags & MSK_DI_FILTER) == 0))
7477c478bd9Sstevel@tonic-gate 				continue;
7487c478bd9Sstevel@tonic-gate 
74956deab07SRod Evans 			for (ALIST_TRAVERSE(falp, idx2, pdp)) {
75056deab07SRod Evans 				Aliste		idx3;
7517c478bd9Sstevel@tonic-gate 				Grp_hdl		*ghp;
7527c478bd9Sstevel@tonic-gate 				Grp_desc	*gdp;
7537c478bd9Sstevel@tonic-gate 
75456deab07SRod Evans 				if ((pdp->pd_plen == 0) ||
75556deab07SRod Evans 				    ((ghp = (Grp_hdl *)pdp->pd_info) == NULL))
7567c478bd9Sstevel@tonic-gate 					continue;
7577c478bd9Sstevel@tonic-gate 
758cce0e03bSab196087 				if (aplist_test(&ghalp, ghp, 0) ==
759cce0e03bSab196087 				    ALE_EXISTS)
7607c478bd9Sstevel@tonic-gate 					continue;
7617c478bd9Sstevel@tonic-gate 
76256deab07SRod Evans 				for (ALIST_TRAVERSE(ghp->gh_depends, idx3,
7637c478bd9Sstevel@tonic-gate 				    gdp)) {
7647c478bd9Sstevel@tonic-gate 					Rt_map	*dlmp = gdp->gd_depend;
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 					if (FLAGS(dlmp) & FLG_RT_DELETE) {
7677c478bd9Sstevel@tonic-gate 						FLAGS(dlmp) &= ~FLG_RT_DELETE;
7687c478bd9Sstevel@tonic-gate 						if (--(*delcnt) == 0)
7697c478bd9Sstevel@tonic-gate 							return (0);
7707c478bd9Sstevel@tonic-gate 						rescan = 1;
7717c478bd9Sstevel@tonic-gate 					}
7727c478bd9Sstevel@tonic-gate 				}
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 				/*
7757c478bd9Sstevel@tonic-gate 				 * Remove this group handle from our dynamic
7767c478bd9Sstevel@tonic-gate 				 * deletion list.
7777c478bd9Sstevel@tonic-gate 				 */
778cce0e03bSab196087 				(void) aplist_delete_value(ghalp, ghp);
7797c478bd9Sstevel@tonic-gate 			}
7807c478bd9Sstevel@tonic-gate 		}
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate 	return (rescan);
7837c478bd9Sstevel@tonic-gate }
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate /*
7867c478bd9Sstevel@tonic-gate  * Cleanup any collection alists we've created.
7877c478bd9Sstevel@tonic-gate  */
7887c478bd9Sstevel@tonic-gate static void
remove_collect(APlist * ghalp,APlist * lmalp)789cce0e03bSab196087 remove_collect(APlist *ghalp, APlist *lmalp)
7907c478bd9Sstevel@tonic-gate {
7917c478bd9Sstevel@tonic-gate 	if (ghalp)
7927c478bd9Sstevel@tonic-gate 		free(ghalp);
7937c478bd9Sstevel@tonic-gate 	if (lmalp)
7947c478bd9Sstevel@tonic-gate 		free(lmalp);
7957c478bd9Sstevel@tonic-gate }
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate /*
7982017c965SRod Evans  * Remove a handle, leaving the associated objects intact.
7997c478bd9Sstevel@tonic-gate  */
8007c478bd9Sstevel@tonic-gate void
free_hdl(Grp_hdl * ghp)8012017c965SRod Evans free_hdl(Grp_hdl *ghp)
8027c478bd9Sstevel@tonic-gate {
8032017c965SRod Evans 	if (--(ghp->gh_refcnt) == 0) {
8047c478bd9Sstevel@tonic-gate 		Grp_desc	*gdp;
805cce0e03bSab196087 		Aliste		idx;
8068af2c5b9Srie 		uintptr_t	ndx;
8078af2c5b9Srie 
808cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
8097c478bd9Sstevel@tonic-gate 			Rt_map	*lmp = gdp->gd_depend;
8107c478bd9Sstevel@tonic-gate 
8115aefb655Srie 			if (ghp->gh_ownlmp == lmp)
812cce0e03bSab196087 				(void) aplist_delete_value(HANDLES(lmp), ghp);
813cce0e03bSab196087 			(void) aplist_delete_value(GROUPS(lmp), ghp);
8147c478bd9Sstevel@tonic-gate 		}
8157c478bd9Sstevel@tonic-gate 		(void) free(ghp->gh_depends);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 		/* LINTED */
8187c478bd9Sstevel@tonic-gate 		ndx = (uintptr_t)ghp % HDLIST_SZ;
81956deab07SRod Evans 		(void) aplist_delete_value(hdl_alp[ndx], ghp);
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 		(void) free(ghp);
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate /*
82602ca3e02Srie  * If a load operation, using a new link-map control list, has failed, then
82702ca3e02Srie  * forcibly remove the failed objects.  This failure can occur as a result
82802ca3e02Srie  * of a lazy load, a dlopen(), or a filtee load, once the application is
82902ca3e02Srie  * running.  If the link-map control list has not yet started relocation, then
83002ca3e02Srie  * cleanup is simply a process of removing all the objects from the control
83102ca3e02Srie  * list.  If relocation has begun, then other loads may have been triggered to
83202ca3e02Srie  * satisfy the relocations, and thus we need to break down the control list
83302ca3e02Srie  * using handles.
83402ca3e02Srie  *
83502ca3e02Srie  * The objects associated with this load must be part of a unique handle.  In
83602ca3e02Srie  * the case of a dlopen() or filtee request, a handle will have been created.
83702ca3e02Srie  * For a lazyload request, a handle must be generated so that the remove
83802ca3e02Srie  * process can use the handle.
83902ca3e02Srie  *
84002ca3e02Srie  * During the course of processing these objects, other objects (handles) may
84102ca3e02Srie  * have been loaded to satisfy relocation requirements.  After these families
84202ca3e02Srie  * have successfully loaded, they will have been propagated to the same link-map
84302ca3e02Srie  * control list.  The failed objects need to be removed from this list, while
84402ca3e02Srie  * any successfully loaded families can be left alone, and propagated to the
84502ca3e02Srie  * previous link-map control list.  By associating each load request with a
84602ca3e02Srie  * handle, we can isolate the failed objects while not interfering with any
84702ca3e02Srie  * successfully loaded families.
8487c478bd9Sstevel@tonic-gate  */
8497c478bd9Sstevel@tonic-gate void
remove_lmc(Lm_list * lml,Rt_map * clmp,Aliste lmco,const char * name)850481bba9eSRod Evans remove_lmc(Lm_list *lml, Rt_map *clmp, Aliste lmco, const char *name)
8517c478bd9Sstevel@tonic-gate {
85202ca3e02Srie 	Grp_hdl		*ghp;
8537c478bd9Sstevel@tonic-gate 	Grp_desc	*gdp;
854cce0e03bSab196087 	Aliste		idx;
855481bba9eSRod Evans 	Lm_cntl		*lmc;
85602ca3e02Srie 	Rt_map		*lmp;
8577c478bd9Sstevel@tonic-gate 
858481bba9eSRod Evans 	/*
859481bba9eSRod Evans 	 * Determine the link-map control list, and whether any object has been
860481bba9eSRod Evans 	 * added to this list.
861481bba9eSRod Evans 	 */
862481bba9eSRod Evans 	/* LINTED */
863481bba9eSRod Evans 	lmc = (Lm_cntl *)alist_item_by_offset(lml->lm_lists, lmco);
864481bba9eSRod Evans 	if (lmc->lc_head == NULL)
865481bba9eSRod Evans 		return;
866481bba9eSRod Evans 
86702ca3e02Srie 	DBG_CALL(Dbg_file_cleanup(lml, name, lmco));
8687c478bd9Sstevel@tonic-gate 
86902ca3e02Srie 	/*
87002ca3e02Srie 	 * Obtain a handle for the first object on the link-map control list.
87102ca3e02Srie 	 * If none exists (which would occur from a lazy load request), and
87202ca3e02Srie 	 * the link-map control list is being relocated, create a handle.
87302ca3e02Srie 	 */
87402ca3e02Srie 	lmp = lmc->lc_head;
87502ca3e02Srie 	if (HANDLES(lmp)) {
876cce0e03bSab196087 		ghp = (Grp_hdl *)HANDLES(lmp)->apl_data[0];
87702ca3e02Srie 
8782017c965SRod Evans 		/*
8792017c965SRod Evans 		 * If this is a private handle, remove this state, so as to
8802017c965SRod Evans 		 * prevent any attempt to remove the handle more than once.
8812017c965SRod Evans 		 */
8822017c965SRod Evans 		ghp->gh_flags &= ~GPH_PRIVATE;
8832017c965SRod Evans 
88402ca3e02Srie 	} else if (lmc->lc_flags & LMC_FLG_RELOCATING) {
88502ca3e02Srie 		/*
88602ca3e02Srie 		 * Establish a handle, and should anything fail, fall through
88702ca3e02Srie 		 * to remove the link-map control list.
88802ca3e02Srie 		 */
8892017c965SRod Evans 		if (((ghp = hdl_create(lml, lmc->lc_head, NULL, GPH_PUBLIC,
890b4059b01SRod Evans 		    GPD_ADDEPS, 0)) == NULL) ||
89102ca3e02Srie 		    (hdl_initialize(ghp, lmc->lc_head, 0, 0) == 0))
89202ca3e02Srie 			lmc->lc_flags &= ~LMC_FLG_RELOCATING;
893ed98f06cSrie 	} else {
894b4059b01SRod Evans 		ghp = NULL;
89502ca3e02Srie 	}
89602ca3e02Srie 
89702ca3e02Srie 	/*
89802ca3e02Srie 	 * If relocation hasn't begun, simply remove all the objects from this
89902ca3e02Srie 	 * list, and any handle that may have been created.
90002ca3e02Srie 	 */
90102ca3e02Srie 	if ((lmc->lc_flags & LMC_FLG_RELOCATING) == 0) {
902*2020b2b6SRod Evans 		remove_incomplete(lml, lmco, clmp);
90302ca3e02Srie 
90402ca3e02Srie 		if (ghp) {
90502ca3e02Srie 			ghp->gh_refcnt = 1;
9062017c965SRod Evans 			free_hdl(ghp);
90702ca3e02Srie 		}
90802ca3e02Srie 		return;
90902ca3e02Srie 	}
91002ca3e02Srie 
911b4059b01SRod Evans 	ASSERT(ghp != NULL);
912ed98f06cSrie 
91302ca3e02Srie 	/*
91402ca3e02Srie 	 * As the objects of this handle are being forcibly removed, first
91502ca3e02Srie 	 * remove any associations to objects on parent link-map control
91602ca3e02Srie 	 * lists.  This breaks the bond between a caller and a hierarchy of
91702ca3e02Srie 	 * dependencies represented by the handle, thus the caller doesn't lock
91802ca3e02Srie 	 * the hierarchy and prevent their deletion from the generic handle
91902ca3e02Srie 	 * processing or remove_hdl().
92002ca3e02Srie 	 *
92102ca3e02Srie 	 * This scenario can be produced when the relocation of a object
92202ca3e02Srie 	 * results in vectoring through a filter that is already loaded.  The
92302ca3e02Srie 	 * filtee may be on the link-map list that is presently being processed,
92402ca3e02Srie 	 * however an association between the filter and filtee would have been
92502ca3e02Srie 	 * established during filtee processing.  It is this association that
92602ca3e02Srie 	 * must be broken to allow the objects on this link-map list to be
92702ca3e02Srie 	 * removed.
92802ca3e02Srie 	 */
929cce0e03bSab196087 	for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
9307c478bd9Sstevel@tonic-gate 		Rt_map	*lmp = gdp->gd_depend;
9317c478bd9Sstevel@tonic-gate 
93202ca3e02Srie 		/*
93302ca3e02Srie 		 * If this object has not been relocated, break down any
93402ca3e02Srie 		 * dependency relationships the object might have established.
93502ca3e02Srie 		 */
93602ca3e02Srie 		if ((FLAGS(lmp) & FLG_RT_RELOCED) == 0)
93702ca3e02Srie 			remove_lists(lmp, 1);
93802ca3e02Srie 
93902ca3e02Srie 		if (CNTL(lmp) == lmco)
94002ca3e02Srie 			continue;
94102ca3e02Srie 
94202ca3e02Srie 		if (gdp->gd_flags & GPD_FILTER) {
94302ca3e02Srie 			Dyninfo	*dip = DYNINFO(lmp);
94402ca3e02Srie 			uint_t	cnt, max = DYNINFOCNT(lmp);
94502ca3e02Srie 
94602ca3e02Srie 			for (cnt = 0; cnt < max; cnt++, dip++) {
94756deab07SRod Evans 				Alist	*falp;
94856deab07SRod Evans 				Aliste	idx2;
94956deab07SRod Evans 				Pdesc	*pdp;
95002ca3e02Srie 
95156deab07SRod Evans 				if (((falp = (Alist *)dip->di_info) == NULL) ||
95202ca3e02Srie 				    ((dip->di_flags & MSK_DI_FILTER) == 0))
95302ca3e02Srie 					continue;
95402ca3e02Srie 
95556deab07SRod Evans 				for (ALIST_TRAVERSE(falp, idx2, pdp)) {
95656deab07SRod Evans 					if ((Grp_hdl *)pdp->pd_info == ghp) {
957b4059b01SRod Evans 						pdp->pd_info = NULL;
9587c478bd9Sstevel@tonic-gate 						break;
9597c478bd9Sstevel@tonic-gate 					}
9607c478bd9Sstevel@tonic-gate 				}
9617c478bd9Sstevel@tonic-gate 			}
96202ca3e02Srie 		}
963cce0e03bSab196087 		(void) aplist_delete_value(GROUPS(lmp), ghp);
964cce0e03bSab196087 		alist_delete(ghp->gh_depends, &idx);
96502ca3e02Srie 	}
96602ca3e02Srie 
96702ca3e02Srie 	/*
96802ca3e02Srie 	 * Having removed any callers, set the group handle reference count to
96902ca3e02Srie 	 * one, and let the generic handle remover delete the associated
97002ca3e02Srie 	 * objects.
97102ca3e02Srie 	 */
97202ca3e02Srie 	ghp->gh_refcnt = 1;
973b4059b01SRod Evans 	(void) remove_hdl(ghp, clmp, NULL);
97402ca3e02Srie 
97502ca3e02Srie 	/*
97602ca3e02Srie 	 * If this link-map control list still contains objects, determine the
97702ca3e02Srie 	 * previous control list and move the objects.
97802ca3e02Srie 	 */
97902ca3e02Srie 	if (lmc->lc_head) {
98002ca3e02Srie 		Lm_cntl *plmc;
98102ca3e02Srie 		Aliste  plmco;
98202ca3e02Srie 
98302ca3e02Srie 		plmco = lmco - lml->lm_lists->al_size;
98402ca3e02Srie 		/* LINTED */
985cce0e03bSab196087 		plmc = (Lm_cntl *)alist_item_by_offset(lml->lm_lists, plmco);
98602ca3e02Srie 
98702ca3e02Srie 		lm_move(lml, lmco, plmco, lmc, plmc);
98802ca3e02Srie 	}
98902ca3e02Srie }
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate /*
9927c478bd9Sstevel@tonic-gate  * Remove the objects associated with a handle.  There are two goals here, to
9937c478bd9Sstevel@tonic-gate  * delete the objects associated with the handle, and to remove the handle
9947c478bd9Sstevel@tonic-gate  * itself.  Things get a little more complex if the objects selected for
9957c478bd9Sstevel@tonic-gate  * deletion are filters, in this case we also need to collect their filtees,
9967c478bd9Sstevel@tonic-gate  * and process the combined groups as a whole.  But, care still must be exer-
9977c478bd9Sstevel@tonic-gate  * cised to make sure any filtees found aren't being used by filters outside of
9987c478bd9Sstevel@tonic-gate  * the groups we've collect.  The series of events is basically:
9997c478bd9Sstevel@tonic-gate  *
10002017c965SRod Evans  *  -	Determine the groups (handles) that might be deletable.
10017c478bd9Sstevel@tonic-gate  *
10022017c965SRod Evans  *  -	Determine the objects of these handles that can be deleted.
10037c478bd9Sstevel@tonic-gate  *
10042017c965SRod Evans  *  -	Fire the fini's of those objects selected for deletion.
10057c478bd9Sstevel@tonic-gate  *
10062017c965SRod Evans  *  -	Remove all inter-dependency linked lists while the objects link-maps
10077c478bd9Sstevel@tonic-gate  *	are still available.
10087c478bd9Sstevel@tonic-gate  *
10092017c965SRod Evans  *  -	Remove all deletable objects link-maps and unmap the objects themselves.
10107c478bd9Sstevel@tonic-gate  *
10112017c965SRod Evans  *  -	Remove the handle descriptors for each deleted object, and hopefully
10127c478bd9Sstevel@tonic-gate  *	the whole handle.
10137c478bd9Sstevel@tonic-gate  *
1014*2020b2b6SRod Evans  * A handle that can't be deleted is added to an orphans list.  This list is
10157c478bd9Sstevel@tonic-gate  * revisited any time another dlclose() request results in handle descriptors
10167c478bd9Sstevel@tonic-gate  * being deleted.  These deleted descriptors can be sufficient to allow the
10177c478bd9Sstevel@tonic-gate  * final deletion of the orphaned handles.
10187c478bd9Sstevel@tonic-gate  */
10197c478bd9Sstevel@tonic-gate int
remove_hdl(Grp_hdl * ghp,Rt_map * clmp,int * removed)10207c478bd9Sstevel@tonic-gate remove_hdl(Grp_hdl *ghp, Rt_map *clmp, int *removed)
10217c478bd9Sstevel@tonic-gate {
1022cce0e03bSab196087 	Rt_map		*lmp;
10237c478bd9Sstevel@tonic-gate 	int		rescan = 0;
10247c478bd9Sstevel@tonic-gate 	int		delcnt = 0, rmcnt = 0, error = 0, orphans;
1025cce0e03bSab196087 	APlist		*lmalp = NULL, *ghalp = NULL;
1026cce0e03bSab196087 	Aliste		idx1, idx2;
1027cce0e03bSab196087 	Grp_hdl		*ghp2;
10287c478bd9Sstevel@tonic-gate 	Grp_desc	*gdp;
1029cce0e03bSab196087 	Lm_list		*lml = NULL;
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	/*
10327c478bd9Sstevel@tonic-gate 	 * Generate the family of groups and objects that are candidates for
10337c478bd9Sstevel@tonic-gate 	 * deletion.  This consists of the objects that are explicitly defined
10347c478bd9Sstevel@tonic-gate 	 * as dependencies of this handle, plus any filtee handles and their
10357c478bd9Sstevel@tonic-gate 	 * associated objects.
10367c478bd9Sstevel@tonic-gate 	 */
10377c478bd9Sstevel@tonic-gate 	if (gdp_collect(&ghalp, &lmalp, ghp) == 0) {
10387c478bd9Sstevel@tonic-gate 		remove_collect(ghalp, lmalp);
10397c478bd9Sstevel@tonic-gate 		return (0);
10407c478bd9Sstevel@tonic-gate 	}
10417c478bd9Sstevel@tonic-gate 
10428af2c5b9Srie 	DBG_CALL(Dbg_file_hdl_title(DBG_HDL_DELETE));
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 	/*
10457c478bd9Sstevel@tonic-gate 	 * Traverse the groups we've collected to determine if any filtees are
10467c478bd9Sstevel@tonic-gate 	 * included.  If so, and the filtee handle is in use by a filter outside
10477c478bd9Sstevel@tonic-gate 	 * of the family of objects collected for this deletion, it can not be
10487c478bd9Sstevel@tonic-gate 	 * removed.
10497c478bd9Sstevel@tonic-gate 	 */
1050cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1051cce0e03bSab196087 		Grp_hdl	*ghp = ghp2;
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 		DBG_CALL(Dbg_file_hdl_collect(ghp, 0));
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 		if ((ghp->gh_flags & GPH_FILTEE) == 0)
10567c478bd9Sstevel@tonic-gate 			continue;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 		/*
10597c478bd9Sstevel@tonic-gate 		 * Special case for ld.so.1.  There can be multiple instances of
10607c478bd9Sstevel@tonic-gate 		 * libdl.so.1 using this handle, so although we want the handles
10617c478bd9Sstevel@tonic-gate 		 * reference count to be decremented, we don't want the handle
10627c478bd9Sstevel@tonic-gate 		 * removed.
10637c478bd9Sstevel@tonic-gate 		 */
10647c478bd9Sstevel@tonic-gate 		if (ghp->gh_flags & GPH_LDSO) {
10657c478bd9Sstevel@tonic-gate 			DBG_CALL(Dbg_file_hdl_collect(ghp,
10667c478bd9Sstevel@tonic-gate 			    NAME(lml_rtld.lm_head)));
1067cce0e03bSab196087 			aplist_delete(ghalp, &idx1);
10687c478bd9Sstevel@tonic-gate 			continue;
10697c478bd9Sstevel@tonic-gate 		}
10707c478bd9Sstevel@tonic-gate 
1071cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
1072cce0e03bSab196087 			Grp_hdl	*ghp3;
1073cce0e03bSab196087 			Aliste	idx3;
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 			/*
10767c478bd9Sstevel@tonic-gate 			 * Determine whether this dependency is the filtee's
10777c478bd9Sstevel@tonic-gate 			 * parent filter, and that it isn't also an explicit
10787c478bd9Sstevel@tonic-gate 			 * dependency (in which case it would have added its own
10797c478bd9Sstevel@tonic-gate 			 * dependencies to the handle).
10807c478bd9Sstevel@tonic-gate 			 */
10817c478bd9Sstevel@tonic-gate 			if ((gdp->gd_flags &
10827c478bd9Sstevel@tonic-gate 			    (GPD_FILTER | GPD_ADDEPS)) != GPD_FILTER)
10837c478bd9Sstevel@tonic-gate 				continue;
10847c478bd9Sstevel@tonic-gate 
1085dde769a2SRod Evans 			lmp = gdp->gd_depend;
1086dde769a2SRod Evans 
1087dde769a2SRod Evans 			if (FLAGS(lmp) & FLG_RT_DELETE)
1088dde769a2SRod Evans 				continue;
1089dde769a2SRod Evans 
1090dde769a2SRod Evans 			if (aplist_test(&lmalp, lmp, 0) == ALE_EXISTS)
10917c478bd9Sstevel@tonic-gate 				continue;
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 			/*
10947c478bd9Sstevel@tonic-gate 			 * Remove this group handle from our dynamic deletion
10957c478bd9Sstevel@tonic-gate 			 * list.  In addition, recompute the list of objects
10967c478bd9Sstevel@tonic-gate 			 * that are candidates for deletion to continue this
10977c478bd9Sstevel@tonic-gate 			 * group verification.
10987c478bd9Sstevel@tonic-gate 			 */
1099dde769a2SRod Evans 			DBG_CALL(Dbg_file_hdl_collect(ghp, NAME(lmp)));
1100cce0e03bSab196087 			aplist_delete(ghalp, &idx1);
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate 			free(lmalp);
1103cce0e03bSab196087 			lmalp = NULL;
1104cce0e03bSab196087 			for (APLIST_TRAVERSE(ghalp, idx3, ghp3)) {
1105cce0e03bSab196087 				Aliste		idx4;
11067c478bd9Sstevel@tonic-gate 				Grp_desc	*gdp4;
11077c478bd9Sstevel@tonic-gate 
1108cce0e03bSab196087 				for (ALIST_TRAVERSE(ghp3->gh_depends,
1109cce0e03bSab196087 				    idx4, gdp4))  {
11107c478bd9Sstevel@tonic-gate 					if ((gdp4->gd_flags & GPD_ADDEPS) == 0)
11117c478bd9Sstevel@tonic-gate 						continue;
1112cce0e03bSab196087 					if (aplist_test(&lmalp, gdp4->gd_depend,
1113cce0e03bSab196087 					    AL_CNT_GRPCLCT) == ALE_ALLOCFAIL) {
11147c478bd9Sstevel@tonic-gate 						remove_collect(ghalp, lmalp);
11157c478bd9Sstevel@tonic-gate 						return (0);
11167c478bd9Sstevel@tonic-gate 					}
11177c478bd9Sstevel@tonic-gate 				}
11187c478bd9Sstevel@tonic-gate 			}
11197c478bd9Sstevel@tonic-gate 			break;
11207c478bd9Sstevel@tonic-gate 		}
11217c478bd9Sstevel@tonic-gate 	}
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	/*
11247c478bd9Sstevel@tonic-gate 	 * Now that we've collected all the handles dependencies, traverse the
11257c478bd9Sstevel@tonic-gate 	 * collection determining whether they are a candidate for deletion.
11267c478bd9Sstevel@tonic-gate 	 */
1127cce0e03bSab196087 	for (APLIST_TRAVERSE(lmalp, idx1, lmp)) {
11287c478bd9Sstevel@tonic-gate 		/*
11297c478bd9Sstevel@tonic-gate 		 * Establish which link-map list we're dealing with for later
11307c478bd9Sstevel@tonic-gate 		 * .fini processing.
11317c478bd9Sstevel@tonic-gate 		 */
1132b4059b01SRod Evans 		if (lml == NULL)
11337c478bd9Sstevel@tonic-gate 			lml = LIST(lmp);
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate 		/*
11367c478bd9Sstevel@tonic-gate 		 * If an object isn't a candidate for deletion we'll have to
11377c478bd9Sstevel@tonic-gate 		 * rescan the handle insuring that this objects dependencies
11387c478bd9Sstevel@tonic-gate 		 * aren't deleted either.
11397c478bd9Sstevel@tonic-gate 		 */
11407c478bd9Sstevel@tonic-gate 		if (is_deletable(&lmalp, &ghalp, lmp)) {
11417c478bd9Sstevel@tonic-gate 			FLAGS(lmp) |= FLG_RT_DELETE;
11427c478bd9Sstevel@tonic-gate 			delcnt++;
11437c478bd9Sstevel@tonic-gate 		} else
11447c478bd9Sstevel@tonic-gate 			rescan = 1;
11457c478bd9Sstevel@tonic-gate 	}
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	/*
11487c478bd9Sstevel@tonic-gate 	 * Rescan the handle if any objects where found non-deletable.
11497c478bd9Sstevel@tonic-gate 	 */
11507c478bd9Sstevel@tonic-gate 	while (rescan)
11517c478bd9Sstevel@tonic-gate 		rescan = remove_rescan(lmalp, ghalp, &delcnt);
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	/*
11547c478bd9Sstevel@tonic-gate 	 * Now that we have determined the number of groups that are candidates
11557c478bd9Sstevel@tonic-gate 	 * for removal, mark each group descriptor as a candidate for removal
11567c478bd9Sstevel@tonic-gate 	 * from the group.
11577c478bd9Sstevel@tonic-gate 	 */
1158cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1159cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp2->gh_depends, idx2, gdp))
11607c478bd9Sstevel@tonic-gate 			gdp->gd_flags |= GPD_REMOVE;
11617c478bd9Sstevel@tonic-gate 	}
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 	/*
11647c478bd9Sstevel@tonic-gate 	 * Now that we know which objects on this handle can't be deleted
11657c478bd9Sstevel@tonic-gate 	 * determine whether they still need to remain identified as belonging
11667c478bd9Sstevel@tonic-gate 	 * to this group to be able to continue binding to one another.
11677c478bd9Sstevel@tonic-gate 	 */
1168cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1169cce0e03bSab196087 		Grp_hdl	*ghp = ghp2;
11707c478bd9Sstevel@tonic-gate 
1171cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
1172cce0e03bSab196087 			Aliste		idx3;
1173cce0e03bSab196087 			Bnd_desc	*bdp;
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 			lmp = gdp->gd_depend;
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 			if (FLAGS(lmp) & FLG_RT_DELETE)
11787c478bd9Sstevel@tonic-gate 				continue;
11797c478bd9Sstevel@tonic-gate 
1180cce0e03bSab196087 			for (APLIST_TRAVERSE(DEPENDS(lmp), idx3, bdp)) {
1181cce0e03bSab196087 				Aliste 		idx4;
11827c478bd9Sstevel@tonic-gate 				Grp_desc	*gdp4;
1183cce0e03bSab196087 				Rt_map		*dlmp = bdp->b_depend;
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 				/*
11867c478bd9Sstevel@tonic-gate 				 * If this dependency (dlmp) can be referenced
11877c478bd9Sstevel@tonic-gate 				 * by the caller (clmp) without being part of
11887c478bd9Sstevel@tonic-gate 				 * this group (ghp) then belonging to this group
11897c478bd9Sstevel@tonic-gate 				 * is no longer necessary.  This can occur when
11907c478bd9Sstevel@tonic-gate 				 * objects are part of multiple handles, or if a
11917c478bd9Sstevel@tonic-gate 				 * previously deleted handle was moved to the
11927c478bd9Sstevel@tonic-gate 				 * orphan list and has been reopened.  Note,
11937c478bd9Sstevel@tonic-gate 				 * first make sure the caller can reference the
11947c478bd9Sstevel@tonic-gate 				 * dependency with this group, if it can't we
11957c478bd9Sstevel@tonic-gate 				 * must be bound to a filtee, so there's no need
11967c478bd9Sstevel@tonic-gate 				 * to remain a part of this group either.
11977c478bd9Sstevel@tonic-gate 				 */
119860758829Srie 				if ((callable(lmp, dlmp, 0, 0) == 0) ||
119960758829Srie 				    callable(lmp, dlmp, ghp, 0))
12007c478bd9Sstevel@tonic-gate 					continue;
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 				if (gdp->gd_flags & GPD_REMOVE)
12037c478bd9Sstevel@tonic-gate 					gdp->gd_flags &= ~GPD_REMOVE;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 				for (ALIST_TRAVERSE(ghp->gh_depends,
1206cce0e03bSab196087 				    idx4, gdp4)) {
12077c478bd9Sstevel@tonic-gate 					if (gdp4->gd_depend != dlmp)
12087c478bd9Sstevel@tonic-gate 						continue;
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 					if (gdp4->gd_flags & GPD_REMOVE)
12117c478bd9Sstevel@tonic-gate 						gdp4->gd_flags &= ~GPD_REMOVE;
12127c478bd9Sstevel@tonic-gate 				}
12137c478bd9Sstevel@tonic-gate 			}
12147c478bd9Sstevel@tonic-gate 		}
12157c478bd9Sstevel@tonic-gate 	}
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 	/*
12187c478bd9Sstevel@tonic-gate 	 * If the owner of a handle can't be deleted and it's handle descriptor
12197c478bd9Sstevel@tonic-gate 	 * must remain also, don't delete the handle at all.  Leave it for
12207c478bd9Sstevel@tonic-gate 	 * possible later use.  Although it's left intact, it will still be
12217c478bd9Sstevel@tonic-gate 	 * moved to the orphans list, as we might be able to revisit it on later
12227c478bd9Sstevel@tonic-gate 	 * dlclose() operations and finally remove the underlying objects.  Note
12237c478bd9Sstevel@tonic-gate 	 * that the handle still remains attached to the owner via the HANDLES
12247c478bd9Sstevel@tonic-gate 	 * list, so that it can be re-associated to the owner if a dlopen()
12257c478bd9Sstevel@tonic-gate 	 * of this object reoccurs.
12267c478bd9Sstevel@tonic-gate 	 */
1227cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1228cce0e03bSab196087 		Grp_hdl	*ghp = ghp2;
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 		/*
12317c478bd9Sstevel@tonic-gate 		 * If this handle is already an orphan, or if it's owner is
12327c478bd9Sstevel@tonic-gate 		 * deletable there's no need to inspect its dependencies.
12337c478bd9Sstevel@tonic-gate 		 */
1234b4059b01SRod Evans 		if ((ghp->gh_ownlmp == NULL) ||
12355aefb655Srie 		    (FLAGS(ghp->gh_ownlmp) & FLG_RT_DELETE))
12367c478bd9Sstevel@tonic-gate 			continue;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 		/*
12397c478bd9Sstevel@tonic-gate 		 * Make sure all handle dependencies aren't removed or the
12407c478bd9Sstevel@tonic-gate 		 * dependencies themselves aren't deleted.
12417c478bd9Sstevel@tonic-gate 		 */
1242cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
12437c478bd9Sstevel@tonic-gate 			lmp = gdp->gd_depend;
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 			/*
12467c478bd9Sstevel@tonic-gate 			 * The first dependency of a non-orphaned handle is the
12477c478bd9Sstevel@tonic-gate 			 * owner.  If the handle descriptor for this isn't
12487c478bd9Sstevel@tonic-gate 			 * required there's no need to look at any other of the
12497c478bd9Sstevel@tonic-gate 			 * handles dependencies.
12507c478bd9Sstevel@tonic-gate 			 */
12515aefb655Srie 			if ((lmp == ghp->gh_ownlmp) &&
12527c478bd9Sstevel@tonic-gate 			    (gdp->gd_flags & GPD_REMOVE))
12537c478bd9Sstevel@tonic-gate 				break;
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 			if (gdp->gd_flags & GPD_REMOVE)
12567c478bd9Sstevel@tonic-gate 				gdp->gd_flags &= ~GPD_REMOVE;
12577c478bd9Sstevel@tonic-gate 			if (FLAGS(lmp) & FLG_RT_DELETE) {
12587c478bd9Sstevel@tonic-gate 				FLAGS(lmp) &= ~FLG_RT_DELETE;
12597c478bd9Sstevel@tonic-gate 				delcnt--;
12607c478bd9Sstevel@tonic-gate 			}
12617c478bd9Sstevel@tonic-gate 		}
12627c478bd9Sstevel@tonic-gate 	}
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	/*
12657c478bd9Sstevel@tonic-gate 	 * Final scan of objects to see if any objects are to to be deleted.
12667c478bd9Sstevel@tonic-gate 	 * Also - display diagnostic information on what operations are to be
12677c478bd9Sstevel@tonic-gate 	 * performed on the collected handles before firing .fini's (which
12687c478bd9Sstevel@tonic-gate 	 * produces additional diagnostics).
12697c478bd9Sstevel@tonic-gate 	 */
1270cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1271cce0e03bSab196087 		Grp_hdl	*ghp = ghp2;
12727c478bd9Sstevel@tonic-gate 
12738af2c5b9Srie 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_DELETE));
12747c478bd9Sstevel@tonic-gate 
1275cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
12762017c965SRod Evans 			Grp_hdl	*ghp3;
12772017c965SRod Evans 			Aliste	idx3;
12787c478bd9Sstevel@tonic-gate 			int	flag;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 			lmp = gdp->gd_depend;
12817c478bd9Sstevel@tonic-gate 
1282efb9e8b8Srie 			/*
1283efb9e8b8Srie 			 * Note, we must never delete a parent.  The parent
1284efb9e8b8Srie 			 * may already be tagged for deletion from a previous
1285efb9e8b8Srie 			 * dlclose(). That dlclose has triggered this dlclose(),
1286efb9e8b8Srie 			 * but the parents deletion is the responsibility of the
1287efb9e8b8Srie 			 * previous dlclose(), not this one.
1288efb9e8b8Srie 			 */
1289efb9e8b8Srie 			if ((FLAGS(lmp) & FLG_RT_DELETE) &&
1290efb9e8b8Srie 			    ((gdp->gd_flags & GPD_PARENT) == 0)) {
12917c478bd9Sstevel@tonic-gate 				flag = DBG_DEP_DELETE;
129202ca3e02Srie 
12937c478bd9Sstevel@tonic-gate 				/*
12947c478bd9Sstevel@tonic-gate 				 * Remove any pathnames from the FullpathNode
12957c478bd9Sstevel@tonic-gate 				 * AVL tree.  As we're about to fire .fini's,
12967c478bd9Sstevel@tonic-gate 				 * it's possible this object will be required
12977c478bd9Sstevel@tonic-gate 				 * again, in which case we want to make sure a
12987c478bd9Sstevel@tonic-gate 				 * new version of the object gets loaded.
12997c478bd9Sstevel@tonic-gate 				 */
13007c478bd9Sstevel@tonic-gate 				if (FPNODE(lmp))
13017c478bd9Sstevel@tonic-gate 					fpavl_remove(lmp);
13027c478bd9Sstevel@tonic-gate 			} else if (gdp->gd_flags & GPD_REMOVE)
13037c478bd9Sstevel@tonic-gate 				flag = DBG_DEP_REMOVE;
13047c478bd9Sstevel@tonic-gate 			else
13057c478bd9Sstevel@tonic-gate 				flag = DBG_DEP_REMAIN;
13067c478bd9Sstevel@tonic-gate 
130702ca3e02Srie 			DBG_CALL(Dbg_file_hdl_action(ghp, lmp, flag, 0));
13082017c965SRod Evans 
13092017c965SRod Evans 			/*
13102017c965SRod Evans 			 * If this object contains any private handles, remove
13112017c965SRod Evans 			 * them now.
13122017c965SRod Evans 			 */
13132017c965SRod Evans 			for (APLIST_TRAVERSE(HANDLES(lmp), idx3, ghp3)) {
13142017c965SRod Evans 				if (ghp3->gh_flags & GPH_PRIVATE)
13152017c965SRod Evans 					free_hdl(ghp3);
13162017c965SRod Evans 			}
13177c478bd9Sstevel@tonic-gate 		}
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 
13207c478bd9Sstevel@tonic-gate 	/*
13217c478bd9Sstevel@tonic-gate 	 * If there are objects to be deleted process their .fini's.
13227c478bd9Sstevel@tonic-gate 	 */
13237c478bd9Sstevel@tonic-gate 	if (delcnt) {
13247c478bd9Sstevel@tonic-gate 		Rt_map	**tobj;
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 		/*
13277c478bd9Sstevel@tonic-gate 		 * Sort and fire all fini's of the objects selected for
13287c478bd9Sstevel@tonic-gate 		 * deletion.  Note that we have to start our search from the
13297c478bd9Sstevel@tonic-gate 		 * link-map head - there's no telling whether this object has
13307c478bd9Sstevel@tonic-gate 		 * dependencies on objects that were loaded before it and which
13317c478bd9Sstevel@tonic-gate 		 * can now be deleted.  If the tsort() fails because of an
13327c478bd9Sstevel@tonic-gate 		 * allocation error then that might just be a symptom of why
13337c478bd9Sstevel@tonic-gate 		 * we're here in the first place - forgo the fini's but
13347c478bd9Sstevel@tonic-gate 		 * continue to try cleaning up.
13357c478bd9Sstevel@tonic-gate 		 */
1336dffec89cSrie 		lml->lm_flags |= LML_FLG_OBJDELETED;
1337dffec89cSrie 
13387c478bd9Sstevel@tonic-gate 		if (((tobj = tsort(lml->lm_head, delcnt,
1339b4059b01SRod Evans 		    (RT_SORT_DELETE | RT_SORT_FWD))) != NULL) &&
13407c478bd9Sstevel@tonic-gate 		    (tobj != (Rt_map **)S_ERROR)) {
13417c478bd9Sstevel@tonic-gate 			error = purge_exit_handlers(lml, tobj);
1342*2020b2b6SRod Evans 			call_fini(lml, tobj, clmp);
13437c478bd9Sstevel@tonic-gate 		}
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	/*
13477c478bd9Sstevel@tonic-gate 	 * Now that .fini processing (which may have involved new bindings)
13487c478bd9Sstevel@tonic-gate 	 * is complete, remove all inter-dependency lists from those objects
13497c478bd9Sstevel@tonic-gate 	 * selected for deletion.
13507c478bd9Sstevel@tonic-gate 	 */
1351cce0e03bSab196087 	for (APLIST_TRAVERSE(lmalp, idx1, lmp)) {
13527c478bd9Sstevel@tonic-gate 		Dyninfo	*dip;
13537c478bd9Sstevel@tonic-gate 		uint_t	cnt, max;
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate 		if (FLAGS(lmp) & FLG_RT_DELETE)
13567c478bd9Sstevel@tonic-gate 			remove_lists(lmp, 0);
13577c478bd9Sstevel@tonic-gate 
13587c478bd9Sstevel@tonic-gate 		/*
13597c478bd9Sstevel@tonic-gate 		 * Determine whether we're dealing with a filter, and if so
13607c478bd9Sstevel@tonic-gate 		 * process any inter-dependencies with its filtee's.
13617c478bd9Sstevel@tonic-gate 		 */
13627c478bd9Sstevel@tonic-gate 		if ((FLAGS1(lmp) & MSK_RT_FILTER) == 0)
13637c478bd9Sstevel@tonic-gate 			continue;
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 		dip = DYNINFO(lmp);
13667c478bd9Sstevel@tonic-gate 		max = DYNINFOCNT(lmp);
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 		for (cnt = 0; cnt < max; cnt++, dip++) {
136956deab07SRod Evans 			Alist	*falp;
137056deab07SRod Evans 			Aliste	idx2;
137156deab07SRod Evans 			Pdesc	*pdp;
13727c478bd9Sstevel@tonic-gate 
137356deab07SRod Evans 			if (((falp = (Alist *)dip->di_info) == NULL) ||
13747c478bd9Sstevel@tonic-gate 			    ((dip->di_flags & MSK_DI_FILTER) == 0))
13757c478bd9Sstevel@tonic-gate 				continue;
13767c478bd9Sstevel@tonic-gate 
137756deab07SRod Evans 			for (ALIST_TRAVERSE(falp, idx2, pdp)) {
13787c478bd9Sstevel@tonic-gate 				Grp_hdl	*ghp;
13797c478bd9Sstevel@tonic-gate 
138056deab07SRod Evans 				if ((pdp->pd_plen == 0) ||
138156deab07SRod Evans 				    ((ghp = (Grp_hdl *)pdp->pd_info) == NULL))
13827c478bd9Sstevel@tonic-gate 					continue;
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 				/*
13857c478bd9Sstevel@tonic-gate 				 * Determine whether this filtee's handle is a
13867c478bd9Sstevel@tonic-gate 				 * part of the list of handles being deleted.
13877c478bd9Sstevel@tonic-gate 				 */
1388cce0e03bSab196087 				if (aplist_test(&ghalp, ghp, 0) == ALE_EXISTS) {
13897c478bd9Sstevel@tonic-gate 					/*
13907c478bd9Sstevel@tonic-gate 					 * If this handle exists on the deletion
13917c478bd9Sstevel@tonic-gate 					 * list, then it has been removed.  If
13927c478bd9Sstevel@tonic-gate 					 * this filter isn't going to be
139302ca3e02Srie 					 * deleted, sever its reference to the
13947c478bd9Sstevel@tonic-gate 					 * handle.
13957c478bd9Sstevel@tonic-gate 					 */
139656deab07SRod Evans 					pdp->pd_info = NULL;
13977c478bd9Sstevel@tonic-gate 				} else {
13987c478bd9Sstevel@tonic-gate 					/*
13997c478bd9Sstevel@tonic-gate 					 * If this handle isn't on the deletion
14007c478bd9Sstevel@tonic-gate 					 * list, then it must still exist.  If
14017c478bd9Sstevel@tonic-gate 					 * this filter is being deleted, make
14027c478bd9Sstevel@tonic-gate 					 * sure the filtees reference count
14037c478bd9Sstevel@tonic-gate 					 * gets decremented.
14047c478bd9Sstevel@tonic-gate 					 */
1405dde769a2SRod Evans 					if (FLAGS(lmp) & FLG_RT_DELETE) {
14067247f888Srie 						(void) dlclose_core(ghp,
14077247f888Srie 						    lmp, lml);
14087247f888Srie 					}
14097c478bd9Sstevel@tonic-gate 				}
14107c478bd9Sstevel@tonic-gate 			}
14117c478bd9Sstevel@tonic-gate 		}
14127c478bd9Sstevel@tonic-gate 	}
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	/*
14157c478bd9Sstevel@tonic-gate 	 * If called from dlclose(), determine if there are already handles on
14167c478bd9Sstevel@tonic-gate 	 * the orphans list that we can reinvestigate.
14177c478bd9Sstevel@tonic-gate 	 */
141856deab07SRod Evans 	if ((removed == 0) && aplist_nitems(hdl_alp[HDLIST_ORP]))
14197c478bd9Sstevel@tonic-gate 		orphans = 1;
14207c478bd9Sstevel@tonic-gate 	else
14217c478bd9Sstevel@tonic-gate 		orphans = 0;
14227c478bd9Sstevel@tonic-gate 
14237c478bd9Sstevel@tonic-gate 	/*
14247c478bd9Sstevel@tonic-gate 	 * Finally remove any handle infrastructure and remove any objects
14257c478bd9Sstevel@tonic-gate 	 * marked for deletion.
14267c478bd9Sstevel@tonic-gate 	 */
1427cce0e03bSab196087 	for (APLIST_TRAVERSE(ghalp, idx1, ghp2)) {
1428cce0e03bSab196087 		Grp_hdl	*ghp = ghp2;
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate 		/*
14317c478bd9Sstevel@tonic-gate 		 * If we're not dealing with orphaned handles remove this handle
14327c478bd9Sstevel@tonic-gate 		 * from its present handle list.
14337c478bd9Sstevel@tonic-gate 		 */
14347c478bd9Sstevel@tonic-gate 		if (removed == 0) {
14357c478bd9Sstevel@tonic-gate 			uintptr_t ndx;
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 			/* LINTED */
14387c478bd9Sstevel@tonic-gate 			ndx = (uintptr_t)ghp % HDLIST_SZ;
143956deab07SRod Evans 			(void) aplist_delete_value(hdl_alp[ndx], ghp);
14407c478bd9Sstevel@tonic-gate 		}
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 		/*
1443efb9e8b8Srie 		 * Traverse each handle dependency.  Retain the dependencies
1444efb9e8b8Srie 		 * flags to insure we don't delete any parents (the flags
1445efb9e8b8Srie 		 * information is deleted as part of the alist removal that
1446efb9e8b8Srie 		 * occurs before we inspect the object for deletion).
14477c478bd9Sstevel@tonic-gate 		 */
1448cce0e03bSab196087 		for (ALIST_TRAVERSE(ghp->gh_depends, idx2, gdp)) {
1449efb9e8b8Srie 			uint_t	flags = gdp->gd_flags;
1450efb9e8b8Srie 
1451efb9e8b8Srie 			if ((flags & GPD_REMOVE) == 0)
14527c478bd9Sstevel@tonic-gate 				continue;
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 			lmp = gdp->gd_depend;
14557c478bd9Sstevel@tonic-gate 			rmcnt++;
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 			/*
14587c478bd9Sstevel@tonic-gate 			 * If this object is the owner of the handle break that
14597c478bd9Sstevel@tonic-gate 			 * association in case the handle is retained.
14607c478bd9Sstevel@tonic-gate 			 */
14615aefb655Srie 			if (ghp->gh_ownlmp == lmp) {
1462cce0e03bSab196087 				(void) aplist_delete_value(HANDLES(lmp), ghp);
1463b4059b01SRod Evans 				ghp->gh_ownlmp = NULL;
14647c478bd9Sstevel@tonic-gate 			}
14657c478bd9Sstevel@tonic-gate 
1466cce0e03bSab196087 			(void) aplist_delete_value(GROUPS(lmp), ghp);
1467cce0e03bSab196087 			alist_delete(ghp->gh_depends, &idx2);
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 			/*
14707c478bd9Sstevel@tonic-gate 			 * Complete the link-map deletion if appropriate.
14717c478bd9Sstevel@tonic-gate 			 */
1472efb9e8b8Srie 			if ((FLAGS(lmp) & FLG_RT_DELETE) &&
1473efb9e8b8Srie 			    ((flags & GPD_PARENT) == 0)) {
147410a4fa49Srie 				tls_modaddrem(lmp, TM_FLG_MODREM);
1475*2020b2b6SRod Evans 				remove_so(LIST(lmp), lmp, clmp);
14767c478bd9Sstevel@tonic-gate 			}
14777c478bd9Sstevel@tonic-gate 		}
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 		/*
148002ca3e02Srie 		 * If we've deleted all the dependencies of the handle, finalize
14817c478bd9Sstevel@tonic-gate 		 * the cleanup by removing the handle itself.
14827c478bd9Sstevel@tonic-gate 		 *
14837c478bd9Sstevel@tonic-gate 		 * Otherwise we're left with a handle containing one or more
14847c478bd9Sstevel@tonic-gate 		 * objects that can not be deleted (they're in use by other
14857c478bd9Sstevel@tonic-gate 		 * handles, non-deletable, etc.), but require to remain a part
14867c478bd9Sstevel@tonic-gate 		 * of this group to allow them to continue binding to one
148702ca3e02Srie 		 * another.
148802ca3e02Srie 		 *
148902ca3e02Srie 		 * If the handles reference count is zero, or represents a
149002ca3e02Srie 		 * link-map list (dlopen(0)), then move that handle to the
149102ca3e02Srie 		 * orphans list.  Should another dlclose() operation occur that
14927c478bd9Sstevel@tonic-gate 		 * results in the removal of handle descriptors, these orphan
149302ca3e02Srie 		 * handles are re-examined to determine if their deletion can
149402ca3e02Srie 		 * be completed.
14957c478bd9Sstevel@tonic-gate 		 */
1496cce0e03bSab196087 		if (ghp->gh_depends->al_nitems == 0) {
14977c478bd9Sstevel@tonic-gate 			free(ghp->gh_depends);
14987c478bd9Sstevel@tonic-gate 			free(ghp);
149902ca3e02Srie 
1500b4059b01SRod Evans 		} else if ((ghp->gh_refcnt == 0) &&
150102ca3e02Srie 		    ((ghp->gh_flags & GPH_ZERO) == 0)) {
15027c478bd9Sstevel@tonic-gate 			/*
150302ca3e02Srie 			 * Move this handle to the orphans list.
15047c478bd9Sstevel@tonic-gate 			 */
150556deab07SRod Evans 			(void) aplist_append(&hdl_alp[HDLIST_ORP], ghp,
150656deab07SRod Evans 			    AL_CNT_HANDLES);
15077c478bd9Sstevel@tonic-gate 
15085aefb655Srie 			if (DBG_ENABLED) {
15098af2c5b9Srie 				DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ORPHAN));
1510cce0e03bSab196087 				for (ALIST_TRAVERSE(ghp->gh_depends, idx1, gdp))
15117c478bd9Sstevel@tonic-gate 					DBG_CALL(Dbg_file_hdl_action(ghp,
151202ca3e02Srie 					    gdp->gd_depend, DBG_DEP_ORPHAN, 0));
15137c478bd9Sstevel@tonic-gate 			}
15147c478bd9Sstevel@tonic-gate 		}
15157c478bd9Sstevel@tonic-gate 	}
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	/*
15187c478bd9Sstevel@tonic-gate 	 * If no handle descriptors got removed there's no point in looking for
15197c478bd9Sstevel@tonic-gate 	 * orphans to process.
15207c478bd9Sstevel@tonic-gate 	 */
15217c478bd9Sstevel@tonic-gate 	if (rmcnt == 0)
15227c478bd9Sstevel@tonic-gate 		orphans = 0;
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	/*
15257c478bd9Sstevel@tonic-gate 	 * Cleanup any alists we've created.
15267c478bd9Sstevel@tonic-gate 	 */
15277c478bd9Sstevel@tonic-gate 	remove_collect(ghalp, lmalp);
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate 	/*
15307c478bd9Sstevel@tonic-gate 	 * If orphan processing isn't required we're done.  If our processing
15317c478bd9Sstevel@tonic-gate 	 * originated from investigating orphans, return the number of handle
15327c478bd9Sstevel@tonic-gate 	 * descriptors removed as an indication whether orphan processing
15337c478bd9Sstevel@tonic-gate 	 * should continue.
15347c478bd9Sstevel@tonic-gate 	 */
15357c478bd9Sstevel@tonic-gate 	if (orphans == 0) {
15367c478bd9Sstevel@tonic-gate 		if (removed)
15377c478bd9Sstevel@tonic-gate 			*removed = rmcnt;
15387c478bd9Sstevel@tonic-gate 		return (error);
15397c478bd9Sstevel@tonic-gate 	}
15407c478bd9Sstevel@tonic-gate 
15417c478bd9Sstevel@tonic-gate 	/*
15427c478bd9Sstevel@tonic-gate 	 * Traverse the orphans list as many times as necessary until no
15437c478bd9Sstevel@tonic-gate 	 * handle removals occur.
15447c478bd9Sstevel@tonic-gate 	 */
15457c478bd9Sstevel@tonic-gate 	do {
154656deab07SRod Evans 		APlist		*alp;
154756deab07SRod Evans 		Aliste		idx;
1548b4059b01SRod Evans 		Grp_hdl		*ghp, *oghp = NULL;
15495aefb655Srie 		int		title = 0;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 		/*
15527c478bd9Sstevel@tonic-gate 		 * Effectively clean the HDLIST_ORP list.  Any object that can't
15537c478bd9Sstevel@tonic-gate 		 * be removed will be re-added to the list.
15547c478bd9Sstevel@tonic-gate 		 */
155556deab07SRod Evans 		alp = hdl_alp[HDLIST_ORP];
155656deab07SRod Evans 		hdl_alp[HDLIST_ORP] = NULL;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 		rescan = 0;
155956deab07SRod Evans 		for (APLIST_TRAVERSE(alp, idx, ghp)) {
15607c478bd9Sstevel@tonic-gate 			int	_error, _remove;
15617c478bd9Sstevel@tonic-gate 
15625aefb655Srie 			if (title++ == 0)
15635aefb655Srie 				DBG_CALL(Dbg_file_del_rescan(ghp->gh_ownlml));
15645aefb655Srie 
15657c478bd9Sstevel@tonic-gate 			if (oghp) {
156656deab07SRod Evans 				(void) aplist_delete_value(alp, oghp);
1567b4059b01SRod Evans 				oghp = NULL;
15687c478bd9Sstevel@tonic-gate 			}
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate 			if (((_error = remove_hdl(ghp, clmp, &_remove)) != 0) &&
15717c478bd9Sstevel@tonic-gate 			    (error == 0))
15727c478bd9Sstevel@tonic-gate 				error = _error;
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 			if (_remove)
15757c478bd9Sstevel@tonic-gate 				rescan++;
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 			oghp = ghp;
15787c478bd9Sstevel@tonic-gate 		}
15797c478bd9Sstevel@tonic-gate 		if (oghp) {
158056deab07SRod Evans 			(void) aplist_delete_value(alp, oghp);
1581b4059b01SRod Evans 			oghp = NULL;
15827c478bd9Sstevel@tonic-gate 		}
158356deab07SRod Evans 		if (alp)
158456deab07SRod Evans 			free((void *)alp);
15857c478bd9Sstevel@tonic-gate 
158656deab07SRod Evans 	} while (rescan && aplist_nitems(hdl_alp[HDLIST_ORP]));
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate 	return (error);
15897c478bd9Sstevel@tonic-gate }
1590