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 *
_elfdump_msg(Msg mid)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 *
demangle(const char * name,uint_t flags)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
failure(const char * file,const char * func)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
detail_usage()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
dump_hex_bytes_cb(void * arg,uint64_t addr,const char * str,size_t len __unused)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
dump_hex_bytes(const void * data,size_t n,int indent,int bytes_per_col,int col_per_row)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
process_index_opt(const char * str,match_rec_t * rec)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
atoui(const char * str,atoui_type_t type,uint32_t * v)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
match_prepare(char * argv0,uint_t flags)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
match(match_flags_t match_flags,const char * name,uint_t ndx,uint_t type)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
add_match_record(char * argv0,match_rec_t * data)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
decide(const char * file,int fd,Elf * elf,uint_t flags,const char * wname,int wfd,uchar_t osabi)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
archive(const char * file,int fd,Elf * elf,uint_t flags,const char * wname,int wfd,uchar_t osabi)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
main(int argc,char ** argv,char ** envp)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