xref: /illumos-gate/usr/src/cmd/sgs/libld/common/util.c (revision 56f33205c9ed776c3c909e07d52e94610a675740)
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  * Utility functions
34  */
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/mman.h>
42 #include <errno.h>
43 #include <sgs.h>
44 #include <libintl.h>
45 #include <debug.h>
46 #include "msg.h"
47 #include "_libld.h"
48 
49 /*
50  * libld_malloc() and dz_map() are used for both performance and for ease of
51  * programming:
52  *
53  * Performance:
54  *	The link-edit is a short lived process which doesn't really free much
55  *	of the dynamic memory that it requests.  Because of this, it is more
56  *	important to optimize for quick memory allocations than the
57  *	re-usability of the memory.
58  *
59  *	By also mmaping blocks of pages in from /dev/zero we don't need to
60  *	waste the overhead of zeroing out these pages for calloc() requests.
61  *
62  * Memory Management:
63  *	By doing all libld memory management through the ld_malloc routine
64  *	it's much easier to free up all memory at the end by simply unmaping
65  *	all of the blocks that were mapped in through dz_map().  This is much
66  *	simpler then trying to track all of the libld structures that were
67  *	dynamically allocate and are actually pointers into the ELF files.
68  *
69  *	It's important that we can free up all of our dynamic memory because
70  *	libld is used by ld.so.1 when it performs dlopen()'s of relocatable
71  *	objects.
72  *
73  * Format:
74  *	The memory blocks for each allocation store the size of the allocation
75  *	in the first 8 bytes of the block.  The pointer that is returned by
76  *	libld_malloc() is actually the address of (block + 8):
77  *
78  *		(addr - 8)	block_size
79  *		(addr)		<allocated block>
80  *
81  *	The size is retained in order to implement realloc(), and to perform
82  *	the required memcpy().  8 bytes are uses, as the memory area returned
83  *	by libld_malloc() must be 8 byte-aligned.  Even in a 32-bit environment,
84  *	u_longlog_t pointers are employed.
85  *
86  * Map anonymous memory via MAP_ANON (added in Solaris 8).
87  */
88 static void *
89 dz_map(size_t size)
90 {
91 	void	*addr;
92 
93 	if ((addr = mmap(0, size, (PROT_READ | PROT_WRITE | PROT_EXEC),
94 	    (MAP_PRIVATE | MAP_ANON), -1, 0)) == MAP_FAILED) {
95 		int	err = errno;
96 		eprintf(0, ERR_FATAL, MSG_INTL(MSG_SYS_MMAPANON),
97 		    strerror(err));
98 		return (MAP_FAILED);
99 	}
100 	return (addr);
101 }
102 
103 void *
104 libld_malloc(size_t size)
105 {
106 	Ld_heap		*chp = ld_heap;
107 	void		*vptr;
108 	size_t		asize = size + HEAPALIGN;
109 
110 	/*
111 	 * If this is the first allocation, or the allocation request is greater
112 	 * than the current free space available, allocate a new heap.
113 	 */
114 	if ((chp == NULL) ||
115 	    (((size_t)chp->lh_end - (size_t)chp->lh_free) <= asize)) {
116 		Ld_heap	*nhp;
117 		size_t	hsize = (size_t)S_ROUND(sizeof (Ld_heap), HEAPALIGN);
118 		size_t	tsize = (size_t)S_ROUND((asize + hsize), HEAPALIGN);
119 
120 		/*
121 		 * Allocate a block that is at minimum 'HEAPBLOCK' size
122 		 */
123 		if (tsize < HEAPBLOCK)
124 			tsize = HEAPBLOCK;
125 
126 		if ((nhp = dz_map(tsize)) == MAP_FAILED)
127 			return (NULL);
128 
129 		nhp->lh_next = chp;
130 		nhp->lh_free = (void *)((size_t)nhp + hsize);
131 		nhp->lh_end = (void *)((size_t)nhp + tsize);
132 
133 		ld_heap = chp = nhp;
134 	}
135 	vptr = chp->lh_free;
136 
137 	/*
138 	 * Assign size to head of allocated block (used by realloc), and
139 	 * memory arena as then next 8-byte aligned offset.
140 	 */
141 	*((size_t *)vptr) = size;
142 	vptr = (void *)((size_t)vptr + HEAPALIGN);
143 
144 	/*
145 	 * Increment free to point to next available block
146 	 */
147 	chp->lh_free = (void *)S_ROUND((size_t)chp->lh_free + asize,
148 	    HEAPALIGN);
149 
150 	return (vptr);
151 }
152 
153 void *
154 libld_realloc(void *ptr, size_t size)
155 {
156 	size_t	psize;
157 	void	*vptr;
158 
159 	if (ptr == NULL)
160 		return (libld_malloc(size));
161 
162 	/*
163 	 * Size of the allocated blocks is stored *just* before the blocks
164 	 * address.
165 	 */
166 	psize = *((size_t *)((size_t)ptr - HEAPALIGN));
167 
168 	/*
169 	 * If the block actually fits then just return.
170 	 */
171 	if (size <= psize)
172 		return (ptr);
173 
174 	if ((vptr = libld_malloc(size)) != NULL)
175 		(void) memcpy(vptr, ptr, psize);
176 
177 	return (vptr);
178 }
179 
180 void
181 /* ARGSUSED 0 */
182 libld_free(void *ptr)
183 {
184 }
185 
186 /*
187  * Determine if a shared object definition structure already exists and if
188  * not create one.  These definitions provide for recording information
189  * regarding shared objects that are still to be processed.  Once processed
190  * shared objects are maintained on the ofl_sos list.  The information
191  * recorded in this structure includes:
192  *
193  *  o	DT_USED requirements.  In these cases definitions are added during
194  *	mapfile processing of `-' entries (see map_dash()).
195  *
196  *  o	implicit NEEDED entries.  As shared objects are processed from the
197  *	command line so any of their dependencies are recorded in these
198  *	structures for later processing (see process_dynamic()).
199  *
200  *  o	version requirements.  Any explicit shared objects that have version
201  *	dependencies on other objects have their version requirements recorded.
202  *	In these cases definitions are added during mapfile processing of `-'
203  *	entries (see map_dash()).  Also, shared objects may have versioning
204  *	requirements on their NEEDED entries.  These cases are added during
205  *	their version processing (see vers_need_process()).
206  *
207  *	Note: Both process_dynamic() and vers_need_process() may generate the
208  *	initial version definition structure because you can't rely on what
209  *	section (.dynamic or .SUNW_version) may be processed first from	any
210  *	input file.
211  */
212 Sdf_desc *
213 sdf_find(const char *name, APlist *alp)
214 {
215 	Aliste		idx;
216 	Sdf_desc	*sdf;
217 
218 	for (APLIST_TRAVERSE(alp, idx, sdf))
219 		if (strcmp(name, sdf->sdf_name) == 0)
220 			return (sdf);
221 
222 	return (NULL);
223 }
224 
225 Sdf_desc *
226 sdf_add(const char *name, APlist **alpp)
227 {
228 	Sdf_desc	*sdf;
229 
230 	if ((sdf = libld_calloc(sizeof (Sdf_desc), 1)) == NULL)
231 		return ((Sdf_desc *)S_ERROR);
232 
233 	sdf->sdf_name = name;
234 
235 	if (aplist_append(alpp, sdf, AL_CNT_OFL_LIBS) == NULL)
236 		return ((Sdf_desc *)S_ERROR);
237 
238 	return (sdf);
239 }
240 
241 /*
242  * Add a string, separated by a colon, to an existing string.  Typically used
243  * to maintain filter, rpath and audit names, of which there is normally only
244  * one string supplied anyway.
245  */
246 char *
247 add_string(char *old, char *str)
248 {
249 	char	*new;
250 
251 	if (old) {
252 		char	*_str;
253 		size_t	len;
254 
255 		/*
256 		 * If an original string exists, make sure this new string
257 		 * doesn't get duplicated.
258 		 */
259 		if ((_str = strstr(old, str)) != NULL) {
260 			if (((_str == old) ||
261 			    (*(_str - 1) == *(MSG_ORIG(MSG_STR_COLON)))) &&
262 			    (_str += strlen(str)) &&
263 			    ((*_str == '\0') ||
264 			    (*_str == *(MSG_ORIG(MSG_STR_COLON)))))
265 				return (old);
266 		}
267 
268 		len = strlen(old) + strlen(str) + 2;
269 		if ((new = libld_calloc(1, len)) == NULL)
270 			return ((char *)S_ERROR);
271 		(void) snprintf(new, len, MSG_ORIG(MSG_FMT_COLPATH), old, str);
272 	} else {
273 		if ((new = libld_malloc(strlen(str) + 1)) == NULL)
274 			return ((char *)S_ERROR);
275 		(void) strcpy(new, str);
276 	}
277 
278 	return (new);
279 }
280 
281 /*
282  * The GNU ld '-wrap=XXX' and '--wrap=XXX' options correspond to our
283  * '-z wrap=XXX'. When str2chr() does this conversion, we end up with
284  * the return character set to 'z' and optarg set to 'XXX'. This callback
285  * changes optarg to include the missing wrap= prefix.
286  *
287  * exit:
288  *	Returns c on success, or '?' on error.
289  */
290 static int
291 str2chr_wrap_cb(int c)
292 {
293 	char    *str;
294 	size_t  len = MSG_ARG_WRAP_SIZE + strlen(optarg) + 1;
295 
296 	if ((str = libld_malloc(len)) == NULL)
297 		return ('?');
298 	(void) snprintf(str, len, MSG_ORIG(MSG_FMT_STRCAT),
299 	    MSG_ORIG(MSG_ARG_WRAP), optarg);
300 	optarg = str;
301 	return (c);
302 }
303 
304 /*
305  * Determine whether this string, possibly with an associated option, should be
306  * translated to an option character.  If so, update the optind and optarg
307  * as described for short options in getopt(3c).
308  *
309  * entry:
310  *	lml - Link map list for debug messages
311  *	ndx - Starting optind for current item
312  *	argc, argv - Command line arguments
313  *	arg - Option to be examined
314  *	c, opt - Option character (c) and corresponding long name (opt)
315  *	optsz - 0 if option does not accept a value. If option does
316  *		accept a value, strlen(opt), giving the offset to the
317  *		value if the option and value are combined in one string.
318  *	cbfunc - NULL, or pointer to function to call if a translation is
319  *		successful.
320  */
321 static int
322 str2chr(Lm_list *lml, int ndx, int argc, char **argv, char *arg, int c,
323     const char *opt, size_t optsz, int cbfunc(int))
324 {
325 	if (optsz == 0) {
326 		/*
327 		 * Compare a single option (ie. there's no associated option
328 		 * argument).
329 		 */
330 		if (strcmp(arg, opt) == 0) {
331 			DBG_CALL(Dbg_args_str2chr(lml, ndx, opt, c));
332 			optind += 1;
333 			return (c);
334 		}
335 
336 	} else if (strncmp(arg, opt, optsz) == 0) {
337 		/*
338 		 * Otherwise, compare the option name, which may be
339 		 * concatenated with the option argument.
340 		 */
341 		DBG_CALL(Dbg_args_str2chr(lml, ndx, opt, c));
342 
343 		if (arg[optsz] == '\0') {
344 			/*
345 			 * Optarg is the next argument (white space separated).
346 			 * Make sure an optarg is available, and if not return
347 			 * a failure to prevent any fall-through to the generic
348 			 * getopt() processing.
349 			 */
350 			if ((++optind + 1) > argc) {
351 				return ('?');
352 			}
353 			optarg = argv[optind];
354 			optind++;
355 		} else {
356 			/*
357 			 * Optarg concatenated to option (no white space).
358 			 * GNU option/option argument pairs can be represented
359 			 * with a "=" separator.  If this is the case, remove
360 			 * the separator.
361 			 */
362 			optarg = &arg[optsz];
363 			optind++;
364 			if (*optarg == '=') {
365 				if (*(++optarg) == '\0')
366 					return ('?');
367 			}
368 		}
369 
370 		if (cbfunc != NULL)
371 			c = (*cbfunc)(c);
372 
373 		return (c);
374 	}
375 	return (0);
376 }
377 
378 /*
379  * Parse an individual option.  The intent of this function is to determine if
380  * any known, non-Solaris options have been passed to ld(1).  This condition
381  * can occur as a result of build configuration tools, because of users
382  * familiarity with other systems, or simply the users preferences.  If a known
383  * non-Solaris option can be determined, translate that option into the Solaris
384  * counterpart.
385  *
386  * This function will probably never be a complete solution, as new, non-Solaris
387  * options are discovered, their translation will have to be added.  Other
388  * non-Solaris options are incompatible with the Solaris link-editor, and will
389  * never be recognized.  We support what we can.
390  */
391 int
392 ld_getopt(Lm_list *lml, int ndx, int argc, char **argv)
393 {
394 	int	c;
395 
396 	if ((optind < argc) && argv[optind] && (argv[optind][0] == '-')) {
397 		char	*arg = &argv[optind][1];
398 
399 		switch (*arg) {
400 		case 'r':
401 			/* Translate -rpath <optarg> to -R <optarg> */
402 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'R',
403 			    MSG_ORIG(MSG_ARG_T_RPATH),
404 			    MSG_ARG_T_RPATH_SIZE, NULL)) != 0) {
405 				return (c);
406 			}
407 			break;
408 		case 's':
409 			/* Translate -shared to -G */
410 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'G',
411 			    MSG_ORIG(MSG_ARG_T_SHARED), 0, NULL)) != 0) {
412 				return (c);
413 
414 			/* Translate -soname <optarg> to -h <optarg> */
415 			} else if ((c = str2chr(lml, ndx, argc, argv, arg, 'h',
416 			    MSG_ORIG(MSG_ARG_T_SONAME),
417 			    MSG_ARG_T_SONAME_SIZE, NULL)) != 0) {
418 				return (c);
419 			}
420 			break;
421 		case 'w':
422 			/* Translate -wrap to -z wrap= */
423 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
424 			    MSG_ORIG(MSG_ARG_T_WRAP) + 1,
425 			    MSG_ARG_T_WRAP_SIZE - 1, str2chr_wrap_cb)) != 0) {
426 				return (c);
427 			}
428 			break;
429 		case '(':
430 			/*
431 			 * Translate -( to -z rescan-start
432 			 */
433 			if ((c = str2chr(lml, ndx, argc, argv,
434 			    arg, 'z', MSG_ORIG(MSG_ARG_T_OPAR), 0, NULL)) !=
435 			    0) {
436 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_START);
437 				return (c);
438 			}
439 			break;
440 		case ')':
441 			/*
442 			 * Translate -) to -z rescan-end
443 			 */
444 			if ((c = str2chr(lml, ndx, argc, argv,
445 			    arg, 'z', MSG_ORIG(MSG_ARG_T_CPAR), 0, NULL)) !=
446 			    0) {
447 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_END);
448 				return (c);
449 			}
450 			break;
451 		case '-':
452 			switch (*(arg + 1)) {
453 			case 'a':
454 				/*
455 				 * Translate --allow-multiple-definition to
456 				 * -zmuldefs
457 				 */
458 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
459 				    MSG_ORIG(MSG_ARG_T_MULDEFS), 0, NULL)) !=
460 				    0) {
461 					optarg =
462 					    (char *)MSG_ORIG(MSG_ARG_MULDEFS);
463 					return (c);
464 
465 				/*
466 				 * Translate --auxiliary <optarg> to
467 				 * -f <optarg>
468 				 */
469 				} else if ((c = str2chr(lml, argc, ndx, argv,
470 				    arg, 'f', MSG_ORIG(MSG_ARG_T_AUXFLTR),
471 				    MSG_ARG_T_AUXFLTR_SIZE, NULL)) != 0) {
472 					return (c);
473 				}
474 				break;
475 			case 'd':
476 				/*
477 				 * Translate --dynamic-linker <optarg> to
478 				 * -I <optarg>
479 				 */
480 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'I',
481 				    MSG_ORIG(MSG_ARG_T_INTERP),
482 				    MSG_ARG_T_INTERP_SIZE, NULL)) != 0) {
483 					return (c);
484 				}
485 				break;
486 			case 'e':
487 				/* Translate --entry <optarg> to -e <optarg> */
488 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'e',
489 				    MSG_ORIG(MSG_ARG_T_ENTRY),
490 				    MSG_ARG_T_ENTRY_SIZE, NULL)) != 0) {
491 					return (c);
492 				}
493 				/*
494 				 * Translate --end-group to -z rescan-end
495 				 */
496 				if ((c = str2chr(lml, ndx, argc, argv,
497 				    arg, 'z', MSG_ORIG(MSG_ARG_T_ENDGROUP),
498 				    0, NULL)) != 0) {
499 					optarg = (char *)
500 					    MSG_ORIG(MSG_ARG_RESCAN_END);
501 					return (c);
502 				}
503 				break;
504 			case 'f':
505 				/* Translate --filter <optarg> to -F <optarg> */
506 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'F',
507 				    MSG_ORIG(MSG_ARG_T_STDFLTR),
508 				    MSG_ARG_T_STDFLTR_SIZE, NULL)) != 0) {
509 					return (c);
510 				}
511 				break;
512 			case 'h':
513 				/* Translate --help to -zhelp */
514 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
515 				    MSG_ORIG(MSG_ARG_T_HELP), 0, NULL)) !=
516 				    0) {
517 					optarg = (char *)MSG_ORIG(MSG_ARG_HELP);
518 					return (c);
519 				}
520 				break;
521 			case 'l':
522 				/*
523 				 * Translate --library <optarg> to -l <optarg>
524 				 */
525 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'l',
526 				    MSG_ORIG(MSG_ARG_T_LIBRARY),
527 				    MSG_ARG_T_LIBRARY_SIZE, NULL)) != 0) {
528 					return (c);
529 
530 				/*
531 				 * Translate --library-path <optarg> to
532 				 * -L <optarg>
533 				 */
534 				} else if ((c = str2chr(lml, ndx, argc, argv,
535 				    arg, 'L', MSG_ORIG(MSG_ARG_T_LIBPATH),
536 				    MSG_ARG_T_LIBPATH_SIZE, NULL)) != 0) {
537 					return (c);
538 				}
539 				break;
540 			case 'n':
541 				/* Translate --no-undefined to -zdefs */
542 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
543 				    MSG_ORIG(MSG_ARG_T_NOUNDEF), 0, NULL)) !=
544 				    0) {
545 					optarg = (char *)MSG_ORIG(MSG_ARG_DEFS);
546 					return (c);
547 
548 				/*
549 				 * Translate --no-whole-archive to
550 				 * -z defaultextract
551 				 */
552 				} else if ((c = str2chr(lml, ndx, argc, argv,
553 				    arg, 'z', MSG_ORIG(MSG_ARG_T_NOWHOLEARC),
554 				    0, NULL)) != 0) {
555 					optarg =
556 					    (char *)MSG_ORIG(MSG_ARG_DFLEXTRT);
557 					return (c);
558 				}
559 				break;
560 			case 'o':
561 				/* Translate --output <optarg> to -o <optarg> */
562 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'o',
563 				    MSG_ORIG(MSG_ARG_T_OUTPUT),
564 				    MSG_ARG_T_OUTPUT_SIZE, NULL)) != 0) {
565 					return (c);
566 				}
567 				break;
568 			case 'r':
569 				/* Translate --relocatable to -r */
570 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'r',
571 				    MSG_ORIG(MSG_ARG_T_RELOCATABLE), 0,
572 				    NULL)) != 0) {
573 					return (c);
574 				}
575 				break;
576 			case 's':
577 				/* Translate --strip-all to -s */
578 				if ((c = str2chr(lml, ndx, argc, argv, arg, 's',
579 				    MSG_ORIG(MSG_ARG_T_STRIP), 0, NULL)) !=
580 				    0) {
581 					return (c);
582 				}
583 				/*
584 				 * Translate --start-group to -z rescan-start
585 				 */
586 				if ((c = str2chr(lml, ndx, argc, argv,
587 				    arg, 'z', MSG_ORIG(MSG_ARG_T_STARTGROUP),
588 				    0, NULL)) != 0) {
589 					optarg = (char *)
590 					    MSG_ORIG(MSG_ARG_RESCAN_START);
591 					return (c);
592 				}
593 				break;
594 			case 'u':
595 				/*
596 				 * Translate --undefined <optarg> to
597 				 * -u <optarg>
598 				 */
599 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'u',
600 				    MSG_ORIG(MSG_ARG_T_UNDEF),
601 				    MSG_ARG_T_UNDEF_SIZE, NULL)) != 0) {
602 					return (c);
603 				}
604 				break;
605 			case 'v':
606 				/* Translate --version to -V */
607 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'V',
608 				    MSG_ORIG(MSG_ARG_T_VERSION), 0, NULL)) !=
609 				    0) {
610 					return (c);
611 				}
612 				break;
613 			case 'w':
614 				/*
615 				 * Translate --whole-archive to -z alltextract
616 				 */
617 				if ((c = str2chr(lml, ndx, argc, argv,
618 				    arg, 'z', MSG_ORIG(MSG_ARG_T_WHOLEARC),
619 				    0, NULL)) != 0) {
620 					optarg =
621 					    (char *)MSG_ORIG(MSG_ARG_ALLEXTRT);
622 					return (c);
623 				}
624 				/*
625 				 * Translate --wrap to -z wrap=
626 				 */
627 				if ((c = str2chr(lml, ndx, argc, argv,
628 				    arg, 'z', MSG_ORIG(MSG_ARG_T_WRAP),
629 				    MSG_ARG_T_WRAP_SIZE, str2chr_wrap_cb)) !=
630 				    0) {
631 					return (c);
632 				}
633 				break;
634 			}
635 			break;
636 		}
637 	}
638 
639 	if ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) {
640 		/*
641 		 * It is possible that a "-Wl," argument has been used to
642 		 * specify an option.  This isn't advertized ld(1) syntax, but
643 		 * compiler drivers and configuration tools, have been known to
644 		 * pass this compiler option to ld(1).  Strip off the "-Wl,"
645 		 * prefix and pass the option through.
646 		 */
647 		if ((c == 'W') && (strncmp(optarg,
648 		    MSG_ORIG(MSG_ARG_T_WL), MSG_ARG_T_WL_SIZE) == 0)) {
649 			DBG_CALL(Dbg_args_Wldel(lml, ndx, optarg));
650 			c = optarg[MSG_ARG_T_WL_SIZE];
651 			optarg += MSG_ARG_T_WL_SIZE + 1;
652 		}
653 	}
654 
655 	return (c);
656 }
657 
658 /*
659  * A compare routine for Isd_node AVL trees.
660  */
661 int
662 isdavl_compare(const void *n1, const void *n2)
663 {
664 	uint_t		hash1, hash2;
665 	const char	*st1, *st2;
666 	int		rc;
667 
668 	hash1 = ((Isd_node *)n1)->isd_hash;
669 	hash2 = ((Isd_node *)n2)->isd_hash;
670 
671 	if (hash1 > hash2)
672 		return (1);
673 	if (hash1 < hash2)
674 		return (-1);
675 
676 	st1 = ((Isd_node *)n1)->isd_name;
677 	st2 = ((Isd_node *)n2)->isd_name;
678 
679 	rc = strcmp(st1, st2);
680 	if (rc > 0)
681 		return (1);
682 	if (rc < 0)
683 		return (-1);
684 	return (0);
685 }
686 
687 /*
688  * Messaging support - funnel everything through dgettext().
689  */
690 const char *
691 _libld_msg(Msg mid)
692 {
693 	return (dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
694 }
695 
696 /*
697  * Determine whether a symbol name should be demangled.
698  */
699 const char *
700 demangle(const char *name)
701 {
702 	if (demangle_flag)
703 		return (Elf_demangle_name(name));
704 	else
705 		return (name);
706 }
707