xref: /illumos-gate/usr/src/cmd/sgs/elfdump/common/main.c (revision 5016ae894be01e501342a67035ea848043662a45)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2024 Oxide Computer Company
25  */
26 
27 /*
28  * Dump an elf file.
29  */
30 #include	<sys/param.h>
31 #include	<fcntl.h>
32 #include	<stdio.h>
33 #include	<stdlib.h>
34 #include	<ctype.h>
35 #include	<_libelf.h>
36 #include	<link.h>
37 #include	<stdarg.h>
38 #include	<unistd.h>
39 #include	<libgen.h>
40 #include	<libintl.h>
41 #include	<locale.h>
42 #include	<errno.h>
43 #include	<strings.h>
44 #include	<debug.h>
45 #include	<conv.h>
46 #include	<msg.h>
47 #include	<_elfdump.h>
48 #include	<sys/elf_SPARC.h>
49 #include	<sys/elf_amd64.h>
50 #include	<sys/hexdump.h>
51 
52 
53 const Cache	cache_init = {NULL, NULL, NULL, NULL, 0};
54 
55 
56 
57 /*
58  * The -I, -N, and -T options are called "match options", because
59  * they allow selecting the items to be displayed based on matching
60  * their index, name, or type.
61  *
62  * The ELF information to which -I, -N, or -T are applied in
63  * the current invocation is called the "match item".
64  */
65 typedef enum {
66 	MATCH_ITEM_PT,		/* Program header (PT_) */
67 	MATCH_ITEM_SHT		/* Section header (SHT_) */
68 } match_item_t;
69 
70 /* match_opt_t is  used to note which match option was used */
71 typedef enum {
72 	MATCH_OPT_NAME,		/* Record contains a name */
73 	MATCH_OPT_NDX,		/* Record contains a single index */
74 	MATCH_OPT_RANGE,	/* Record contains an index range */
75 	MATCH_OPT_TYPE,		/* Record contains a type (shdr or phdr) */
76 } match_opt_t;
77 
78 typedef struct _match {
79 	struct _match	*next;		/* Pointer to next item in list */
80 	match_opt_t	opt_type;
81 	union {
82 		const char	*name;	/* MATCH_OPT_NAME */
83 		struct {		/* MATCH_OPT_NDX and MATCH_OPT_RANGE */
84 			int	start;
85 			int	end;	/* Only for MATCH_OPT_RANGE */
86 		} ndx;
87 		uint32_t	type;	/* MATCH_OPT_TYPE */
88 	} value;
89 } match_rec_t;
90 
91 static struct {
92 	match_item_t	item_type;	/* Type of item being matched */
93 	match_rec_t	*list;		/* Records for (-I, -N, -T) options */
94 } match_state;
95 
96 
97 
98 const char *
99 _elfdump_msg(Msg mid)
100 {
101 	return (gettext(MSG_ORIG(mid)));
102 }
103 
104 /*
105  * Determine whether a symbol name should be demangled.
106  */
107 const char *
108 demangle(const char *name, uint_t flags)
109 {
110 	if (flags & FLG_CTL_DEMANGLE)
111 		return (Elf_demangle_name(name));
112 	else
113 		return ((char *)name);
114 }
115 
116 /*
117  * Define our own standard error routine.
118  */
119 void
120 failure(const char *file, const char *func)
121 {
122 	(void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE),
123 	    file, func, elf_errmsg(elf_errno()));
124 }
125 
126 /*
127  * The full usage message
128  */
129 static void
130 detail_usage()
131 {
132 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1));
133 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2));
134 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3));
135 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4));
136 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5));
137 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6));
138 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7));
139 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8));
140 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9));
141 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10));
142 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11));
143 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12));
144 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13));
145 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14));
146 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15));
147 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16));
148 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17));
149 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18));
150 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19));
151 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20));
152 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21));
153 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22));
154 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23));
155 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL24));
156 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL25));
157 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL26));
158 }
159 
160 /*
161  * Output a block of raw data as hex bytes. Each row is given
162  * the index of the first byte in the row.
163  *
164  * entry:
165  *	data - Pointer to first byte of data to be displayed
166  *	n - # of bytes of data
167  *	bytes_per_col - # of space separated bytes to output in each column.
168  *	col_per_row - # of columns to output per row
169  *
170  * exit:
171  *	The formatted data has been sent to stdout.
172  */
173 typedef struct {
174 	uint_t	dd_indent;
175 } dump_data_t;
176 
177 static int
178 dump_hex_bytes_cb(void *arg, uint64_t addr, const char *str,
179     size_t len __unused)
180 {
181 	char index[MAXNDXSIZE];
182 	dump_data_t *dd = arg;
183 	size_t index_width;
184 
185 	(void) snprintf(index, sizeof (index), MSG_ORIG(MSG_FMT_INDEX2),
186 	    EC_WORD(addr));
187 	index_width = strlen(index);
188 	index_width = S_ROUND(index_width, 8);
189 	dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW),
190 	    dd->dd_indent, MSG_ORIG(MSG_STR_EMPTY),
191 	    index_width, index, str);
192 	return (0);
193 }
194 
195 void
196 dump_hex_bytes(const void *data, size_t n, int indent, int bytes_per_col,
197     int col_per_row)
198 {
199 	hexdump_t h;
200 	dump_data_t dd = {
201 		.dd_indent = indent
202 	};
203 	hexdump_init(&h);
204 	hexdump_set_grouping(&h, bytes_per_col);
205 	hexdump_set_width(&h, bytes_per_col * col_per_row);
206 	(void) hexdumph(&h, data, n, HDF_DOUBLESPACE, dump_hex_bytes_cb, &dd);
207 	hexdump_fini(&h);
208 }
209 
210 /*
211  * Convert the ASCII representation of an index, or index range, into
212  * binary form, and store it in rec:
213  *
214  *	index: An positive or 0 valued integer
215  *	range: Two indexes, separated by a ':' character, denoting
216  *		a range of allowed values. If the second value is omitted,
217  *		any values equal to or greater than the first will match.
218  *
219  * exit:
220  *	On success, *rec is filled in with a MATCH_OPT_NDX or MATCH_OPT_RANGE
221  *	value, and this function returns (1). On failure, the contents
222  *	of *rec are undefined, and (0) is returned.
223  */
224 int
225 process_index_opt(const char *str, match_rec_t *rec)
226 {
227 #define	SKIP_BLANK for (; *str && isspace(*str); str++)
228 
229 	char	*endptr;
230 
231 	rec->value.ndx.start = strtol(str, &endptr, 10);
232 	/* Value must use some of the input, and be 0 or positive */
233 	if ((str == endptr) || (rec->value.ndx.start < 0))
234 		return (0);
235 	str = endptr;
236 
237 	SKIP_BLANK;
238 	if (*str != ':') {
239 		rec->opt_type = MATCH_OPT_NDX;
240 	} else {
241 		str++;					/* Skip the ':' */
242 		rec->opt_type = MATCH_OPT_RANGE;
243 		SKIP_BLANK;
244 		if (*str == '\0') {
245 			rec->value.ndx.end = -1;	/* Indicates "to end" */
246 		} else {
247 			rec->value.ndx.end = strtol(str, &endptr, 10);
248 			if ((str == endptr) || (rec->value.ndx.end < 0))
249 				return (0);
250 			str = endptr;
251 			SKIP_BLANK;
252 		}
253 	}
254 
255 	/* Syntax error if anything is left over */
256 	if (*str != '\0')
257 		return (0);
258 
259 	return (1);
260 
261 #undef	SKIP_BLANK
262 }
263 
264 /*
265  * Convert a string containing a specific type of ELF constant, or an ASCII
266  * representation of a number, to an integer. Strings starting with '0'
267  * are taken to be octal, those staring with '0x' are hex, and all
268  * others are decimal.
269  *
270  * entry:
271  *	str - String to be converted
272  *	ctype - Constant type
273  *	v - Address of variable to receive resulting value.
274  *
275  * exit:
276  *	On success, returns True (1) and *v is set to the value.
277  *	On failure, returns False (0) and *v is undefined.
278  */
279 typedef enum {
280 	ATOUI_PT,
281 	ATOUI_SHT,
282 	ATOUI_OSABI
283 } atoui_type_t;
284 
285 static int
286 atoui(const char *str, atoui_type_t type, uint32_t *v)
287 {
288 	conv_strtol_uvalue_t	uvalue;
289 	char			*endptr;
290 
291 	if (conv_iter_strtol_init(str, &uvalue) != 0) {
292 		switch (type) {
293 		case ATOUI_PT:
294 			if (conv_iter_phdr_type(CONV_OSABI_ALL, CONV_FMT_ALT_CF,
295 			    conv_iter_strtol, &uvalue) == CONV_ITER_DONE)
296 				break;
297 			(void) conv_iter_phdr_type(CONV_OSABI_ALL,
298 			    CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue);
299 			break;
300 		case ATOUI_SHT:
301 			if (conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL,
302 			    CONV_FMT_ALT_CF, conv_iter_strtol, &uvalue) ==
303 			    CONV_ITER_DONE)
304 				break;
305 			(void) conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL,
306 			    CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue);
307 			break;
308 		case ATOUI_OSABI:
309 			if (conv_iter_ehdr_osabi(CONV_FMT_ALT_CF,
310 			    conv_iter_strtol, &uvalue) == CONV_ITER_DONE)
311 				break;
312 			(void) conv_iter_ehdr_osabi(CONV_FMT_ALT_NF,
313 			    conv_iter_strtol, &uvalue);
314 			break;
315 		}
316 		if (uvalue.csl_found) {
317 			*v = uvalue.csl_value;
318 			return (1);
319 		}
320 	}
321 
322 	*v = strtoull(str, &endptr, 0);
323 
324 	/* If the left over part contains anything but whitespace, fail */
325 	for (; *endptr; endptr++)
326 		if (!isspace(*endptr))
327 			return (0);
328 	return (1);
329 }
330 
331 /*
332  * Called after getopt() processing is finished if there is a non-empty
333  * match list. Prepares the matching code for use.
334  *
335  * exit:
336  *	Returns True (1) if no errors are encountered. Writes an
337  *	error string to stderr and returns False (0) otherwise.
338  */
339 static int
340 match_prepare(char *argv0, uint_t flags)
341 {
342 	match_rec_t	*list;
343 	const char	*str;
344 	int		minus_p = (flags & FLG_SHOW_PHDR) != 0;
345 	atoui_type_t	atoui_type;
346 
347 	/*
348 	 * Flag ambiguous attempt to use match option with both -p and
349 	 * and one or more section SHOW options. In this case, we
350 	 * can't tell what type of item we're supposed to match against.
351 	 */
352 	if (minus_p && (flags & FLG_MASK_SHOW_SHDR)) {
353 		(void) fprintf(stderr, MSG_INTL(MSG_ERR_AMBIG_MATCH),
354 		    basename(argv0));
355 		return (0);
356 	}
357 
358 	/* Set the match type, based on the presence of the -p option */
359 	if (minus_p) {
360 		match_state.item_type = MATCH_ITEM_PT;
361 		atoui_type = ATOUI_PT;
362 	} else {
363 		match_state.item_type = MATCH_ITEM_SHT;
364 		atoui_type = ATOUI_SHT;
365 	}
366 
367 	/*
368 	 * Scan match list and perform any necessary fixups:
369 	 *
370 	 * MATCH_OPT_NAME: If -p is specified, convert MATCH_OPT_NAME (-N)
371 	 *	requests into MATCH_OPT_TYPE (-T).
372 	 *
373 	 * MATCH_OPT_TYPE: Now that we know item type we are matching
374 	 *	against, we can convert the string saved in the name
375 	 *	field during getopt() processing into an integer and
376 	 *	write it into the type field.
377 	 */
378 	for (list = match_state.list; list; list = list->next) {
379 		if ((list->opt_type == MATCH_OPT_NAME) && minus_p)
380 			list->opt_type = MATCH_OPT_TYPE;
381 
382 		if (list->opt_type != MATCH_OPT_TYPE)
383 			continue;
384 
385 		str = list->value.name;
386 		if (atoui(str, atoui_type, &list->value.type) == 0) {
387 			const char *fmt = minus_p ?
388 			    MSG_INTL(MSG_ERR_BAD_T_PT) :
389 			    MSG_INTL(MSG_ERR_BAD_T_SHT);
390 
391 			(void) fprintf(stderr, fmt, basename(argv0), str);
392 			return (0);
393 		}
394 	}
395 
396 	return (1);
397 }
398 
399 
400 /*
401  * Returns True (1) if the item with the given name or index should
402  * be displayed, and False (0) if it should not be.
403  *
404  * entry:
405  *	match_flags - Bitmask specifying matching options, as described
406  *		in _elfdump.h.
407  *	name - If MATCH_F_NAME flag is set, name of item under
408  *		consideration. Otherwise ignored.
409  *		should not be considered.
410  *	ndx - If MATCH_F_NDX flag is set, index of item under consideration.
411  *	type - If MATCH_F_TYPE is set, type of item under consideration.
412  *		If MATCH_F_PHDR is set, this would be a program
413  *		header type (PT_). Otherwise, a section header type (SHT_).
414  *
415  * exit:
416  *	True will be returned if the given name/index matches those given
417  *	by one of the (-I, -N -T) command line options, or if no such option
418  *	was used in the command invocation and MATCH_F_STRICT is not
419  *	set.
420  */
421 int
422 match(match_flags_t match_flags, const char *name, uint_t ndx, uint_t type)
423 {
424 	match_item_t item_type = (match_flags & MATCH_F_PHDR) ?
425 	    MATCH_ITEM_PT  : MATCH_ITEM_SHT;
426 	match_rec_t *list;
427 
428 	/*
429 	 * If there is no match list, then we use the MATCH_F_STRICT
430 	 * flag to decide what to return. In the strict case, we return
431 	 * False (0), in the normal case, True (1).
432 	 */
433 	if (match_state.list == NULL)
434 		return ((match_flags & MATCH_F_STRICT) == 0);
435 
436 	/*
437 	 * If item being checked is not the current match type,
438 	 * then allow it.
439 	 */
440 	if (item_type != match_state.item_type)
441 		return (1);
442 
443 	/* Run through the match records and check for a hit */
444 	for (list = match_state.list; list; list = list->next) {
445 		switch (list->opt_type) {
446 		case MATCH_OPT_NAME:
447 			if (((match_flags & MATCH_F_NAME) == 0) ||
448 			    (name == NULL))
449 				break;
450 			if (strcmp(list->value.name, name) == 0)
451 				return (1);
452 			break;
453 		case MATCH_OPT_NDX:
454 			if ((match_flags & MATCH_F_NDX) &&
455 			    (ndx == list->value.ndx.start))
456 				return (1);
457 			break;
458 		case MATCH_OPT_RANGE:
459 			/*
460 			 * A range end value less than 0 means that any value
461 			 * above the start is acceptible.
462 			 */
463 			if ((match_flags & MATCH_F_NDX) &&
464 			    (ndx >= list->value.ndx.start) &&
465 			    ((list->value.ndx.end < 0) ||
466 			    (ndx <= list->value.ndx.end)))
467 				return (1);
468 			break;
469 
470 		case MATCH_OPT_TYPE:
471 			if ((match_flags & MATCH_F_TYPE) &&
472 			    (type == list->value.type))
473 				return (1);
474 			break;
475 		}
476 	}
477 
478 	/* Nothing matched */
479 	return (0);
480 }
481 
482 /*
483  * Add an entry to match_state.list for use by match(). This routine is for
484  * use during getopt() processing. It should not be called once
485  * match_prepare() has been called.
486  *
487  * Return True (1) for success. On failure, an error is written
488  * to stderr, and False (0) is returned.
489  */
490 static int
491 add_match_record(char *argv0, match_rec_t *data)
492 {
493 	match_rec_t	*rec;
494 	match_rec_t	*list;
495 
496 	if ((rec = malloc(sizeof (*rec))) == NULL) {
497 		int err = errno;
498 		(void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC),
499 		    basename(argv0), strerror(err));
500 		return (0);
501 	}
502 
503 	*rec = *data;
504 
505 	/* Insert at end of match_state.list */
506 	if (match_state.list == NULL) {
507 		match_state.list = rec;
508 	} else {
509 		for (list = match_state.list; list->next != NULL;
510 		    list = list->next)
511 			;
512 		list->next = rec;
513 	}
514 
515 	rec->next = NULL;
516 	return (1);
517 }
518 
519 static int
520 decide(const char *file, int fd, Elf *elf, uint_t flags,
521     const char *wname, int wfd, uchar_t osabi)
522 {
523 	int r;
524 
525 	if (gelf_getclass(elf) == ELFCLASS64)
526 		r = regular64(file, fd, elf, flags, wname, wfd, osabi);
527 	else
528 		r = regular32(file, fd, elf, flags, wname, wfd, osabi);
529 
530 	return (r);
531 }
532 
533 static int
534 archive(const char *file, int fd, Elf *elf, uint_t flags,
535     const char *wname, int wfd, uchar_t osabi)
536 {
537 	Elf_Cmd		cmd = ELF_C_READ;
538 	Elf_Arhdr	*arhdr;
539 	Elf		*_elf = NULL;
540 	size_t		ptr;
541 	Elf_Arsym	*arsym = NULL;
542 
543 	/*
544 	 * Determine if the archive symbol table itself is required.
545 	 */
546 	if ((flags & FLG_SHOW_SYMBOLS) &&
547 	    match(MATCH_F_NAME, MSG_ORIG(MSG_ELF_ARSYM), 0, 0)) {
548 		/*
549 		 * Get the archive symbol table.
550 		 */
551 		if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) {
552 			/*
553 			 * The arsym could be 0 even though there was no error.
554 			 * Print the error message only when there was
555 			 * real error from elf_getarsym().
556 			 */
557 			failure(file, MSG_ORIG(MSG_ELF_GETARSYM));
558 			return (0);
559 		}
560 	}
561 
562 	/*
563 	 * Print the archive symbol table only when the archive symbol
564 	 * table exists and it was requested to print.
565 	 */
566 	if (arsym) {
567 		size_t		cnt;
568 		char		index[MAXNDXSIZE];
569 		size_t		offset = 0, _offset = 0;
570 		const char	*fmt_arsym1, *fmt_arsym2;
571 
572 		/*
573 		 * Print out all the symbol entries. The format width used
574 		 * corresponds to whether the archive symbol table is 32
575 		 * or 64-bit. We see them via Elf_Arhdr as size_t values
576 		 * in either case with no information loss (see the comments
577 		 * in libelf/getarsym.c) so this is done simply to improve
578 		 * the user presentation.
579 		 */
580 		if (_elf_getarsymwordsize(elf) == 8) {
581 			dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB_64));
582 			dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS_64));
583 
584 			fmt_arsym1 = MSG_ORIG(MSG_FMT_ARSYM1_64);
585 			fmt_arsym2 = MSG_ORIG(MSG_FMT_ARSYM2_64);
586 		} else {
587 			dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB_32));
588 			dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS_32));
589 
590 			fmt_arsym1 = MSG_ORIG(MSG_FMT_ARSYM1_32);
591 			fmt_arsym2 = MSG_ORIG(MSG_FMT_ARSYM2_32);
592 		}
593 
594 		for (cnt = 0; cnt < ptr; cnt++, arsym++) {
595 			/*
596 			 * For each object obtain an elf descriptor so that we
597 			 * can establish the members name.  Note, we have had
598 			 * archives where the archive header has not been
599 			 * obtainable so be lenient with errors.
600 			 */
601 			if ((offset == 0) || ((arsym->as_off != 0) &&
602 			    (arsym->as_off != _offset))) {
603 
604 				if (_elf)
605 					(void) elf_end(_elf);
606 
607 				if (elf_rand(elf, arsym->as_off) !=
608 				    arsym->as_off) {
609 					failure(file, MSG_ORIG(MSG_ELF_RAND));
610 					arhdr = NULL;
611 				} else if ((_elf = elf_begin(fd,
612 				    ELF_C_READ, elf)) == 0) {
613 					failure(file, MSG_ORIG(MSG_ELF_BEGIN));
614 					arhdr = NULL;
615 				} else if ((arhdr = elf_getarhdr(_elf)) == 0) {
616 					failure(file,
617 					    MSG_ORIG(MSG_ELF_GETARHDR));
618 					arhdr = NULL;
619 				}
620 
621 				_offset = arsym->as_off;
622 				if (offset == 0)
623 					offset = _offset;
624 			}
625 
626 			(void) snprintf(index, MAXNDXSIZE,
627 			    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt));
628 			if (arsym->as_off)
629 				dbg_print(0, fmt_arsym1, index,
630 				    EC_XWORD(arsym->as_off),
631 				    arhdr ? arhdr->ar_name :
632 				    MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ?
633 				    demangle(arsym->as_name, flags) :
634 				    MSG_INTL(MSG_STR_NULL)));
635 			else
636 				dbg_print(0, fmt_arsym2, index,
637 				    EC_XWORD(arsym->as_off));
638 		}
639 
640 		if (_elf)
641 			(void) elf_end(_elf);
642 
643 		/*
644 		 * If we only need the archive symbol table return.
645 		 */
646 		if ((flags & FLG_SHOW_SYMBOLS) &&
647 		    match(MATCH_F_STRICT | MATCH_F_NAME,
648 		    MSG_ORIG(MSG_ELF_ARSYM), -1, -1))
649 			return (0);
650 
651 		/*
652 		 * Reset elf descriptor in preparation for processing each
653 		 * member.
654 		 */
655 		if (offset)
656 			(void) elf_rand(elf, offset);
657 	}
658 
659 	/*
660 	 * Process each object within the archive.
661 	 */
662 	while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
663 		char	name[MAXPATHLEN];
664 
665 		if ((arhdr = elf_getarhdr(_elf)) == NULL) {
666 			failure(file, MSG_ORIG(MSG_ELF_GETARHDR));
667 			return (0);
668 		}
669 		if (*arhdr->ar_name != '/') {
670 			(void) snprintf(name, MAXPATHLEN,
671 			    MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name);
672 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name);
673 
674 			switch (elf_kind(_elf)) {
675 			case ELF_K_AR:
676 				if (archive(name, fd, _elf, flags,
677 				    wname, wfd, osabi) == 1)
678 					return (1);
679 				break;
680 			case ELF_K_ELF:
681 				if (decide(name, fd, _elf, flags,
682 				    wname, wfd, osabi) == 1)
683 					return (1);
684 				break;
685 			default:
686 				(void) fprintf(stderr,
687 				    MSG_INTL(MSG_ERR_BADFILE), name);
688 				break;
689 			}
690 		}
691 
692 		cmd = elf_next(_elf);
693 		(void) elf_end(_elf);
694 	}
695 
696 	return (0);
697 }
698 
699 int
700 main(int argc, char **argv, char **envp)
701 {
702 	Elf		*elf;
703 	int		var, fd, wfd = 0;
704 	char		*wname = NULL;
705 	uint_t		flags = 0;
706 	match_rec_t	match_data;
707 	int		ret;
708 	uchar_t		osabi = ELFOSABI_NONE;
709 
710 	/*
711 	 * Establish locale.
712 	 */
713 	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
714 	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
715 
716 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
717 	(void) setvbuf(stderr, NULL, _IOLBF, 0);
718 
719 	opterr = 0;
720 	while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
721 		switch (var) {
722 		case 'C':
723 			flags |= FLG_CTL_DEMANGLE;
724 			break;
725 		case 'c':
726 			flags |= FLG_SHOW_SHDR;
727 			break;
728 		case 'd':
729 			flags |= FLG_SHOW_DYNAMIC;
730 			break;
731 		case 'e':
732 			flags |= FLG_SHOW_EHDR;
733 			break;
734 		case 'G':
735 			flags |= FLG_SHOW_GOT;
736 			break;
737 		case 'g':
738 			flags |= FLG_SHOW_GROUP;
739 			break;
740 		case 'H':
741 			flags |= FLG_SHOW_CAP;
742 			break;
743 		case 'h':
744 			flags |= FLG_SHOW_HASH;
745 			break;
746 		case 'I':
747 			if (!process_index_opt(optarg, &match_data))
748 				goto usage_brief;
749 			if (!add_match_record(argv[0], &match_data))
750 				return (1);
751 			flags |= FLG_CTL_MATCH;
752 			break;
753 		case 'i':
754 			flags |= FLG_SHOW_INTERP;
755 			break;
756 		case 'k':
757 			flags |= FLG_CALC_CHECKSUM;
758 			break;
759 		case 'l':
760 			flags |= FLG_CTL_LONGNAME;
761 			break;
762 		case 'm':
763 			flags |= FLG_SHOW_MOVE;
764 			break;
765 		case 'N':
766 			match_data.opt_type = MATCH_OPT_NAME;
767 			match_data.value.name = optarg;
768 			if (!add_match_record(argv[0], &match_data))
769 				return (1);
770 			flags |= FLG_CTL_MATCH;
771 			break;
772 		case 'n':
773 			flags |= FLG_SHOW_NOTE;
774 			break;
775 		case 'O':
776 			{
777 				uint32_t val;
778 
779 				/*
780 				 * osabi is a uchar_t in the ELF header.
781 				 * Don't accept any value that exceeds
782 				 * that range.
783 				 */
784 				if ((atoui(optarg, ATOUI_OSABI, &val) == 0) ||
785 				    (val > 255)) {
786 					(void) fprintf(stderr,
787 					    MSG_INTL(MSG_ERR_BAD_T_OSABI),
788 					    basename(argv[0]), optarg);
789 					return (1);
790 				}
791 				osabi = val;
792 			}
793 			flags |= FLG_CTL_OSABI;
794 			break;
795 		case 'P':
796 			flags |= FLG_CTL_FAKESHDR;
797 			break;
798 		case 'p':
799 			flags |= FLG_SHOW_PHDR;
800 			break;
801 		case 'r':
802 			flags |= FLG_SHOW_RELOC;
803 			break;
804 		case 'S':
805 			flags |= FLG_SHOW_SORT;
806 			break;
807 		case 's':
808 			flags |= FLG_SHOW_SYMBOLS;
809 			break;
810 		case 'T':
811 			/*
812 			 * We can't evaluate the value yet, because
813 			 * we need to know if -p is used or not in
814 			 * order to tell if we're seeing section header
815 			 * or program header types. So, we save the
816 			 * string in the name field, and then convert
817 			 * it to a type integer in a following pass.
818 			 */
819 			match_data.opt_type = MATCH_OPT_TYPE;
820 			match_data.value.name = optarg;
821 			if (!add_match_record(argv[0], &match_data))
822 				return (1);
823 			flags |= FLG_CTL_MATCH;
824 			break;
825 		case 'u':
826 			flags |= FLG_SHOW_UNWIND;
827 			break;
828 		case 'v':
829 			flags |= FLG_SHOW_VERSIONS;
830 			break;
831 		case 'w':
832 			wname = optarg;
833 			break;
834 		case 'y':
835 			flags |= FLG_SHOW_SYMINFO;
836 			break;
837 		case '?':
838 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
839 			    basename(argv[0]));
840 			detail_usage();
841 			return (1);
842 		default:
843 			break;
844 		}
845 	}
846 
847 	/* -p and -w are mutually exclusive. -w only works with sections */
848 	if (((flags & FLG_SHOW_PHDR) != 0) && (wname != NULL))
849 		goto usage_brief;
850 
851 	/* If a match argument is present, prepare the match state */
852 	if ((match_state.list != NULL) && (match_prepare(argv[0], flags) == 0))
853 		return (1);
854 
855 	/*
856 	 * Decide what to do if no options specifying something to
857 	 * show or do are present.
858 	 *
859 	 * If there is no -w and no match options, then we will set all
860 	 * the show flags, causing a full display of everything in the
861 	 * file that we know how to handle.
862 	 *
863 	 * Otherwise, if there is no match list, we generate a usage
864 	 * error and quit.
865 	 *
866 	 * In the case where there is a match list, we go ahead and call
867 	 * regular() anyway, leaving it to decide what to do. If -w is
868 	 * present, regular() will use the match list to handle it.
869 	 * In addition, in the absence of explicit show/calc flags, regular()
870 	 * will compare the section headers to the match list and use
871 	 * that to generate the FLG_ bits that will display the information
872 	 * specified by the match list.
873 	 */
874 	if ((flags & ~FLG_MASK_CTL) == 0) {
875 		if (!wname && (match_state.list == NULL))
876 			flags |= FLG_MASK_SHOW;
877 		else if (match_state.list == NULL)
878 			goto usage_brief;
879 	}
880 
881 	/* There needs to be at least 1 filename left following the options */
882 	if ((var = argc - optind) == 0)
883 		goto usage_brief;
884 
885 	/*
886 	 * If the -l/-C option is specified, set up the liblddbg.so.
887 	 */
888 	if (flags & FLG_CTL_LONGNAME)
889 		dbg_desc->d_extra |= DBG_E_LONG;
890 	if (flags & FLG_CTL_DEMANGLE)
891 		dbg_desc->d_extra |= DBG_E_DEMANGLE;
892 
893 	/*
894 	 * If the -w option has indicated an output file open it.  It's
895 	 * arguable whether this option has much use when multiple files are
896 	 * being processed.
897 	 *
898 	 * If wname is non-NULL, we know that -p was not specified, due
899 	 * to the test above.
900 	 */
901 	if (wname) {
902 		if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC),
903 		    0666)) < 0) {
904 			int err = errno;
905 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
906 			    wname, strerror(err));
907 			return (1);
908 		}
909 	}
910 
911 	/*
912 	 * Open the input file, initialize the elf interface, and
913 	 * process it.
914 	 */
915 	ret = 0;
916 	for (; (optind < argc) && (ret == 0); optind++) {
917 		const char	*file = argv[optind];
918 
919 		if ((fd = open(argv[optind], O_RDONLY)) == -1) {
920 			int err = errno;
921 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
922 			    file, strerror(err));
923 			continue;
924 		}
925 		(void) elf_version(EV_CURRENT);
926 		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
927 			failure(file, MSG_ORIG(MSG_ELF_BEGIN));
928 			(void) close(fd);
929 			continue;
930 		}
931 
932 		if (var > 1)
933 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file);
934 
935 		switch (elf_kind(elf)) {
936 		case ELF_K_AR:
937 			ret = archive(file, fd, elf, flags, wname, wfd, osabi);
938 			break;
939 		case ELF_K_ELF:
940 			ret = decide(file, fd, elf, flags, wname, wfd, osabi);
941 			break;
942 		default:
943 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file);
944 			break;
945 		}
946 
947 		(void) close(fd);
948 		(void) elf_end(elf);
949 	}
950 
951 	if (wfd)
952 		(void) close(wfd);
953 	return (ret);
954 
955 usage_brief:
956 	/* Control comes here for a simple usage message and exit */
957 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
958 	    basename(argv[0]));
959 	return (1);
960 
961 }
962