xref: /titanic_41/usr/src/cmd/sgs/libld/common/util.c (revision 9c9af2590af49bb395bc8d2eace0f2d4ea16d165)
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 2008 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 == 0) ||
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)) != 0)
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  * Append an item to the specified list, and return a pointer to the list
188  * node created.
189  */
190 Listnode *
191 list_appendc(List *lst, const void *item)
192 {
193 	Listnode	*_lnp;
194 
195 	if ((_lnp = libld_malloc(sizeof (Listnode))) == NULL)
196 		return (NULL);
197 
198 	_lnp->data = (void *)item;
199 	_lnp->next = NULL;
200 
201 	if (lst->head == NULL)
202 		lst->tail = lst->head = _lnp;
203 	else {
204 		lst->tail->next = _lnp;
205 		lst->tail = lst->tail->next;
206 	}
207 	return (_lnp);
208 }
209 
210 /*
211  * Add an item after the specified listnode, and return a pointer to the list
212  * node created.
213  */
214 Listnode *
215 list_insertc(List *lst, const void *item, Listnode *lnp)
216 {
217 	Listnode	*_lnp;
218 
219 	if ((_lnp = libld_malloc(sizeof (Listnode))) == NULL)
220 		return (NULL);
221 
222 	_lnp->data = (void *)item;
223 	_lnp->next = lnp->next;
224 	if (_lnp->next == NULL)
225 		lst->tail = _lnp;
226 	lnp->next = _lnp;
227 	return (_lnp);
228 }
229 
230 /*
231  * Prepend an item to the specified list, and return a pointer to the
232  * list node created.
233  */
234 Listnode *
235 list_prependc(List *lst, const void *item)
236 {
237 	Listnode	*_lnp;
238 
239 	if ((_lnp = libld_malloc(sizeof (Listnode))) == NULL)
240 		return (NULL);
241 
242 	_lnp->data = (void *)item;
243 
244 	if (lst->head == NULL) {
245 		_lnp->next = NULL;
246 		lst->tail = lst->head = _lnp;
247 	} else {
248 		_lnp->next = lst->head;
249 		lst->head = _lnp;
250 	}
251 	return (_lnp);
252 }
253 
254 /*
255  * Find out where to insert the node for reordering.  List of insect structures
256  * is traversed and the is_txtndx field of the insect structure is examined
257  * and that determines where the new input section should be inserted.
258  * All input sections which have a non zero is_txtndx value will be placed
259  * in ascending order before sections with zero is_txtndx value.  This
260  * implies that any section that does not appear in the map file will be
261  * placed at the end of this list as it will have a is_txtndx value of 0.
262  * Returns:  NULL if the input section should be inserted at beginning
263  * of list else A pointer to the entry AFTER which this new section should
264  * be inserted.
265  */
266 Listnode *
267 list_where(List *lst, Word num)
268 {
269 	Listnode	*ln, *pln;	/* Temp list node ptr */
270 	Is_desc		*isp;		/* Temp Insect structure */
271 	Word		n;
272 
273 	/*
274 	 * No input sections exist, so add at beginning of list
275 	 */
276 	if (lst->head == NULL)
277 		return (NULL);
278 
279 	for (ln = lst->head, pln = ln; ln != NULL; pln = ln, ln = ln->next) {
280 		isp = (Is_desc *)ln->data;
281 		/*
282 		 *  This should never happen, but if it should we
283 		 *  try to do the right thing.  Insert at the
284 		 *  beginning of list if no other items exist, else
285 		 *  end of already existing list, prior to this null
286 		 *  item.
287 		 */
288 		if (isp == NULL) {
289 			if (ln == pln) {
290 				return (NULL);
291 			} else {
292 				return (pln);
293 			}
294 		}
295 		/*
296 		 *  We have reached end of reorderable items.  All
297 		 *  following items have is_txtndx values of zero
298 		 *  So insert at end of reorderable items.
299 		 */
300 		if ((n = isp->is_txtndx) > num || n == 0) {
301 			if (ln == pln) {
302 				return (NULL);
303 			} else {
304 				return (pln);
305 			}
306 		}
307 		/*
308 		 *  We have reached end of list, so insert
309 		 *  at the end of this list.
310 		 */
311 		if ((n != 0) && (ln->next == NULL))
312 			return (ln);
313 	}
314 	return (NULL);
315 }
316 
317 /*
318  * Determine if a shared object definition structure already exists and if
319  * not create one.  These definitions provide for recording information
320  * regarding shared objects that are still to be processed.  Once processed
321  * shared objects are maintained on the ofl_sos list.  The information
322  * recorded in this structure includes:
323  *
324  *  o	DT_USED requirements.  In these cases definitions are added during
325  *	mapfile processing of `-' entries (see map_dash()).
326  *
327  *  o	implicit NEEDED entries.  As shared objects are processed from the
328  *	command line so any of their dependencies are recorded in these
329  *	structures for later processing (see process_dynamic()).
330  *
331  *  o	version requirements.  Any explicit shared objects that have version
332  *	dependencies on other objects have their version requirements recorded.
333  *	In these cases definitions are added during mapfile processing of `-'
334  *	entries (see map_dash()).  Also, shared objects may have versioning
335  *	requirements on their NEEDED entries.  These cases are added during
336  *	their version processing (see vers_need_process()).
337  *
338  *	Note: Both process_dynamic() and vers_need_process() may generate the
339  *	initial version definition structure because you can't rely on what
340  *	section (.dynamic or .SUNW_version) may be processed first from	any
341  *	input file.
342  */
343 Sdf_desc *
344 sdf_find(const char *name, List *lst)
345 {
346 	Listnode	*lnp;
347 	Sdf_desc	*sdf;
348 
349 	for (LIST_TRAVERSE(lst, lnp, sdf))
350 		if (strcmp(name, sdf->sdf_name) == 0)
351 			return (sdf);
352 
353 	return (NULL);
354 }
355 
356 Sdf_desc *
357 sdf_add(const char *name, List *lst)
358 {
359 	Sdf_desc	*sdf;
360 
361 	if (!(sdf = libld_calloc(sizeof (Sdf_desc), 1)))
362 		return ((Sdf_desc *)S_ERROR);
363 
364 	sdf->sdf_name = name;
365 
366 	if (list_appendc(lst, sdf) == 0)
367 		return ((Sdf_desc *)S_ERROR);
368 	else
369 		return (sdf);
370 }
371 
372 /*
373  * Add a string, separated by a colon, to an existing string.  Typically used
374  * to maintain filter, rpath and audit names, of which there is normally only
375  * one string supplied anyway.
376  */
377 char *
378 add_string(char *old, char *str)
379 {
380 	char	*new;
381 
382 	if (old) {
383 		char	*_str;
384 		size_t	len;
385 
386 		/*
387 		 * If an original string exists, make sure this new string
388 		 * doesn't get duplicated.
389 		 */
390 		if ((_str = strstr(old, str)) != NULL) {
391 			if (((_str == old) ||
392 			    (*(_str - 1) == *(MSG_ORIG(MSG_STR_COLON)))) &&
393 			    (_str += strlen(str)) &&
394 			    ((*_str == '\0') ||
395 			    (*_str == *(MSG_ORIG(MSG_STR_COLON)))))
396 				return (old);
397 		}
398 
399 		len = strlen(old) + strlen(str) + 2;
400 		if ((new = libld_calloc(1, len)) == NULL)
401 			return ((char *)S_ERROR);
402 		(void) snprintf(new, len, MSG_ORIG(MSG_FMT_COLPATH), old, str);
403 	} else {
404 		if ((new = libld_malloc(strlen(str) + 1)) == NULL)
405 			return ((char *)S_ERROR);
406 		(void) strcpy(new, str);
407 	}
408 
409 	return (new);
410 }
411 
412 /*
413  * Determine whether this string, possibly with an associated option, should be
414  * translated to an option character.  If so, update the optind and optarg
415  * as described for short options in getopt(3c).
416  */
417 static int
418 str2chr(Lm_list *lml, int ndx, int argc, char **argv, char *arg, int c,
419     const char *opt, size_t optsz)
420 {
421 	if (optsz == 0) {
422 		/*
423 		 * Compare a single option (ie. there's no associated option
424 		 * argument).
425 		 */
426 		if (strcmp(arg, opt) == 0) {
427 			DBG_CALL(Dbg_args_str2chr(lml, ndx, opt, c));
428 			optind += 1;
429 			return (c);
430 		}
431 
432 	} else if (strncmp(arg, opt, optsz) == 0) {
433 		/*
434 		 * Otherwise, compare the option name, which may be
435 		 * concatenated with the option argument.
436 		 */
437 		DBG_CALL(Dbg_args_str2chr(lml, ndx, opt, c));
438 
439 		if (arg[optsz] == '\0') {
440 			/*
441 			 * Optarg is the next argument (white space separated).
442 			 * Make sure an optarg is available, and if not return
443 			 * a failure to prevent any fall-through to the generic
444 			 * getopt() processing.
445 			 */
446 			if ((++optind + 1) > argc) {
447 				return ('?');
448 			}
449 			optarg = argv[optind];
450 			optind++;
451 		} else {
452 			/*
453 			 * Optarg concatenated to option (no white space).
454 			 * GNU option/option argument pairs can be represented
455 			 * with a "=" separator.  If this is the case, remove
456 			 * the separator.
457 			 */
458 			optarg = &arg[optsz];
459 			optind++;
460 			if (*optarg == '=') {
461 				if (*(++optarg) == '\0')
462 					return ('?');
463 			}
464 		}
465 		return (c);
466 	}
467 	return (0);
468 }
469 
470 /*
471  * Parse an individual option.  The intent of this function is to determine if
472  * any known, non-Solaris options have been passed to ld(1).  This condition
473  * can occur as a result of build configuration tools, because of users
474  * familiarity with other systems, or simply the users preferences.  If a known
475  * non-Solaris option can be determined, translate that option into the Solaris
476  * counterpart.
477  *
478  * This function will probably never be a complete solution, as new, non-Solaris
479  * options are discovered, their translation will have to be added.  Other
480  * non-Solaris options are incompatible with the Solaris link-editor, and will
481  * never be recognized.  We support what we can.
482  */
483 int
484 ld_getopt(Lm_list *lml, int ndx, int argc, char **argv)
485 {
486 	int	c;
487 
488 	if ((optind < argc) && argv[optind] && (argv[optind][0] == '-')) {
489 		char	*arg = &argv[optind][1];
490 
491 		switch (*arg) {
492 		case 'r':
493 			/* Translate -rpath <optarg> to -R <optarg> */
494 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'R',
495 			    MSG_ORIG(MSG_ARG_T_RPATH),
496 			    MSG_ARG_T_RPATH_SIZE)) != 0) {
497 				return (c);
498 			}
499 			break;
500 		case 's':
501 			/* Translate -shared to -G */
502 			if ((c = str2chr(lml, ndx, argc, argv, arg, 'G',
503 			    MSG_ORIG(MSG_ARG_T_SHARED), 0)) != 0) {
504 				return (c);
505 
506 			/* Translate -soname <optarg> to -h <optarg> */
507 			} else if ((c = str2chr(lml, ndx, argc, argv, arg, 'h',
508 			    MSG_ORIG(MSG_ARG_T_SONAME),
509 			    MSG_ARG_T_SONAME_SIZE)) != 0) {
510 				return (c);
511 			}
512 			break;
513 		case '(':
514 			/*
515 			 * Translate -( to -z rescan-start
516 			 */
517 			if ((c = str2chr(lml, ndx, argc, argv,
518 			    arg, 'z', MSG_ORIG(MSG_ARG_T_OPAR), 0)) != 0) {
519 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_START);
520 				return (c);
521 			}
522 			break;
523 		case ')':
524 			/*
525 			 * Translate -) to -z rescan-end
526 			 */
527 			if ((c = str2chr(lml, ndx, argc, argv,
528 			    arg, 'z', MSG_ORIG(MSG_ARG_T_CPAR), 0)) != 0) {
529 				optarg = (char *)MSG_ORIG(MSG_ARG_RESCAN_END);
530 				return (c);
531 			}
532 			break;
533 		case '-':
534 			switch (*(arg + 1)) {
535 			case 'a':
536 				/*
537 				 * Translate --allow-multiple-definition to
538 				 * -zmuldefs
539 				 */
540 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
541 				    MSG_ORIG(MSG_ARG_T_MULDEFS), 0)) != 0) {
542 					optarg =
543 					    (char *)MSG_ORIG(MSG_ARG_MULDEFS);
544 					return (c);
545 
546 				/*
547 				 * Translate --auxiliary <optarg> to
548 				 * -f <optarg>
549 				 */
550 				} else if ((c = str2chr(lml, argc, ndx, argv,
551 				    arg, 'f', MSG_ORIG(MSG_ARG_T_AUXFLTR),
552 				    MSG_ARG_T_AUXFLTR_SIZE)) != 0) {
553 					return (c);
554 				}
555 				break;
556 			case 'd':
557 				/*
558 				 * Translate --dynamic-linker <optarg> to
559 				 * -I <optarg>
560 				 */
561 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'I',
562 				    MSG_ORIG(MSG_ARG_T_INTERP),
563 				    MSG_ARG_T_INTERP_SIZE)) != 0) {
564 					return (c);
565 				}
566 				break;
567 			case 'e':
568 				/* Translate --entry <optarg> to -e <optarg> */
569 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'e',
570 				    MSG_ORIG(MSG_ARG_T_ENTRY),
571 				    MSG_ARG_T_ENTRY_SIZE)) != 0) {
572 					return (c);
573 				}
574 				/*
575 				 * Translate --end-group to -z rescan-end
576 				 */
577 				if ((c = str2chr(lml, ndx, argc, argv,
578 				    arg, 'z',
579 				    MSG_ORIG(MSG_ARG_T_ENDGROUP), 0)) != 0) {
580 					optarg = (char *)
581 					    MSG_ORIG(MSG_ARG_RESCAN_END);
582 					return (c);
583 				}
584 				break;
585 			case 'f':
586 				/* Translate --filter <optarg> to -F <optarg> */
587 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'F',
588 				    MSG_ORIG(MSG_ARG_T_STDFLTR),
589 				    MSG_ARG_T_STDFLTR_SIZE)) != 0) {
590 					return (c);
591 				}
592 				break;
593 			case 'h':
594 				/* Translate --help to -zhelp */
595 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
596 				    MSG_ORIG(MSG_ARG_T_HELP), 0)) != 0) {
597 					optarg = (char *)MSG_ORIG(MSG_ARG_HELP);
598 					return (c);
599 				}
600 				break;
601 			case 'l':
602 				/*
603 				 * Translate --library <optarg> to -l <optarg>
604 				 */
605 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'l',
606 				    MSG_ORIG(MSG_ARG_T_LIBRARY),
607 				    MSG_ARG_T_LIBRARY_SIZE)) != 0) {
608 					return (c);
609 
610 				/*
611 				 * Translate --library-path <optarg> to
612 				 * -L <optarg>
613 				 */
614 				} else if ((c = str2chr(lml, ndx, argc, argv,
615 				    arg, 'L', MSG_ORIG(MSG_ARG_T_LIBPATH),
616 				    MSG_ARG_T_LIBPATH_SIZE)) != 0) {
617 					return (c);
618 				}
619 				break;
620 			case 'n':
621 				/* Translate --no-undefined to -zdefs */
622 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'z',
623 				    MSG_ORIG(MSG_ARG_T_NOUNDEF), 0)) != 0) {
624 					optarg = (char *)MSG_ORIG(MSG_ARG_DEFS);
625 					return (c);
626 
627 				/*
628 				 * Translate --no-whole-archive to
629 				 * -z defaultextract
630 				 */
631 				} else if ((c = str2chr(lml, ndx, argc, argv,
632 				    arg, 'z',
633 				    MSG_ORIG(MSG_ARG_T_NOWHOLEARC), 0)) != 0) {
634 					optarg =
635 					    (char *)MSG_ORIG(MSG_ARG_DFLEXTRT);
636 					return (c);
637 				}
638 				break;
639 			case 'o':
640 				/* Translate --output <optarg> to -o <optarg> */
641 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'o',
642 				    MSG_ORIG(MSG_ARG_T_OUTPUT),
643 				    MSG_ARG_T_OUTPUT_SIZE)) != 0) {
644 					return (c);
645 				}
646 				break;
647 			case 'r':
648 				/* Translate --relocatable to -r */
649 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'r',
650 				    MSG_ORIG(MSG_ARG_T_RELOCATABLE), 0)) != 0) {
651 					return (c);
652 				}
653 				break;
654 			case 's':
655 				/* Translate --strip-all to -s */
656 				if ((c = str2chr(lml, ndx, argc, argv, arg, 's',
657 				    MSG_ORIG(MSG_ARG_T_STRIP), 0)) != 0) {
658 					return (c);
659 				}
660 				/*
661 				 * Translate --start-group to -z rescan-start
662 				 */
663 				if ((c = str2chr(lml, ndx, argc, argv,
664 				    arg, 'z',
665 				    MSG_ORIG(MSG_ARG_T_STARTGROUP), 0)) != 0) {
666 					optarg = (char *)
667 					    MSG_ORIG(MSG_ARG_RESCAN_START);
668 					return (c);
669 				}
670 				break;
671 			case 'u':
672 				/*
673 				 * Translate --undefined <optarg> to
674 				 * -u <optarg>
675 				 */
676 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'u',
677 				    MSG_ORIG(MSG_ARG_T_UNDEF),
678 				    MSG_ARG_T_UNDEF_SIZE)) != 0) {
679 					return (c);
680 				}
681 				break;
682 			case 'v':
683 				/* Translate --version to -V */
684 				if ((c = str2chr(lml, ndx, argc, argv, arg, 'V',
685 				    MSG_ORIG(MSG_ARG_T_VERSION), 0)) != 0) {
686 					return (c);
687 				}
688 				break;
689 			case 'w':
690 				/*
691 				 * Translate --whole-archive to -z alltextract
692 				 */
693 				if ((c = str2chr(lml, ndx, argc, argv,
694 				    arg, 'z',
695 				    MSG_ORIG(MSG_ARG_T_WHOLEARC), 0)) != 0) {
696 					optarg =
697 					    (char *)MSG_ORIG(MSG_ARG_ALLEXTRT);
698 					return (c);
699 				}
700 				break;
701 			}
702 			break;
703 		}
704 	}
705 	if ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) {
706 		/*
707 		 * It is possible that a "-Wl," argument has been used to
708 		 * specify an option.  This isn't advertized ld(1) syntax, but
709 		 * compiler drivers and configuration tools, have been known to
710 		 * pass this compiler option to ld(1).  Strip off the "-Wl,"
711 		 * prefix and pass the option through.
712 		 */
713 		if ((c == 'W') && (strncmp(optarg,
714 		    MSG_ORIG(MSG_ARG_T_WL), MSG_ARG_T_WL_SIZE) == 0)) {
715 			DBG_CALL(Dbg_args_Wldel(lml, ndx, optarg));
716 			c = optarg[MSG_ARG_T_WL_SIZE];
717 			optarg += MSG_ARG_T_WL_SIZE + 1;
718 		}
719 	}
720 
721 	return (c);
722 }
723 
724 /*
725  * Messaging support - funnel everything through dgettext().
726  */
727 
728 const char *
729 _libld_msg(Msg mid)
730 {
731 	return (dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid)));
732 }
733 
734 /*
735  * Determine whether a symbol name should be demangled.
736  */
737 const char *
738 demangle(const char *name)
739 {
740 	if (demangle_flag)
741 		return (Elf_demangle_name(name));
742 	else
743 		return (name);
744 }
745