xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/dlfcns.c (revision 5279807d7e1818eac6f90ac640b7a89cdb37522d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  *	Copyright (c) 1988 AT&T
29  *	  All Rights Reserved
30  */
31 
32 /*
33  * Programmatic interface to the run_time linker.
34  */
35 
36 #include	<sys/debug.h>
37 #include	<stdio.h>
38 #include	<string.h>
39 #include	<dlfcn.h>
40 #include	<synch.h>
41 #include	<limits.h>
42 #include	<debug.h>
43 #include	"_rtld.h"
44 #include	"_audit.h"
45 #include	"_elf.h"
46 #include	"_inline.h"
47 #include	"msg.h"
48 
49 /*
50  * Determine who called us - given a pc determine in which object it resides.
51  *
52  * For dlopen() the link map of the caller must be passed to load_so() so that
53  * the appropriate search rules (4.x or 5.0) are used to locate any
54  * dependencies.  Also, if we've been called from a 4.x module it may be
55  * necessary to fix the specified pathname so that it conforms with the 5.0 elf
56  * rules.
57  *
58  * For dlsym() the link map of the caller is used to determine RTLD_NEXT
59  * requests, together with requests based off of a dlopen(0).
60  * For dladdr() this routines provides a generic means of scanning all loaded
61  * segments.
62  */
63 Rt_map *
64 _caller(caddr_t cpc, int flags)
65 {
66 	Lm_list	*lml;
67 	Aliste	idx1;
68 
69 	for (APLIST_TRAVERSE(dynlm_list, idx1, lml)) {
70 		Aliste	idx2;
71 		Lm_cntl	*lmc;
72 
73 		for (ALIST_TRAVERSE(lml->lm_lists, idx2, lmc)) {
74 			Rt_map	*lmp;
75 
76 			for (lmp = lmc->lc_head; lmp;
77 			    lmp = NEXT_RT_MAP(lmp)) {
78 
79 				if (find_segment(cpc, lmp))
80 					return (lmp);
81 			}
82 		}
83 	}
84 
85 	/*
86 	 * No mapping can be determined.  If asked for a default, assume this
87 	 * is from the executable.
88 	 */
89 	if (flags & CL_EXECDEF)
90 		return ((Rt_map *)lml_main.lm_head);
91 
92 	return (0);
93 }
94 
95 #pragma weak _dlerror = dlerror
96 
97 /*
98  * External entry for dlerror(3dl).  Returns a pointer to the string describing
99  * the last occurring error.  The last occurring error is cleared.
100  */
101 char *
102 dlerror()
103 {
104 	char	*error;
105 	Rt_map	*clmp;
106 	int	entry;
107 
108 	entry = enter(0);
109 
110 	clmp = _caller(caller(), CL_EXECDEF);
111 
112 	error = lasterr;
113 	lasterr = NULL;
114 
115 	if (entry)
116 		leave(LIST(clmp), 0);
117 	return (error);
118 }
119 
120 /*
121  * Add a dependency as a group descriptor to a group handle.  Returns 0 on
122  * failure.  On success, returns the group descriptor, and if alep is non-NULL
123  * the *alep is set to ALE_EXISTS if the dependency already exists, or to
124  * ALE_CREATE if the dependency is newly created.
125  */
126 Grp_desc *
127 hdl_add(Grp_hdl *ghp, Rt_map *lmp, uint_t dflags, int *alep)
128 {
129 	Grp_desc	*gdp;
130 	Aliste		idx;
131 	int		ale = ALE_CREATE;
132 	uint_t		oflags;
133 
134 	/*
135 	 * Make sure this dependency hasn't already been recorded.
136 	 */
137 	for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
138 		if (gdp->gd_depend == lmp) {
139 			ale = ALE_EXISTS;
140 			break;
141 		}
142 	}
143 
144 	if (ale == ALE_CREATE) {
145 		Grp_desc	gd;
146 
147 		/*
148 		 * Create a new handle descriptor.
149 		 */
150 		gd.gd_depend = lmp;
151 		gd.gd_flags = 0;
152 
153 		/*
154 		 * Indicate this object is a part of this handles group.
155 		 */
156 		if (aplist_append(&GROUPS(lmp), ghp, AL_CNT_GROUPS) == NULL)
157 			return (NULL);
158 
159 		/*
160 		 * Append the new dependency to this handle.
161 		 */
162 		if ((gdp = alist_append(&ghp->gh_depends, &gd,
163 		    sizeof (Grp_desc), AL_CNT_DEPENDS)) == NULL)
164 			return (NULL);
165 	}
166 
167 	oflags = gdp->gd_flags;
168 	gdp->gd_flags |= dflags;
169 
170 	if (DBG_ENABLED) {
171 		if (ale == ALE_CREATE)
172 			DBG_CALL(Dbg_file_hdl_action(ghp, lmp, DBG_DEP_ADD,
173 			    gdp->gd_flags));
174 		else if (gdp->gd_flags != oflags)
175 			DBG_CALL(Dbg_file_hdl_action(ghp, lmp, DBG_DEP_UPDATE,
176 			    gdp->gd_flags));
177 	}
178 
179 	if (alep)
180 		*alep = ale;
181 	return (gdp);
182 }
183 
184 /*
185  * Create a handle.
186  *
187  *   rlmp -	represents the reference link-map for which the handle is being
188  *		created.
189  *   clmp -	represents the caller who is requesting the handle.
190  *   hflags -	provide group handle flags (GPH_*) that affect the use of the
191  *		handle, such as dlopen(0), or use or use of RTLD_FIRST.
192  *   rdflags -	provide group dependency flags for the reference link-map rlmp,
193  *		such as whether the dependency can be used for dlsym(), can be
194  *		relocated against, or whether this objects dependencies should
195  *		be processed.
196  *   cdflags -	provide group dependency flags for the caller.
197  */
198 Grp_hdl *
199 hdl_create(Lm_list *lml, Rt_map *rlmp, Rt_map *clmp, uint_t hflags,
200     uint_t rdflags, uint_t cdflags)
201 {
202 	Grp_hdl	*ghp = NULL, *aghp;
203 	APlist	**alpp;
204 	Aliste	idx;
205 
206 	/*
207 	 * For dlopen(0) the handle is maintained as part of the link-map list,
208 	 * otherwise the handle is associated with the reference link-map.
209 	 */
210 	if (hflags & GPH_ZERO)
211 		alpp = &(lml->lm_handle);
212 	else
213 		alpp = &(HANDLES(rlmp));
214 
215 	/*
216 	 * Objects can contain multiple handles depending on the handle flags
217 	 * supplied.  Most RTLD flags pertain to the object itself and the
218 	 * bindings that it can achieve.  Multiple handles for these flags
219 	 * don't make sense.  But if the flag determines how the handle might
220 	 * be used, then multiple handles may exist.  Presently this only makes
221 	 * sense for RTLD_FIRST.  Determine if an appropriate handle already
222 	 * exists.
223 	 */
224 	for (APLIST_TRAVERSE(*alpp, idx, aghp)) {
225 		if ((aghp->gh_flags & GPH_FIRST) == (hflags & GPH_FIRST)) {
226 			ghp = aghp;
227 			break;
228 		}
229 	}
230 
231 	if (ghp == NULL) {
232 		uint_t	ndx;
233 
234 		/*
235 		 * If this is the first request for this handle, allocate and
236 		 * initialize a new handle.
237 		 */
238 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_CREATE));
239 
240 		if ((ghp = malloc(sizeof (Grp_hdl))) == NULL)
241 			return (NULL);
242 
243 		/*
244 		 * Associate the handle with the link-map list or the reference
245 		 * link-map as appropriate.
246 		 */
247 		if (aplist_append(alpp, ghp, AL_CNT_GROUPS) == NULL) {
248 			free(ghp);
249 			return (NULL);
250 		}
251 
252 		/*
253 		 * Record the existence of this handle for future verification.
254 		 */
255 		/* LINTED */
256 		ndx = (uintptr_t)ghp % HDLIST_SZ;
257 
258 		if (aplist_append(&hdl_alp[ndx], ghp, AL_CNT_HANDLES) == NULL) {
259 			(void) aplist_delete_value(*alpp, ghp);
260 			free(ghp);
261 			return (NULL);
262 		}
263 
264 		ghp->gh_depends = NULL;
265 		ghp->gh_refcnt = 1;
266 		ghp->gh_flags = hflags;
267 
268 		/*
269 		 * A dlopen(0) handle is identified by the GPH_ZERO flag, the
270 		 * head of the link-map list is defined as the owner.  There is
271 		 * no need to maintain a list of dependencies, for when this
272 		 * handle is used (for dlsym()) a dynamic search through the
273 		 * entire link-map list provides for searching all objects with
274 		 * GLOBAL visibility.
275 		 */
276 		if (hflags & GPH_ZERO) {
277 			ghp->gh_ownlmp = lml->lm_head;
278 			ghp->gh_ownlml = lml;
279 		} else {
280 			ghp->gh_ownlmp = rlmp;
281 			ghp->gh_ownlml = LIST(rlmp);
282 
283 			if (hdl_add(ghp, rlmp, rdflags, NULL) == NULL)
284 				return (NULL);
285 
286 			/*
287 			 * If this new handle is a private handle, there's no
288 			 * need to track the caller, so we're done.
289 			 */
290 			if (hflags & GPH_PRIVATE)
291 				return (ghp);
292 
293 			/*
294 			 * If this new handle is public, and isn't a special
295 			 * handle representing ld.so.1, indicate that a local
296 			 * group now exists.  This state allows singleton
297 			 * searches to be optimized.
298 			 */
299 			if ((hflags & GPH_LDSO) == 0)
300 				LIST(rlmp)->lm_flags |= LML_FLG_GROUPSEXIST;
301 		}
302 	} else {
303 		/*
304 		 * If a handle already exists, bump its reference count.
305 		 *
306 		 * If the previous reference count was 0, then this is a handle
307 		 * that an earlier call to dlclose() was unable to remove.  Such
308 		 * handles are put on the orphan list.  As this handle is back
309 		 * in use, it must be removed from the orphan list.
310 		 *
311 		 * Note, handles associated with a link-map list itself (i.e.
312 		 * dlopen(0)) can have a reference count of 0.  However, these
313 		 * handles are never deleted, and therefore are never moved to
314 		 * the orphan list.
315 		 */
316 		if ((ghp->gh_refcnt++ == 0) &&
317 		    ((ghp->gh_flags & GPH_ZERO) == 0)) {
318 			uint_t	ndx;
319 
320 			/* LINTED */
321 			ndx = (uintptr_t)ghp % HDLIST_SZ;
322 
323 			(void) aplist_delete_value(hdl_alp[HDLIST_ORP], ghp);
324 			(void) aplist_append(&hdl_alp[ndx], ghp,
325 			    AL_CNT_HANDLES);
326 
327 			if (DBG_ENABLED) {
328 				Aliste		idx;
329 				Grp_desc	*gdp;
330 
331 				DBG_CALL(Dbg_file_hdl_title(DBG_HDL_REINST));
332 				for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp))
333 					DBG_CALL(Dbg_file_hdl_action(ghp,
334 					    gdp->gd_depend, DBG_DEP_REINST, 0));
335 			}
336 		}
337 
338 		/*
339 		 * If we've been asked to create a private handle, there's no
340 		 * need to track the caller.
341 		 */
342 		if (hflags & GPH_PRIVATE) {
343 			/*
344 			 * Negate the reference count increment.
345 			 */
346 			ghp->gh_refcnt--;
347 			return (ghp);
348 		} else {
349 			/*
350 			 * If a private handle already exists, promote this
351 			 * handle to public by initializing both the reference
352 			 * count and the handle flags.
353 			 */
354 			if (ghp->gh_flags & GPH_PRIVATE) {
355 				ghp->gh_refcnt = 1;
356 				ghp->gh_flags &= ~GPH_PRIVATE;
357 				ghp->gh_flags |= hflags;
358 			}
359 		}
360 	}
361 
362 	/*
363 	 * Keep track of the parent (caller).  As this object can be referenced
364 	 * by different parents, this processing is carried out every time a
365 	 * handle is requested.
366 	 */
367 	if (clmp && (hdl_add(ghp, clmp, cdflags, NULL) == NULL))
368 		return (NULL);
369 
370 	return (ghp);
371 }
372 
373 /*
374  * Initialize a handle that has been created for an object that is already
375  * loaded.  The handle is initialized with the present dependencies of that
376  * object.  Once this initialization has occurred, any new objects that might
377  * be loaded as dependencies (lazy-loading) are added to the handle as each new
378  * object is loaded.
379  */
380 int
381 hdl_initialize(Grp_hdl *ghp, Rt_map *nlmp, int mode, int promote)
382 {
383 	Aliste		idx;
384 	Grp_desc	*gdp;
385 
386 	/*
387 	 * If the handle has already been initialized, and the initial object's
388 	 * mode hasn't been promoted, there's no need to recompute the modes of
389 	 * any dependencies.  If the object we've added has just been opened,
390 	 * the objects dependencies will not yet have been processed.  These
391 	 * dependencies will be added on later calls to load_one().  Otherwise,
392 	 * this object already exists, so add all of its dependencies to the
393 	 * handle were operating on.
394 	 */
395 	if (((ghp->gh_flags & GPH_INITIAL) && (promote == 0)) ||
396 	    ((FLAGS(nlmp) & FLG_RT_ANALYZED) == 0)) {
397 		ghp->gh_flags |= GPH_INITIAL;
398 		return (1);
399 	}
400 
401 	DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
402 	for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
403 		Rt_map		*lmp = gdp->gd_depend;
404 		Aliste		idx1;
405 		Bnd_desc	*bdp;
406 
407 		/*
408 		 * If this dependency doesn't indicate that its dependencies
409 		 * should be added to a handle, ignore it.  This case identifies
410 		 * a parent of a dlopen(RTLD_PARENT) request.
411 		 */
412 		if ((gdp->gd_flags & GPD_ADDEPS) == 0)
413 			continue;
414 
415 		for (APLIST_TRAVERSE(DEPENDS(lmp), idx1, bdp)) {
416 			Grp_desc	*gdp;
417 			Rt_map		*dlmp = bdp->b_depend;
418 
419 			if ((bdp->b_flags & BND_NEEDED) == 0)
420 				continue;
421 
422 			if ((gdp = hdl_add(ghp, dlmp,
423 			    (GPD_DLSYM | GPD_RELOC | GPD_ADDEPS),
424 			    NULL)) == NULL)
425 				return (0);
426 
427 			if (update_mode(dlmp, MODE(dlmp), mode))
428 				gdp->gd_flags |= GPD_MODECHANGE;
429 		}
430 	}
431 	ghp->gh_flags |= GPH_INITIAL;
432 	return (1);
433 }
434 
435 /*
436  * Sanity check a program-provided handle.
437  */
438 static int
439 hdl_validate(Grp_hdl *ghp)
440 {
441 	Aliste		idx;
442 	Grp_hdl		*lghp;
443 	uint_t		ndx;
444 
445 	/* LINTED */
446 	ndx = (uintptr_t)ghp % HDLIST_SZ;
447 
448 	for (APLIST_TRAVERSE(hdl_alp[ndx], idx, lghp)) {
449 		if ((lghp == ghp) && (ghp->gh_refcnt != 0))
450 			return (1);
451 	}
452 	return (0);
453 }
454 
455 /*
456  * Core dlclose activity.
457  */
458 int
459 dlclose_core(Grp_hdl *ghp, Rt_map *clmp, Lm_list *lml)
460 {
461 	int	error;
462 
463 	/*
464 	 * If we're already at atexit() there's no point processing further,
465 	 * all objects have already been tsorted for fini processing.
466 	 */
467 	if (rtld_flags & RT_FL_ATEXIT)
468 		return (0);
469 
470 	/*
471 	 * Diagnose what we're up to.
472 	 */
473 	if (ghp->gh_flags & GPH_ZERO) {
474 		DBG_CALL(Dbg_file_dlclose(LIST(clmp), MSG_ORIG(MSG_STR_ZERO),
475 		    DBG_DLCLOSE_IGNORE));
476 	} else {
477 		DBG_CALL(Dbg_file_dlclose(LIST(clmp), NAME(ghp->gh_ownlmp),
478 		    DBG_DLCLOSE_NULL));
479 	}
480 
481 	/*
482 	 * Decrement reference count of this object.
483 	 */
484 	if (--(ghp->gh_refcnt))
485 		return (0);
486 
487 	/*
488 	 * If this handle is special (dlopen(0)), then leave it around - it
489 	 * has little overhead.
490 	 */
491 	if (ghp->gh_flags & GPH_ZERO)
492 		return (0);
493 
494 	/*
495 	 * This handle is no longer being referenced, remove it.  If this handle
496 	 * is part of an alternative link-map list, determine if the whole list
497 	 * can be removed also.
498 	 */
499 	error = remove_hdl(ghp, clmp, NULL);
500 
501 	if ((lml->lm_flags & (LML_FLG_BASELM | LML_FLG_RTLDLM)) == 0)
502 		remove_lml(lml);
503 
504 	return (error);
505 }
506 
507 /*
508  * Internal dlclose activity.  Called from user level or directly for internal
509  * error cleanup.
510  */
511 int
512 dlclose_intn(Grp_hdl *ghp, Rt_map *clmp)
513 {
514 	Rt_map	*nlmp = NULL;
515 	Lm_list	*olml = NULL;
516 	int	error;
517 
518 	/*
519 	 * Although we're deleting object(s) it's quite possible that additional
520 	 * objects get loaded from running the .fini section(s) of the objects
521 	 * being deleted.  These objects will have been added to the same
522 	 * link-map list as those objects being deleted.  Remember this list
523 	 * for later investigation.
524 	 */
525 	olml = ghp->gh_ownlml;
526 
527 	error = dlclose_core(ghp, clmp, olml);
528 
529 	/*
530 	 * Determine whether the original link-map list still exists.  In the
531 	 * case of a dlclose of an alternative (dlmopen) link-map the whole
532 	 * list may have been removed.
533 	 */
534 	if (olml) {
535 		Aliste	idx;
536 		Lm_list	*lml;
537 
538 		for (APLIST_TRAVERSE(dynlm_list, idx, lml)) {
539 			if (olml == lml) {
540 				nlmp = olml->lm_head;
541 				break;
542 			}
543 		}
544 	}
545 	load_completion(nlmp);
546 	return (error);
547 }
548 
549 /*
550  * Argument checking for dlclose.  Only called via external entry.
551  */
552 static int
553 dlclose_check(void *handle, Rt_map *clmp)
554 {
555 	Grp_hdl	*ghp = (Grp_hdl *)handle;
556 
557 	if (hdl_validate(ghp) == 0) {
558 		eprintf(LIST(clmp), ERR_FATAL, MSG_INTL(MSG_ARG_INVHNDL),
559 		    EC_NATPTR(handle));
560 		return (1);
561 	}
562 	return (dlclose_intn(ghp, clmp));
563 }
564 
565 #pragma weak _dlclose = dlclose
566 
567 /*
568  * External entry for dlclose(3dl).  Returns 0 for success, non-zero otherwise.
569  */
570 int
571 dlclose(void *handle)
572 {
573 	int		error, entry;
574 	Rt_map		*clmp;
575 
576 	entry = enter(0);
577 
578 	clmp = _caller(caller(), CL_EXECDEF);
579 
580 	error = dlclose_check(handle, clmp);
581 
582 	if (entry)
583 		leave(LIST(clmp), 0);
584 	return (error);
585 }
586 
587 static uint_t	lmid = 0;
588 
589 /*
590  * The addition of new link-map lists is assumed to be in small quantities.
591  * Here, we assign a unique link-map id for diagnostic use.  Simply update the
592  * running link-map count until we max out.
593  */
594 int
595 newlmid(Lm_list *lml)
596 {
597 	char	buffer[MSG_LMID_ALT_SIZE + 12];
598 
599 	if (lmid == UINT_MAX) {
600 		lml->lm_lmid = UINT_MAX;
601 		(void) strncpy(buffer, MSG_ORIG(MSG_LMID_MAXED),
602 		    MSG_LMID_ALT_SIZE + 12);
603 	} else {
604 		lml->lm_lmid = lmid++;
605 		(void) snprintf(buffer, MSG_LMID_ALT_SIZE + 12,
606 		    MSG_ORIG(MSG_LMID_FMT), MSG_ORIG(MSG_LMID_ALT),
607 		    lml->lm_lmid);
608 	}
609 	if ((lml->lm_lmidstr = strdup(buffer)) == NULL)
610 		return (0);
611 
612 	return (1);
613 }
614 
615 /*
616  * Core dlopen activity.
617  */
618 static Grp_hdl *
619 dlmopen_core(Lm_list *lml, Lm_list *olml, const char *path, int mode,
620     Rt_map *clmp, uint_t flags, uint_t orig, int *in_nfavl)
621 {
622 	Alist		*palp = NULL;
623 	Rt_map		*nlmp;
624 	Grp_hdl		*ghp;
625 	Aliste		olmco, nlmco;
626 
627 	DBG_CALL(Dbg_file_dlopen(clmp,
628 	    (path ? path : MSG_ORIG(MSG_STR_ZERO)), in_nfavl, mode));
629 
630 	/*
631 	 * Having diagnosed the originally defined modes, assign any defaults
632 	 * or corrections.
633 	 */
634 	if (((mode & (RTLD_GROUP | RTLD_WORLD)) == 0) &&
635 	    ((mode & RTLD_NOLOAD) == 0))
636 		mode |= (RTLD_GROUP | RTLD_WORLD);
637 	if ((mode & RTLD_NOW) && (rtld_flags2 & RT_FL2_BINDLAZY)) {
638 		mode &= ~RTLD_NOW;
639 		mode |= RTLD_LAZY;
640 	}
641 
642 	/*
643 	 * If the path specified is null then we're operating on global
644 	 * objects.  Associate a dummy handle with the link-map list.
645 	 */
646 	if (path == NULL) {
647 		Grp_hdl *ghp;
648 		uint_t	hflags, rdflags, cdflags;
649 		int	promote = 0;
650 
651 		/*
652 		 * Establish any flags for the handle (Grp_hdl).
653 		 *
654 		 *  -	This is a dummy, public, handle (0) that provides for a
655 		 *	dynamic	search of all global objects within the process.
656 		 *  -   Use of the RTLD_FIRST mode indicates that only the first
657 		 *	dependency on the handle (the referenced object) can be
658 		 *	used to satisfy dlsym() requests.
659 		 */
660 		hflags = (GPH_PUBLIC | GPH_ZERO);
661 		if (mode & RTLD_FIRST)
662 			hflags |= GPH_FIRST;
663 
664 		/*
665 		 * Establish the flags for the referenced dependency descriptor
666 		 * (Grp_desc).
667 		 *
668 		 *  -	The referenced object is available for dlsym().
669 		 *  -	The referenced object is available to relocate against.
670 		 *  -	The referenced object should have it's dependencies
671 		 *	added to this handle.
672 		 */
673 		rdflags = (GPD_DLSYM | GPD_RELOC | GPD_ADDEPS);
674 
675 		/*
676 		 * Establish the flags for this callers dependency descriptor
677 		 * (Grp_desc).
678 		 *
679 		 *  -	The explicit creation of a handle creates a descriptor
680 		 *	for the referenced object and the parent (caller).
681 		 *  -	Use of the RTLD_PARENT flag indicates that the parent
682 		 *	can be relocated against.
683 		 */
684 		cdflags = GPD_PARENT;
685 		if (mode & RTLD_PARENT)
686 			cdflags |= GPD_RELOC;
687 
688 		if ((ghp = hdl_create(lml, 0, clmp, hflags, rdflags,
689 		    cdflags)) == NULL)
690 			return (NULL);
691 
692 		/*
693 		 * Traverse the main link-map control list, updating the mode
694 		 * of any objects as necessary.  Call the relocation engine if
695 		 * this mode promotes the existing state of any relocations.
696 		 * crle()'s first pass loads all objects necessary for building
697 		 * a configuration file, however none of them are relocated.
698 		 * crle()'s second pass relocates objects in preparation for
699 		 * dldump()'ing using dlopen(0, RTLD_NOW).
700 		 */
701 		if ((mode & (RTLD_NOW | RTLD_CONFGEN)) == RTLD_CONFGEN)
702 			return (ghp);
703 
704 		for (nlmp = lml->lm_head; nlmp; nlmp = NEXT_RT_MAP(nlmp)) {
705 			if (((MODE(nlmp) & RTLD_GLOBAL) == 0) ||
706 			    (FLAGS(nlmp) & FLG_RT_DELETE))
707 				continue;
708 
709 			if (update_mode(nlmp, MODE(nlmp), mode))
710 				promote = 1;
711 		}
712 		if (promote)
713 			(void) relocate_lmc(lml, ALIST_OFF_DATA, clmp,
714 			    lml->lm_head, in_nfavl);
715 
716 		return (ghp);
717 	}
718 
719 	/*
720 	 * Fix the pathname.  If this object expands to multiple paths (ie.
721 	 * $ISALIST or $HWCAP have been used), then make sure the user has also
722 	 * furnished the RTLD_FIRST flag.  As yet, we don't support opening
723 	 * more than one object at a time, so enforcing the RTLD_FIRST flag
724 	 * provides flexibility should we be able to support dlopening more
725 	 * than one object in the future.
726 	 */
727 	if (LM_FIX_NAME(clmp)(path, clmp, &palp, AL_CNT_NEEDED, orig) == NULL)
728 		return (NULL);
729 
730 	if ((palp->al_arritems > 1) && ((mode & RTLD_FIRST) == 0)) {
731 		remove_plist(&palp, 1);
732 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLMODE_5));
733 		return (NULL);
734 	}
735 
736 	/*
737 	 * Establish a link-map control list for this request, and load the
738 	 * associated object.
739 	 */
740 	if ((nlmco = create_cntl(lml, 1)) == NULL) {
741 		remove_plist(&palp, 1);
742 		return (NULL);
743 	}
744 	olmco = nlmco;
745 
746 	nlmp = load_one(lml, nlmco, palp, clmp, mode, (flags | FLG_RT_PUBHDL),
747 	    &ghp, in_nfavl);
748 
749 	/*
750 	 * Remove any expanded pathname infrastructure, and if the dependency
751 	 * couldn't be loaded, cleanup.
752 	 */
753 	remove_plist(&palp, 1);
754 	if (nlmp == NULL) {
755 		remove_cntl(lml, olmco);
756 		return (NULL);
757 	}
758 
759 	/*
760 	 * If loading an auditor was requested, and the auditor already existed,
761 	 * then the link-map returned will be to the original auditor.  The new
762 	 * link-map list that was initially created, and the associated link-map
763 	 * control list are no longer needed.  As the auditor is already loaded,
764 	 * we're probably done, but fall through in case additional relocations
765 	 * would be triggered by the mode of the caller.
766 	 */
767 	if ((flags & FLG_RT_AUDIT) && (LIST(nlmp) != lml)) {
768 		remove_cntl(lml, olmco);
769 		lml = LIST(nlmp);
770 		olmco = 0;
771 		nlmco = ALIST_OFF_DATA;
772 	}
773 
774 	/*
775 	 * Finish processing the objects associated with this request.
776 	 */
777 	if (((nlmp = analyze_lmc(lml, nlmco, nlmp, in_nfavl)) == NULL) ||
778 	    (relocate_lmc(lml, nlmco, clmp, nlmp, in_nfavl) == 0)) {
779 		ghp = NULL;
780 		nlmp = NULL;
781 	}
782 
783 	/*
784 	 * If the dlopen has failed, clean up any objects that might have been
785 	 * loaded successfully on this new link-map control list.
786 	 */
787 	if (olmco && (nlmp == NULL))
788 		remove_lmc(lml, clmp, olmco, path);
789 
790 	/*
791 	 * Finally, remove any temporary link-map control list.  Note, if this
792 	 * operation successfully established a new link-map list, then a base
793 	 * link-map control list will have been created, which must remain.
794 	 */
795 	if (olmco && ((nlmp == NULL) || (olml != (Lm_list *)LM_ID_NEWLM)))
796 		remove_cntl(lml, olmco);
797 
798 	return (ghp);
799 }
800 
801 /*
802  * dlopen() and dlsym() operations are the means by which a process can
803  * test for the existence of required dependencies.  If the necessary
804  * dependencies don't exist, then associated functionality can't be used.
805  * However, the lack of dependencies can be fixed, and the dlopen() and
806  * dlsym() requests can be repeated.  As we use a "not-found" AVL tree to
807  * cache any failed full path loads, secondary dlopen() and dlsym() requests
808  * will fail, even if the dependencies have been installed.
809  *
810  * dlopen() and dlsym() retry any failures by removing the "not-found" AVL
811  * tree.  Should any dependencies be found, their names are added to the
812  * FullPath AVL tree.  This routine removes any new "not-found" AVL tree,
813  * so that the dlopen() or dlsym() can replace the original "not-found" tree.
814  */
815 inline static void
816 nfavl_remove(avl_tree_t *avlt)
817 {
818 	PathNode	*pnp;
819 	void		*cookie = NULL;
820 
821 	if (avlt) {
822 		while ((pnp = avl_destroy_nodes(avlt, &cookie)) != NULL)
823 			free(pnp);
824 
825 		avl_destroy(avlt);
826 		free(avlt);
827 	}
828 }
829 
830 /*
831  * Internal dlopen() activity.  Called from user level or directly for internal
832  * opens that require a handle.
833  */
834 Grp_hdl *
835 dlmopen_intn(Lm_list *lml, const char *path, int mode, Rt_map *clmp,
836     uint_t flags, uint_t orig)
837 {
838 	Lm_list	*olml = lml;
839 	Rt_map	*dlmp = NULL;
840 	Grp_hdl	*ghp;
841 	int	in_nfavl = 0;
842 
843 	/*
844 	 * Check for magic link-map list values:
845 	 *
846 	 *  LM_ID_BASE:		Operate on the PRIMARY (executables) link map
847 	 *  LM_ID_LDSO:		Operation on ld.so.1's link map
848 	 *  LM_ID_NEWLM: 	Create a new link-map.
849 	 */
850 	if (lml == (Lm_list *)LM_ID_NEWLM) {
851 		if ((lml = calloc(sizeof (Lm_list), 1)) == NULL)
852 			return (NULL);
853 
854 		/*
855 		 * Establish the new link-map flags from the callers and those
856 		 * explicitly provided.
857 		 */
858 		lml->lm_tflags = LIST(clmp)->lm_tflags;
859 		if (flags & FLG_RT_AUDIT) {
860 			/*
861 			 * Unset any auditing flags - an auditor shouldn't be
862 			 * audited.  Insure all audit dependencies are loaded.
863 			 */
864 			lml->lm_tflags &= ~LML_TFLG_AUD_MASK;
865 			lml->lm_tflags |=
866 			    (LML_TFLG_NOLAZYLD | LML_TFLG_LOADFLTR);
867 			lml->lm_flags |= LML_FLG_NOAUDIT;
868 		}
869 
870 		if (aplist_append(&dynlm_list, lml, AL_CNT_DYNLIST) == NULL) {
871 			free(lml);
872 			return (NULL);
873 		}
874 		if (newlmid(lml) == 0) {
875 			(void) aplist_delete_value(dynlm_list, lml);
876 			free(lml);
877 			return (NULL);
878 		}
879 	} else if ((uintptr_t)lml < LM_ID_NUM) {
880 		if ((uintptr_t)lml == LM_ID_BASE)
881 			lml = &lml_main;
882 		else if ((uintptr_t)lml == LM_ID_LDSO)
883 			lml = &lml_rtld;
884 	}
885 
886 	/*
887 	 * Open the required object on the associated link-map list.
888 	 */
889 	ghp = dlmopen_core(lml, olml, path, mode, clmp, flags, orig, &in_nfavl);
890 
891 	/*
892 	 * If the object could not be found it is possible that the "not-found"
893 	 * AVL tree had indicated that the file does not exist.  In case the
894 	 * file system has changed since this "not-found" recording was made,
895 	 * retry the dlopen() with a clean "not-found" AVL tree.
896 	 */
897 	if ((ghp == NULL) && in_nfavl) {
898 		avl_tree_t	*oavlt = nfavl;
899 
900 		nfavl = NULL;
901 		ghp = dlmopen_core(lml, olml, path, mode, clmp, flags, orig,
902 		    NULL);
903 
904 		/*
905 		 * If the file is found, then its full path name will have been
906 		 * registered in the FullPath AVL tree.  Remove any new
907 		 * "not-found" AVL information, and restore the former AVL tree.
908 		 */
909 		nfavl_remove(nfavl);
910 		nfavl = oavlt;
911 	}
912 
913 	/*
914 	 * Establish the new link-map from which .init processing will begin.
915 	 * Ignore .init firing when constructing a configuration file (crle(1)).
916 	 */
917 	if (ghp && ((mode & RTLD_CONFGEN) == 0))
918 		dlmp = ghp->gh_ownlmp;
919 
920 	/*
921 	 * If loading an auditor was requested, and the auditor already existed,
922 	 * then the link-map returned will be to the original auditor.  Remove
923 	 * the link-map control list that was created for this request.
924 	 */
925 	if (dlmp && (flags & FLG_RT_AUDIT) && (LIST(dlmp) != lml)) {
926 		remove_lml(lml);
927 		lml = LIST(dlmp);
928 	}
929 
930 	/*
931 	 * If this load failed, remove any alternative link-map list.
932 	 */
933 	if ((ghp == NULL) &&
934 	    ((lml->lm_flags & (LML_FLG_BASELM | LML_FLG_RTLDLM)) == 0)) {
935 		remove_lml(lml);
936 		lml = NULL;
937 	}
938 
939 	/*
940 	 * Finish this load request.  If objects were loaded, .init processing
941 	 * is computed.  Finally, the debuggers are informed of the link-map
942 	 * lists being stable.
943 	 */
944 	load_completion(dlmp);
945 
946 	return (ghp);
947 }
948 
949 /*
950  * Argument checking for dlopen.  Only called via external entry.
951  */
952 static Grp_hdl *
953 dlmopen_check(Lm_list *lml, const char *path, int mode, Rt_map *clmp)
954 {
955 	/*
956 	 * Verify that a valid pathname has been supplied.
957 	 */
958 	if (path && (*path == '\0')) {
959 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLPATH));
960 		return (0);
961 	}
962 
963 	/*
964 	 * Historically we've always verified the mode is either RTLD_NOW or
965 	 * RTLD_LAZY.  RTLD_NOLOAD is valid by itself.  Use of LM_ID_NEWLM
966 	 * requires a specific pathname, and use of RTLD_PARENT is meaningless.
967 	 */
968 	if ((mode & (RTLD_NOW | RTLD_LAZY | RTLD_NOLOAD)) == 0) {
969 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLMODE_1));
970 		return (0);
971 	}
972 	if ((mode & (RTLD_NOW | RTLD_LAZY)) == (RTLD_NOW | RTLD_LAZY)) {
973 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLMODE_2));
974 		return (0);
975 	}
976 	if ((lml == (Lm_list *)LM_ID_NEWLM) && (path == NULL)) {
977 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLMODE_3));
978 		return (0);
979 	}
980 	if ((lml == (Lm_list *)LM_ID_NEWLM) && (mode & RTLD_PARENT)) {
981 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLMODE_4));
982 		return (0);
983 	}
984 
985 	return (dlmopen_intn(lml, path, mode, clmp, 0, 0));
986 }
987 
988 #pragma weak _dlopen = dlopen
989 
990 /*
991  * External entry for dlopen(3dl).  On success, returns a pointer (handle) to
992  * the structure containing information about the newly added object, ie. can
993  * be used by dlsym(). On failure, returns a null pointer.
994  */
995 void *
996 dlopen(const char *path, int mode)
997 {
998 	int	entry;
999 	Rt_map	*clmp;
1000 	Grp_hdl	*ghp;
1001 	Lm_list	*lml;
1002 
1003 	entry = enter(0);
1004 
1005 	clmp = _caller(caller(), CL_EXECDEF);
1006 	lml = LIST(clmp);
1007 
1008 	ghp = dlmopen_check(lml, path, mode, clmp);
1009 
1010 	if (entry)
1011 		leave(lml, 0);
1012 	return ((void *)ghp);
1013 }
1014 
1015 #pragma weak _dlmopen = dlmopen
1016 
1017 /*
1018  * External entry for dlmopen(3dl).
1019  */
1020 void *
1021 dlmopen(Lmid_t lmid, const char *path, int mode)
1022 {
1023 	int	entry;
1024 	Rt_map	*clmp;
1025 	Grp_hdl	*ghp;
1026 
1027 	entry = enter(0);
1028 
1029 	clmp = _caller(caller(), CL_EXECDEF);
1030 
1031 	ghp = dlmopen_check((Lm_list *)lmid, path, mode, clmp);
1032 
1033 	if (entry)
1034 		leave(LIST(clmp), 0);
1035 	return ((void *)ghp);
1036 }
1037 
1038 /*
1039  * Handle processing for dlsym.
1040  */
1041 Sym *
1042 dlsym_handle(Grp_hdl *ghp, Slookup *slp, Rt_map **_lmp, uint_t *binfo,
1043     int *in_nfavl)
1044 {
1045 	Rt_map		*nlmp, * lmp = ghp->gh_ownlmp;
1046 	Rt_map		*clmp = slp->sl_cmap;
1047 	const char	*name = slp->sl_name;
1048 	Sym		*sym = NULL;
1049 	Slookup		sl = *slp;
1050 
1051 	sl.sl_flags = (LKUP_FIRST | LKUP_SPEC);
1052 
1053 	/*
1054 	 * Continue processing a dlsym request.  Lookup the required symbol in
1055 	 * each link-map specified by the handle.
1056 	 *
1057 	 * To leverage off of lazy loading, dlsym() requests can result in two
1058 	 * passes.  The first descends the link-maps of any objects already in
1059 	 * the address space.  If the symbol isn't located, and lazy
1060 	 * dependencies still exist, then a second pass is made to load these
1061 	 * dependencies if applicable.  This model means that in the case where
1062 	 * a symbols exists in more than one object, the one located may not be
1063 	 * constant - this is the standard issue with lazy loading. In addition,
1064 	 * attempting to locate a symbol that doesn't exist will result in the
1065 	 * loading of all lazy dependencies on the given handle, which can
1066 	 * defeat some of the advantages of lazy loading (look out JVM).
1067 	 */
1068 	if (ghp->gh_flags & GPH_ZERO) {
1069 		Lm_list	*lml;
1070 
1071 		/*
1072 		 * If this symbol lookup is triggered from a dlopen(0) handle,
1073 		 * traverse the present link-map list looking for promiscuous
1074 		 * entries.
1075 		 */
1076 		for (nlmp = lmp; nlmp; nlmp = NEXT_RT_MAP(nlmp)) {
1077 
1078 			/*
1079 			 * If this handle indicates we're only to look in the
1080 			 * first object check whether we're done.
1081 			 */
1082 			if ((nlmp != lmp) && (ghp->gh_flags & GPH_FIRST))
1083 				return (NULL);
1084 
1085 			if (!(MODE(nlmp) & RTLD_GLOBAL))
1086 				continue;
1087 			if ((FLAGS(nlmp) & FLG_RT_DELETE) &&
1088 			    ((FLAGS(clmp) & FLG_RT_DELETE) == 0))
1089 				continue;
1090 
1091 			sl.sl_imap = nlmp;
1092 			if (sym = LM_LOOKUP_SYM(clmp)(&sl, _lmp, binfo,
1093 			    in_nfavl))
1094 				return (sym);
1095 		}
1096 
1097 		/*
1098 		 * If we're unable to locate the symbol and this link-map still
1099 		 * has pending lazy dependencies, start loading them in an
1100 		 * attempt to exhaust the search.  Note that as we're already
1101 		 * traversing a dynamic linked list of link-maps there's no
1102 		 * need for elf_lazy_find_sym() to descend the link-maps itself.
1103 		 */
1104 		lml = LIST(lmp);
1105 		if ((lml->lm_lazy) &&
1106 		    ((lml->lm_flags & LML_FLG_NOPENDGLBLAZY) == 0)) {
1107 			int	lazy = 0;
1108 
1109 			DBG_CALL(Dbg_syms_lazy_rescan(lml, name));
1110 
1111 			sl.sl_flags |= LKUP_NODESCENT;
1112 
1113 			for (nlmp = lmp; nlmp; nlmp = NEXT_RT_MAP(nlmp)) {
1114 
1115 				if (!(MODE(nlmp) & RTLD_GLOBAL) || !LAZY(nlmp))
1116 					continue;
1117 				if ((FLAGS(nlmp) & FLG_RT_DELETE) &&
1118 				    ((FLAGS(clmp) & FLG_RT_DELETE) == 0))
1119 					continue;
1120 
1121 				lazy = 1;
1122 				sl.sl_imap = nlmp;
1123 				if (sym = elf_lazy_find_sym(&sl, _lmp, binfo,
1124 				    in_nfavl))
1125 					return (sym);
1126 			}
1127 
1128 			/*
1129 			 * If no global, lazy loadable dependencies are found,
1130 			 * then none exist for this link-map list.  Pending lazy
1131 			 * loadable objects may still exist for non-local
1132 			 * objects that are associated with this link-map list,
1133 			 * which is why we entered this fallback.  Tag this
1134 			 * link-map list to prevent further searching for lazy
1135 			 * dependencies.
1136 			 */
1137 			if (lazy == 0)
1138 				lml->lm_flags |= LML_FLG_NOPENDGLBLAZY;
1139 		}
1140 	} else {
1141 		/*
1142 		 * Traverse the dlopen() handle for the presently loaded
1143 		 * link-maps.
1144 		 */
1145 		Grp_desc	*gdp;
1146 		Aliste		idx;
1147 
1148 		for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
1149 			if ((gdp->gd_flags & GPD_DLSYM) == 0)
1150 				continue;
1151 
1152 			sl.sl_imap = gdp->gd_depend;
1153 			if (sym = LM_LOOKUP_SYM(clmp)(&sl, _lmp, binfo,
1154 			    in_nfavl))
1155 				return (sym);
1156 
1157 			if (ghp->gh_flags & GPH_FIRST)
1158 				return (NULL);
1159 		}
1160 
1161 		/*
1162 		 * If we're unable to locate the symbol and this link-map still
1163 		 * has pending lazy dependencies, start loading them in an
1164 		 * attempt to exhaust the search.
1165 		 */
1166 		if ((LIST(lmp)->lm_lazy) &&
1167 		    ((ghp->gh_flags & GPH_NOPENDLAZY) == 0)) {
1168 			int	lazy = 0;
1169 
1170 			DBG_CALL(Dbg_syms_lazy_rescan(LIST(lmp), name));
1171 
1172 			for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
1173 				nlmp = gdp->gd_depend;
1174 
1175 				if (((gdp->gd_flags & GPD_DLSYM) == 0) ||
1176 				    (LAZY(nlmp) == 0))
1177 					continue;
1178 
1179 				lazy = 1;
1180 				sl.sl_imap = nlmp;
1181 				if (sym = elf_lazy_find_sym(&sl, _lmp,
1182 				    binfo, in_nfavl))
1183 					return (sym);
1184 			}
1185 
1186 			/*
1187 			 * If no lazy loadable dependencies are found, then
1188 			 * none exist for this handle.  Pending lazy loadable
1189 			 * objects may still exist for the associated link-map
1190 			 * list, which is why we entered this fallback.  Tag
1191 			 * this handle to prevent further searching for lazy
1192 			 * dependencies.
1193 			 */
1194 			if (lazy == 0)
1195 				ghp->gh_flags |= GPH_NOPENDLAZY;
1196 		}
1197 	}
1198 	return (NULL);
1199 }
1200 
1201 /*
1202  * Core dlsym activity.  Selects symbol lookup method from handle.
1203  */
1204 void *
1205 dlsym_core(void *handle, const char *name, Rt_map *clmp, Rt_map **dlmp,
1206     int *in_nfavl)
1207 {
1208 	Sym		*sym = NULL;
1209 	Syminfo		*sip;
1210 	Slookup		sl;
1211 	uint_t		binfo;
1212 
1213 	/*
1214 	 * Initialize the symbol lookup data structure.
1215 	 *
1216 	 * Standard relocations are evaluated using the symbol index of the
1217 	 * associated relocation symbol.  This index provides for loading
1218 	 * any lazy dependency and establishing a direct binding if necessary.
1219 	 * If a dlsym() operation originates from an object that contains a
1220 	 * symbol table entry for the same name, then we need to establish the
1221 	 * symbol index so that any dependency requirements can be triggered.
1222 	 *
1223 	 * Therefore, the first symbol lookup that is carried out is for the
1224 	 * symbol name within the calling object.  If this symbol exists, the
1225 	 * symbols index is computed, added to the Slookup data, and thus used
1226 	 * to seed the real symbol lookup.
1227 	 */
1228 	SLOOKUP_INIT(sl, name, clmp, clmp, ld_entry_cnt, elf_hash(name),
1229 	    0, 0, 0, LKUP_SYMNDX);
1230 
1231 	if (THIS_IS_ELF(clmp) &&
1232 	    ((sym = SYMINTP(clmp)(&sl, 0, 0, NULL)) != NULL)) {
1233 		sl.sl_rsymndx = (((ulong_t)sym -
1234 		    (ulong_t)SYMTAB(clmp)) / SYMENT(clmp));
1235 		sl.sl_rsym = sym;
1236 	}
1237 
1238 	if (sym && (ELF_ST_VISIBILITY(sym->st_other) == STV_SINGLETON)) {
1239 		Rt_map	*hlmp = LIST(clmp)->lm_head;
1240 
1241 		/*
1242 		 * If a symbol reference is known, and that reference indicates
1243 		 * that the symbol is a singleton, then the search for the
1244 		 * symbol must follow the default search path.
1245 		 */
1246 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl, 0,
1247 		    DBG_DLSYM_SINGLETON));
1248 
1249 		sl.sl_imap = hlmp;
1250 		sl.sl_flags = LKUP_SPEC;
1251 		if (handle == RTLD_PROBE)
1252 			sl.sl_flags |= LKUP_NOFALLBACK;
1253 		sym = LM_LOOKUP_SYM(clmp)(&sl, dlmp, &binfo, in_nfavl);
1254 
1255 	} else if (handle == RTLD_NEXT) {
1256 		Rt_map	*nlmp;
1257 
1258 		/*
1259 		 * If this handle is RTLD_NEXT determine whether a lazy load
1260 		 * from the caller might provide the next object.  This mimics
1261 		 * the lazy loading initialization normally carried out by
1262 		 * lookup_sym(), however here, we must do this up-front, as
1263 		 * lookup_sym() will be used to inspect the next object.
1264 		 */
1265 		if ((sl.sl_rsymndx) && ((sip = SYMINFO(clmp)) != NULL)) {
1266 			/* LINTED */
1267 			sip = (Syminfo *)((char *)sip +
1268 			    (sl.sl_rsymndx * SYMINENT(clmp)));
1269 
1270 			if ((sip->si_flags & SYMINFO_FLG_DIRECT) &&
1271 			    (sip->si_boundto < SYMINFO_BT_LOWRESERVE))
1272 				(void) elf_lazy_load(clmp, &sl,
1273 				    sip->si_boundto, name, 0, NULL, in_nfavl);
1274 
1275 			/*
1276 			 * Clear the symbol index, so as not to confuse
1277 			 * lookup_sym() of the next object.
1278 			 */
1279 			sl.sl_rsymndx = 0;
1280 			sl.sl_rsym = NULL;
1281 		}
1282 
1283 		/*
1284 		 * If the handle is RTLD_NEXT start searching in the next link
1285 		 * map from the callers.  Determine permissions from the
1286 		 * present link map.  Indicate to lookup_sym() that we're on an
1287 		 * RTLD_NEXT request so that it will use the callers link map to
1288 		 * start any possible lazy dependency loading.
1289 		 */
1290 		sl.sl_imap = nlmp = NEXT_RT_MAP(clmp);
1291 
1292 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl,
1293 		    (nlmp ? NAME(nlmp) : MSG_INTL(MSG_STR_NULL)),
1294 		    DBG_DLSYM_NEXT));
1295 
1296 		if (nlmp == NULL)
1297 			return (0);
1298 
1299 		sl.sl_flags = LKUP_NEXT;
1300 		sym = LM_LOOKUP_SYM(clmp)(&sl, dlmp, &binfo, in_nfavl);
1301 
1302 	} else if (handle == RTLD_SELF) {
1303 		/*
1304 		 * If the handle is RTLD_SELF start searching from the caller.
1305 		 */
1306 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl, NAME(clmp),
1307 		    DBG_DLSYM_SELF));
1308 
1309 		sl.sl_imap = clmp;
1310 		sl.sl_flags = (LKUP_SPEC | LKUP_SELF);
1311 		sym = LM_LOOKUP_SYM(clmp)(&sl, dlmp, &binfo, in_nfavl);
1312 
1313 	} else if (handle == RTLD_DEFAULT) {
1314 		Rt_map	*hlmp = LIST(clmp)->lm_head;
1315 
1316 		/*
1317 		 * If the handle is RTLD_DEFAULT mimic the standard symbol
1318 		 * lookup as would be triggered by a relocation.
1319 		 */
1320 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl, 0,
1321 		    DBG_DLSYM_DEFAULT));
1322 
1323 		sl.sl_imap = hlmp;
1324 		sl.sl_flags = LKUP_SPEC;
1325 		sym = LM_LOOKUP_SYM(clmp)(&sl, dlmp, &binfo, in_nfavl);
1326 
1327 	} else if (handle == RTLD_PROBE) {
1328 		Rt_map	*hlmp = LIST(clmp)->lm_head;
1329 
1330 		/*
1331 		 * If the handle is RTLD_PROBE, mimic the standard symbol
1332 		 * lookup as would be triggered by a relocation, however do
1333 		 * not fall back to a lazy loading rescan if the symbol can't be
1334 		 * found within the currently loaded objects.  Note, a lazy
1335 		 * loaded dependency required by the caller might still get
1336 		 * loaded to satisfy this request, but no exhaustive lazy load
1337 		 * rescan is carried out.
1338 		 */
1339 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl, 0,
1340 		    DBG_DLSYM_PROBE));
1341 
1342 		sl.sl_imap = hlmp;
1343 		sl.sl_flags = (LKUP_SPEC | LKUP_NOFALLBACK);
1344 		sym = LM_LOOKUP_SYM(clmp)(&sl, dlmp, &binfo, in_nfavl);
1345 
1346 	} else {
1347 		Grp_hdl *ghp = (Grp_hdl *)handle;
1348 
1349 		/*
1350 		 * Look in the shared object specified by the handle and in all
1351 		 * of its dependencies.
1352 		 */
1353 		DBG_CALL(Dbg_syms_dlsym(clmp, name, in_nfavl,
1354 		    NAME(ghp->gh_ownlmp), DBG_DLSYM_DEF));
1355 
1356 		sym = LM_DLSYM(clmp)(ghp, &sl, dlmp, &binfo, in_nfavl);
1357 	}
1358 
1359 	if (sym) {
1360 		Lm_list	*lml = LIST(clmp);
1361 		Addr	addr = sym->st_value;
1362 
1363 		if (!(FLAGS(*dlmp) & FLG_RT_FIXED))
1364 			addr += ADDR(*dlmp);
1365 
1366 		/*
1367 		 * Indicate that the defining object is now used.
1368 		 */
1369 		if (*dlmp != clmp)
1370 			FLAGS1(*dlmp) |= FL1_RT_USED;
1371 
1372 		DBG_CALL(Dbg_bind_global(clmp, 0, 0, (Xword)-1, PLT_T_NONE,
1373 		    *dlmp, addr, sym->st_value, name, binfo));
1374 
1375 		if ((lml->lm_tflags | AFLAGS(clmp)) & LML_TFLG_AUD_SYMBIND) {
1376 			uint_t	sb_flags = LA_SYMB_DLSYM;
1377 			/* LINTED */
1378 			uint_t	symndx = (uint_t)(((Xword)sym -
1379 			    (Xword)SYMTAB(*dlmp)) / SYMENT(*dlmp));
1380 			addr = audit_symbind(clmp, *dlmp, sym, symndx, addr,
1381 			    &sb_flags);
1382 		}
1383 		return ((void *)addr);
1384 	} else
1385 		return (0);
1386 }
1387 
1388 /*
1389  * Internal dlsym activity.  Called from user level or directly for internal
1390  * symbol lookup.
1391  */
1392 void *
1393 dlsym_intn(void *handle, const char *name, Rt_map *clmp, Rt_map **dlmp)
1394 {
1395 	Rt_map		*llmp = NULL;
1396 	void		*error;
1397 	Aliste		idx;
1398 	Grp_desc	*gdp;
1399 	int		in_nfavl = 0;
1400 
1401 	/*
1402 	 * While looking for symbols it's quite possible that additional objects
1403 	 * get loaded from lazy loading.  These objects will have been added to
1404 	 * the same link-map list as those objects on the handle.  Remember this
1405 	 * list for later investigation.
1406 	 */
1407 	if ((handle == RTLD_NEXT) || (handle == RTLD_DEFAULT) ||
1408 	    (handle == RTLD_SELF) || (handle == RTLD_PROBE))
1409 		llmp = LIST(clmp)->lm_tail;
1410 	else {
1411 		Grp_hdl	*ghp = (Grp_hdl *)handle;
1412 
1413 		if (ghp->gh_ownlmp)
1414 			llmp = LIST(ghp->gh_ownlmp)->lm_tail;
1415 		else {
1416 			for (ALIST_TRAVERSE(ghp->gh_depends, idx, gdp)) {
1417 				if ((llmp =
1418 				    LIST(gdp->gd_depend)->lm_tail) != NULL)
1419 					break;
1420 			}
1421 		}
1422 	}
1423 
1424 	error = dlsym_core(handle, name, clmp, dlmp, &in_nfavl);
1425 
1426 	/*
1427 	 * If the symbol could not be found it is possible that the "not-found"
1428 	 * AVL tree had indicated that a required file does not exist.  In case
1429 	 * the file system has changed since this "not-found" recording was
1430 	 * made, retry the dlsym() with a clean "not-found" AVL tree.
1431 	 */
1432 	if ((error == NULL) && in_nfavl) {
1433 		avl_tree_t	*oavlt = nfavl;
1434 
1435 		nfavl = NULL;
1436 		error = dlsym_core(handle, name, clmp, dlmp, NULL);
1437 
1438 		/*
1439 		 * If the symbol is found, then any file that was loaded will
1440 		 * have had its full path name registered in the FullPath AVL
1441 		 * tree.  Remove any new "not-found" AVL information, and
1442 		 * restore the former AVL tree.
1443 		 */
1444 		nfavl_remove(nfavl);
1445 		nfavl = oavlt;
1446 	}
1447 
1448 	if (error == NULL) {
1449 		/*
1450 		 * Cache the error message, as Java tends to fall through this
1451 		 * code many times.
1452 		 */
1453 		if (nosym_str == NULL)
1454 			nosym_str = MSG_INTL(MSG_GEN_NOSYM);
1455 		eprintf(LIST(clmp), ERR_FATAL, nosym_str, name);
1456 	}
1457 
1458 	load_completion(llmp);
1459 	return (error);
1460 }
1461 
1462 /*
1463  * Argument checking for dlsym.  Only called via external entry.
1464  */
1465 static void *
1466 dlsym_check(void *handle, const char *name, Rt_map *clmp, Rt_map **dlmp)
1467 {
1468 	/*
1469 	 * Verify the arguments.
1470 	 */
1471 	if (name == NULL) {
1472 		eprintf(LIST(clmp), ERR_FATAL, MSG_INTL(MSG_ARG_ILLSYM));
1473 		return (NULL);
1474 	}
1475 	if ((handle != RTLD_NEXT) && (handle != RTLD_DEFAULT) &&
1476 	    (handle != RTLD_SELF) && (handle != RTLD_PROBE) &&
1477 	    (hdl_validate((Grp_hdl *)handle) == 0)) {
1478 		eprintf(LIST(clmp), ERR_FATAL, MSG_INTL(MSG_ARG_INVHNDL),
1479 		    EC_NATPTR(handle));
1480 		return (NULL);
1481 	}
1482 	return (dlsym_intn(handle, name, clmp, dlmp));
1483 }
1484 
1485 
1486 #pragma weak _dlsym = dlsym
1487 
1488 /*
1489  * External entry for dlsym().  On success, returns the address of the specified
1490  * symbol.  On error returns a null.
1491  */
1492 void *
1493 dlsym(void *handle, const char *name)
1494 {
1495 	int	entry;
1496 	Rt_map	*clmp, *dlmp = NULL;
1497 	void	*addr;
1498 
1499 	entry = enter(0);
1500 
1501 	clmp = _caller(caller(), CL_EXECDEF);
1502 
1503 	addr = dlsym_check(handle, name, clmp, &dlmp);
1504 
1505 	if (entry) {
1506 		if (dlmp)
1507 			is_dep_init(dlmp, clmp);
1508 		leave(LIST(clmp), 0);
1509 	}
1510 	return (addr);
1511 }
1512 
1513 /*
1514  * Core dladdr activity.
1515  */
1516 static void
1517 dladdr_core(Rt_map *clmp, void *addr, Dl_info_t *dlip, void **info, int flags)
1518 {
1519 	/*
1520 	 * Set up generic information and any defaults.
1521 	 */
1522 	dlip->dli_fname = PATHNAME(clmp);
1523 
1524 	dlip->dli_fbase = (void *)ADDR(clmp);
1525 	dlip->dli_sname = NULL;
1526 	dlip->dli_saddr = NULL;
1527 
1528 	/*
1529 	 * Determine the nearest symbol to this address.
1530 	 */
1531 	LM_DLADDR(clmp)((ulong_t)addr, clmp, dlip, info, flags);
1532 }
1533 
1534 #pragma weak _dladdr = dladdr
1535 
1536 /*
1537  * External entry for dladdr(3dl) and dladdr1(3dl).  Returns an information
1538  * structure that reflects the symbol closest to the address specified.
1539  */
1540 int
1541 dladdr(void *addr, Dl_info_t *dlip)
1542 {
1543 	int	entry, error;
1544 	Rt_map	*clmp;
1545 
1546 	entry = enter(0);
1547 
1548 	/*
1549 	 * Use our calling technique to determine what object is associated
1550 	 * with the supplied address.  If a caller can't be determined,
1551 	 * indicate the failure.
1552 	 */
1553 	if ((clmp = _caller(addr, CL_NONE)) == NULL) {
1554 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_ARG_INVADDR),
1555 		    EC_NATPTR(addr));
1556 		error = 0;
1557 	} else {
1558 		dladdr_core(clmp, addr, dlip, 0, 0);
1559 		error = 1;
1560 	}
1561 
1562 	if (entry)
1563 		leave(0, 0);
1564 	return (error);
1565 }
1566 
1567 #pragma weak _dladdr1 = dladdr1
1568 
1569 int
1570 dladdr1(void *addr, Dl_info_t *dlip, void **info, int flags)
1571 {
1572 	int	entry, error = 0;
1573 	Rt_map	*clmp;
1574 
1575 	/*
1576 	 * Validate any flags.
1577 	 */
1578 	if (flags) {
1579 		int	request;
1580 
1581 		if (((request = (flags & RTLD_DL_MASK)) != RTLD_DL_SYMENT) &&
1582 		    (request != RTLD_DL_LINKMAP)) {
1583 			eprintf(0, ERR_FATAL, MSG_INTL(MSG_ARG_ILLFLAGS),
1584 			    flags);
1585 			return (0);
1586 		}
1587 		if (info == NULL) {
1588 			eprintf(0, ERR_FATAL, MSG_INTL(MSG_ARG_ILLINFO), flags);
1589 			return (0);
1590 		}
1591 	}
1592 
1593 	entry = enter(0);
1594 
1595 	/*
1596 	 * Use our calling technique to determine what object is associated
1597 	 * with the supplied address.  If a caller can't be determined,
1598 	 * indicate the failure.
1599 	 */
1600 	if ((clmp = _caller(addr, CL_NONE)) == NULL) {
1601 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_ARG_INVADDR),
1602 		    EC_NATPTR(addr));
1603 		error = 0;
1604 	} else {
1605 		dladdr_core(clmp, addr, dlip, info, flags);
1606 		error = 1;
1607 	}
1608 
1609 	if (entry)
1610 		leave(0, 0);
1611 	return (error);
1612 }
1613 
1614 /*
1615  * Core dldump activity.
1616  */
1617 static int
1618 dldump_core(Lm_list *lml, const char *ipath, const char *opath, int flags)
1619 {
1620 	Addr	addr = 0;
1621 	Rt_map	*lmp;
1622 
1623 	/*
1624 	 * Verify any arguments first.
1625 	 */
1626 	if ((!opath || (*opath == '\0')) || (ipath && (*ipath == '\0'))) {
1627 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLPATH));
1628 		return (1);
1629 	}
1630 
1631 	/*
1632 	 * If an input file is specified make sure its one of our dependencies
1633 	 * on the main link-map list.  Note, this has really all evolved for
1634 	 * crle(), which uses libcrle.so on an alternative link-map to trigger
1635 	 * dumping objects from the main link-map list.   If we ever want to
1636 	 * dump objects from alternative link-maps, this model is going to
1637 	 * have to be revisited.
1638 	 */
1639 	if (ipath) {
1640 		if ((lmp = is_so_loaded(&lml_main, ipath, NULL)) == NULL) {
1641 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_GEN_NOFILE),
1642 			    ipath);
1643 			return (1);
1644 		}
1645 		if (FLAGS(lmp) & FLG_RT_ALTER) {
1646 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_GEN_ALTER), ipath);
1647 			return (1);
1648 		}
1649 		if (FLAGS(lmp) & FLG_RT_NODUMP) {
1650 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_GEN_NODUMP),
1651 			    ipath);
1652 			return (1);
1653 		}
1654 	} else
1655 		lmp = lml_main.lm_head;
1656 
1657 
1658 	DBG_CALL(Dbg_file_dldump(lmp, opath, flags));
1659 
1660 	/*
1661 	 * If the object being dump'ed isn't fixed identify its mapping.
1662 	 */
1663 	if (!(FLAGS(lmp) & FLG_RT_FIXED))
1664 		addr = ADDR(lmp);
1665 
1666 	/*
1667 	 * As rt_dldump() will effectively lazy load the necessary support
1668 	 * libraries, make sure ld.so.1 is initialized for plt relocations.
1669 	 */
1670 	if (elf_rtld_load() == 0)
1671 		return (0);
1672 
1673 	/*
1674 	 * Dump the required image.
1675 	 */
1676 	return (rt_dldump(lmp, opath, flags, addr));
1677 }
1678 
1679 #pragma weak _dldump = dldump
1680 
1681 /*
1682  * External entry for dldump(3c).  Returns 0 on success, non-zero otherwise.
1683  */
1684 int
1685 dldump(const char *ipath, const char *opath, int flags)
1686 {
1687 	int	error, entry;
1688 	Rt_map	*clmp;
1689 
1690 	entry = enter(0);
1691 
1692 	clmp = _caller(caller(), CL_EXECDEF);
1693 
1694 	error = dldump_core(LIST(clmp), ipath, opath, flags);
1695 
1696 	if (entry)
1697 		leave(LIST(clmp), 0);
1698 	return (error);
1699 }
1700 
1701 /*
1702  * get_linkmap_id() translates Lm_list * pointers to the Link_map id as used by
1703  * the rtld_db and dlmopen() interfaces.  It checks to see if the Link_map is
1704  * one of the primary ones and if so returns it's special token:
1705  *		LM_ID_BASE
1706  *		LM_ID_LDSO
1707  *
1708  * If it's not one of the primary link_map id's it will instead returns a
1709  * pointer to the Lm_list structure which uniquely identifies the Link_map.
1710  */
1711 Lmid_t
1712 get_linkmap_id(Lm_list *lml)
1713 {
1714 	if (lml->lm_flags & LML_FLG_BASELM)
1715 		return (LM_ID_BASE);
1716 	if (lml->lm_flags & LML_FLG_RTLDLM)
1717 		return (LM_ID_LDSO);
1718 
1719 	return ((Lmid_t)lml);
1720 }
1721 
1722 /*
1723  * Extract information for a dlopen() handle.
1724  */
1725 static int
1726 dlinfo_core(void *handle, int request, void *p, Rt_map *clmp)
1727 {
1728 	Lm_list	*lml = LIST(clmp);
1729 	Rt_map	*lmp;
1730 
1731 	if ((request > RTLD_DI_MAX) || (p == NULL)) {
1732 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLVAL));
1733 		return (-1);
1734 	}
1735 
1736 	/*
1737 	 * Return configuration cache name and address.
1738 	 */
1739 	if (request == RTLD_DI_CONFIGADDR) {
1740 		Dl_info_t	*dlip = (Dl_info_t *)p;
1741 
1742 		if ((config->c_name == NULL) || (config->c_bgn == 0) ||
1743 		    (config->c_end == 0)) {
1744 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_NOCONFIG));
1745 			return (-1);
1746 		}
1747 		dlip->dli_fname = config->c_name;
1748 		dlip->dli_fbase = (void *)config->c_bgn;
1749 		return (0);
1750 	}
1751 
1752 	/*
1753 	 * Return profiled object name (used by ldprof audit library).
1754 	 */
1755 	if (request == RTLD_DI_PROFILENAME) {
1756 		if (profile_name == NULL) {
1757 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_NOPROFNAME));
1758 			return (-1);
1759 		}
1760 
1761 		*(const char **)p = profile_name;
1762 		return (0);
1763 	}
1764 	if (request == RTLD_DI_PROFILEOUT) {
1765 		/*
1766 		 * If a profile destination directory hasn't been specified
1767 		 * provide a default.
1768 		 */
1769 		if (profile_out == NULL)
1770 			profile_out = MSG_ORIG(MSG_PTH_VARTMP);
1771 
1772 		*(const char **)p = profile_out;
1773 		return (0);
1774 	}
1775 
1776 	/*
1777 	 * Obtain or establish a termination signal.
1778 	 */
1779 	if (request == RTLD_DI_GETSIGNAL) {
1780 		*(int *)p = killsig;
1781 		return (0);
1782 	}
1783 
1784 	if (request == RTLD_DI_SETSIGNAL) {
1785 		sigset_t	set;
1786 		int		sig = *(int *)p;
1787 
1788 		/*
1789 		 * Determine whether the signal is in range.
1790 		 */
1791 		(void) sigfillset(&set);
1792 		if (sigismember(&set, sig) != 1) {
1793 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_INVSIG), sig);
1794 			return (-1);
1795 		}
1796 
1797 		killsig = sig;
1798 		return (0);
1799 	}
1800 
1801 	/*
1802 	 * For any other request a link-map is required.  Verify the handle.
1803 	 */
1804 	if (handle == RTLD_SELF)
1805 		lmp = clmp;
1806 	else {
1807 		Grp_hdl	*ghp = (Grp_hdl *)handle;
1808 
1809 		if (!hdl_validate(ghp)) {
1810 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_INVHNDL),
1811 			    EC_NATPTR(handle));
1812 			return (-1);
1813 		}
1814 		lmp = ghp->gh_ownlmp;
1815 	}
1816 
1817 	/*
1818 	 * Obtain the process arguments, environment and auxv.  Note, as the
1819 	 * environment can be modified by the user (putenv(3c)), reinitialize
1820 	 * the environment pointer on each request.
1821 	 */
1822 	if (request == RTLD_DI_ARGSINFO) {
1823 		Dl_argsinfo_t	*aip = (Dl_argsinfo_t *)p;
1824 		Lm_list		*lml = LIST(lmp);
1825 
1826 		*aip = argsinfo;
1827 		if (lml->lm_flags & LML_FLG_ENVIRON)
1828 			aip->dla_envp = *(lml->lm_environ);
1829 
1830 		return (0);
1831 	}
1832 
1833 	/*
1834 	 * Return Lmid_t of the Link-Map list that the specified object is
1835 	 * loaded on.
1836 	 */
1837 	if (request == RTLD_DI_LMID) {
1838 		*(Lmid_t *)p = get_linkmap_id(LIST(lmp));
1839 		return (0);
1840 	}
1841 
1842 	/*
1843 	 * Return a pointer to the Link-Map structure associated with the
1844 	 * specified object.
1845 	 */
1846 	if (request == RTLD_DI_LINKMAP) {
1847 		*(Link_map **)p = (Link_map *)lmp;
1848 		return (0);
1849 	}
1850 
1851 	/*
1852 	 * Return search path information, or the size of the buffer required
1853 	 * to store the information.
1854 	 */
1855 	if ((request == RTLD_DI_SERINFO) || (request == RTLD_DI_SERINFOSIZE)) {
1856 		Spath_desc	sd = { search_rules, NULL, 0 };
1857 		Pdesc		*pdp;
1858 		Dl_serinfo_t	*info;
1859 		Dl_serpath_t	*path;
1860 		char		*strs;
1861 		size_t		size = sizeof (Dl_serinfo_t);
1862 		uint_t		cnt = 0;
1863 
1864 		info = (Dl_serinfo_t *)p;
1865 		path = &info->dls_serpath[0];
1866 		strs = (char *)&info->dls_serpath[info->dls_cnt];
1867 
1868 		/*
1869 		 * Traverse search path entries for this object.
1870 		 */
1871 		while ((pdp = get_next_dir(&sd, lmp, 0)) != NULL) {
1872 			size_t	_size;
1873 
1874 			if (pdp->pd_pname == NULL)
1875 				continue;
1876 
1877 			/*
1878 			 * If configuration information exists, it's possible
1879 			 * this path has been identified as non-existent, if so
1880 			 * ignore it.
1881 			 */
1882 			if (pdp->pd_info) {
1883 				Rtc_obj	*dobj = (Rtc_obj *)pdp->pd_info;
1884 				if (dobj->co_flags & RTC_OBJ_NOEXIST)
1885 					continue;
1886 			}
1887 
1888 			/*
1889 			 * Keep track of search path count and total info size.
1890 			 */
1891 			if (cnt++)
1892 				size += sizeof (Dl_serpath_t);
1893 			_size = pdp->pd_plen + 1;
1894 			size += _size;
1895 
1896 			if (request == RTLD_DI_SERINFOSIZE)
1897 				continue;
1898 
1899 			/*
1900 			 * If we're filling in search path information, confirm
1901 			 * there's sufficient space.
1902 			 */
1903 			if (size > info->dls_size) {
1904 				eprintf(lml, ERR_FATAL,
1905 				    MSG_INTL(MSG_ARG_SERSIZE),
1906 				    EC_OFF(info->dls_size));
1907 				return (-1);
1908 			}
1909 			if (cnt > info->dls_cnt) {
1910 				eprintf(lml, ERR_FATAL,
1911 				    MSG_INTL(MSG_ARG_SERCNT), info->dls_cnt);
1912 				return (-1);
1913 			}
1914 
1915 			/*
1916 			 * Append the path to the information buffer.
1917 			 */
1918 			(void) strcpy(strs, pdp->pd_pname);
1919 			path->dls_name = strs;
1920 			path->dls_flags = pdp->pd_flags;
1921 
1922 			strs = strs + _size;
1923 			path++;
1924 		}
1925 
1926 		/*
1927 		 * If we're here to size the search buffer fill it in.
1928 		 */
1929 		if (request == RTLD_DI_SERINFOSIZE) {
1930 			info->dls_size = size;
1931 			info->dls_cnt = cnt;
1932 		}
1933 
1934 		return (0);
1935 	}
1936 
1937 	/*
1938 	 * Return the origin of the object associated with this link-map.
1939 	 * Basically return the dirname(1) of the objects fullpath.
1940 	 */
1941 	if (request == RTLD_DI_ORIGIN) {
1942 		char	*str = (char *)p;
1943 
1944 		(void) strncpy(str, ORIGNAME(lmp), DIRSZ(lmp));
1945 		str += DIRSZ(lmp);
1946 		*str = '\0';
1947 
1948 		return (0);
1949 	}
1950 
1951 	/*
1952 	 * Return the number of object mappings, or the mapping information for
1953 	 * this object.
1954 	 */
1955 	if (request == RTLD_DI_MMAPCNT) {
1956 		uint_t	*cnt = (uint_t *)p;
1957 
1958 		*cnt = MMAPCNT(lmp);
1959 		return (0);
1960 	}
1961 	if (request == RTLD_DI_MMAPS) {
1962 		Dl_mapinfo_t	*mip = (Dl_mapinfo_t *)p;
1963 
1964 		if (mip->dlm_acnt && mip->dlm_maps) {
1965 			uint_t	cnt = 0;
1966 
1967 			while ((cnt < mip->dlm_acnt) && (cnt < MMAPCNT(lmp))) {
1968 				mip->dlm_maps[cnt] = MMAPS(lmp)[cnt];
1969 				cnt++;
1970 			}
1971 			mip->dlm_rcnt = cnt;
1972 		}
1973 		return (0);
1974 	}
1975 
1976 	return (0);
1977 }
1978 
1979 #pragma weak _dlinfo = dlinfo
1980 
1981 /*
1982  * External entry for dlinfo(3dl).
1983  */
1984 int
1985 dlinfo(void *handle, int request, void *p)
1986 {
1987 	int	error, entry;
1988 	Rt_map	*clmp;
1989 
1990 	entry = enter(0);
1991 
1992 	clmp = _caller(caller(), CL_EXECDEF);
1993 
1994 	error = dlinfo_core(handle, request, p, clmp);
1995 
1996 	if (entry)
1997 		leave(LIST(clmp), 0);
1998 	return (error);
1999 }
2000