xref: /illumos-gate/usr/src/cmd/sgs/elfdump/common/main.c (revision c2aa8c918a0c67f7fd93724a31efac84968fc12c)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Dump an elf file.
30  */
31 #include	<sys/param.h>
32 #include	<fcntl.h>
33 #include	<stdio.h>
34 #include	<stdlib.h>
35 #include	<ctype.h>
36 #include	<libelf.h>
37 #include	<link.h>
38 #include	<stdarg.h>
39 #include	<unistd.h>
40 #include	<libgen.h>
41 #include	<libintl.h>
42 #include	<locale.h>
43 #include	<errno.h>
44 #include	<strings.h>
45 #include	<debug.h>
46 #include	<conv.h>
47 #include	<msg.h>
48 #include	<_elfdump.h>
49 
50 const Cache	cache_init = {NULL, NULL, NULL, NULL, 0};
51 
52 
53 
54 /* MATCH is  used to retain information about -N and -I options */
55 typedef enum {
56 	MATCH_T_NAME,		/* Record contains a name */
57 	MATCH_T_NDX,		/* Record contains a single index */
58 	MATCH_T_RANGE		/* Record contains an index range */
59 } MATCH_T;
60 
61 typedef struct _match {
62 	struct _match	*next;		/* Pointer to next item in list */
63 	MATCH_T		type;
64 	union {
65 		const char	*name;	/* MATCH_T_NAME */
66 		struct {		/* MATCH_T_NDX and MATCH_T_RANGE */
67 			int	start;
68 			int	end;	/* Only for MATCH_T_RANGE */
69 		} ndx;
70 	} value;
71 } MATCH;
72 
73 /* List of MATCH records used by match() to implement -N and -I options */
74 static MATCH *match_list = NULL;
75 
76 const char *
77 _elfdump_msg(Msg mid)
78 {
79 	return (gettext(MSG_ORIG(mid)));
80 }
81 
82 /*
83  * Determine whether a symbol name should be demangled.
84  */
85 const char *
86 demangle(const char *name, uint_t flags)
87 {
88 	if (flags & FLG_DEMANGLE)
89 		return (Elf_demangle_name(name));
90 	else
91 		return ((char *)name);
92 }
93 
94 /*
95  * Define our own standard error routine.
96  */
97 void
98 failure(const char *file, const char *func)
99 {
100 	(void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE),
101 	    file, func, elf_errmsg(elf_errno()));
102 }
103 
104 /*
105  * The full usage message
106  */
107 static void
108 detail_usage()
109 {
110 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1));
111 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2));
112 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3));
113 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4));
114 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5));
115 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6));
116 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7));
117 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8));
118 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9));
119 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10));
120 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11));
121 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12));
122 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13));
123 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14));
124 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15));
125 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16));
126 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17));
127 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18));
128 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19));
129 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20));
130 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21));
131 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22));
132 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23));
133 	(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL24));
134 }
135 
136 /*
137  * Convert the ASCII representation of an index, or index range, into
138  * binary form, and store it in rec:
139  *
140  *	index: An positive or 0 valued integer
141  *	range: Two indexes, separated by a ':' character, denoting
142  *		a range of allowed values. If the second value is omitted,
143  *		any values equal to or greater than the first will match.
144  *
145  * exit:
146  *	On success, *rec is filled in with a MATCH_T_NDX or MATCH_T_RANGE
147  *	value, and this function returns (1). On failure, the contents
148  *	of *rec are undefined, and (0) is returned.
149  */
150 int
151 process_index_opt(const char *str, MATCH *rec)
152 {
153 #define	SKIP_BLANK for (; *str && isspace(*str); str++)
154 
155 	char	*endptr;
156 
157 	rec->value.ndx.start = strtol(str, &endptr, 10);
158 	/* Value must use some of the input, and be 0 or positive */
159 	if ((str == endptr) || (rec->value.ndx.start < 0))
160 		return (0);
161 	str = endptr;
162 
163 	SKIP_BLANK;
164 	if (*str != ':') {
165 		rec->type = MATCH_T_NDX;
166 	} else {
167 		str++;					/* Skip the ':' */
168 		rec->type = MATCH_T_RANGE;
169 		SKIP_BLANK;
170 		if (*str == '\0') {
171 			rec->value.ndx.end = -1;	/* Indicates "to end" */
172 		} else {
173 			rec->value.ndx.end = strtol(str, &endptr, 10);
174 			if ((str == endptr) || (rec->value.ndx.end < 0))
175 				return (0);
176 			str = endptr;
177 			SKIP_BLANK;
178 		}
179 	}
180 
181 	/* Syntax error if anything is left over */
182 	if (*str != '\0')
183 		return (0);
184 
185 	return (1);
186 
187 #undef	SKIP_BLANK
188 }
189 
190 /*
191  * Returns True (1) if the item with the given name or index should
192  * be displayed, and False (0) if it should not be.
193  *
194  * entry:
195  *	strict - A strict match requires an explicit match to
196  *		a user specified -I or -N option. A non-strict match
197  *		succeeds if the match list is empty.
198  *	name - Name of item under consideration, or NULL if the name
199  *		should not be considered.
200  *	ndx - if (ndx >= 0) index of item under consideration.
201  *		A negative value indicates that the item has no index.
202  *
203  * exit:
204  *	True will be returned if the given name/index matches those given
205  *	by one of the -N or -I command line options, or if no such option
206  *	was used in the command invocation.
207  */
208 int
209 match(int strict, const char *name, int ndx)
210 {
211 	MATCH *list;
212 
213 	/* If no match options were specified, allow everything */
214 	if (!strict && (match_list == NULL))
215 		return (1);
216 
217 	/* Run through the match records and check for a hit */
218 	for (list = match_list; list; list = list->next) {
219 		switch (list->type) {
220 		case MATCH_T_NAME:
221 			if ((name != NULL) &&
222 			    (strcmp(list->value.name, name) == 0))
223 				return (1);
224 			break;
225 		case MATCH_T_NDX:
226 			if (ndx == list->value.ndx.start)
227 				return (1);
228 			break;
229 		case MATCH_T_RANGE:
230 			/*
231 			 * A range end value less than 0 means that any value
232 			 * above the start is acceptible.
233 			 */
234 			if ((ndx >= list->value.ndx.start) &&
235 			    ((list->value.ndx.end < 0) ||
236 			    (ndx <= list->value.ndx.end)))
237 				return (1);
238 			break;
239 		}
240 	}
241 
242 	/* Nothing matched */
243 	return (0);
244 }
245 
246 /*
247  * Add an entry to match_list for use by match().
248  *
249  * Return True (1) for success. On failure, an error is written
250  * to stderr, and False (0) is returned.
251  */
252 static int
253 add_match_record(char *argv0, MATCH *data)
254 {
255 	MATCH *rec;
256 	MATCH *list;
257 
258 	if ((rec = malloc(sizeof (*rec))) == NULL) {
259 		int err = errno;
260 		(void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC),
261 		    basename(argv0), strerror(err));
262 		return (0);
263 	}
264 
265 	*rec = *data;
266 
267 	/* Insert at end of match_list */
268 	if (match_list == NULL) {
269 		match_list = rec;
270 	} else {
271 		for (list = match_list; list->next != NULL; list = list->next)
272 			;
273 		list->next = rec;
274 	}
275 
276 	rec->next = NULL;
277 	return (1);
278 }
279 
280 static void
281 decide(const char *file, int fd, Elf *elf, uint_t flags, int wfd)
282 {
283 	if (gelf_getclass(elf) == ELFCLASS64)
284 		regular64(file, fd, elf, flags, wfd);
285 	else
286 		regular32(file, fd, elf, flags, wfd);
287 }
288 
289 static void
290 archive(const char *file, int fd, Elf *elf, uint_t flags, int wfd)
291 {
292 	Elf_Cmd		cmd = ELF_C_READ;
293 	Elf_Arhdr	*arhdr;
294 	Elf		*_elf = 0;
295 	size_t		ptr;
296 	Elf_Arsym	*arsym = 0;
297 
298 	/*
299 	 * Determine if the archive symbol table itself is required.
300 	 */
301 	if ((flags & FLG_SYMBOLS) && match(0, MSG_ORIG(MSG_ELF_ARSYM), -1)) {
302 		/*
303 		 * Get the archive symbol table.
304 		 */
305 		if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) {
306 			/*
307 			 * The arsym could be 0 even though there was no error.
308 			 * Print the error message only when there was
309 			 * real error from elf_getarsym().
310 			 */
311 			failure(file, MSG_ORIG(MSG_ELF_GETARSYM));
312 			return;
313 		}
314 	}
315 
316 	/*
317 	 * Print the archive symbol table only when the archive symbol
318 	 * table exists and it was requested to print.
319 	 */
320 	if (arsym) {
321 		size_t		cnt;
322 		char		index[MAXNDXSIZE];
323 		size_t		offset = 0, _offset = 0;
324 
325 		/*
326 		 * Print out all the symbol entries.
327 		 */
328 		dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB));
329 		dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS));
330 
331 		for (cnt = 0; cnt < ptr; cnt++, arsym++) {
332 			/*
333 			 * For each object obtain an elf descriptor so that we
334 			 * can establish the members name.  Note, we have had
335 			 * archives where the archive header has not been
336 			 * obtainable so be lenient with errors.
337 			 */
338 			if ((offset == 0) || ((arsym->as_off != 0) &&
339 			    (arsym->as_off != _offset))) {
340 
341 				if (_elf)
342 					(void) elf_end(_elf);
343 
344 				if (elf_rand(elf, arsym->as_off) !=
345 				    arsym->as_off) {
346 					failure(file, MSG_ORIG(MSG_ELF_RAND));
347 					arhdr = 0;
348 				} else if ((_elf = elf_begin(fd,
349 				    ELF_C_READ, elf)) == 0) {
350 					failure(file, MSG_ORIG(MSG_ELF_BEGIN));
351 					arhdr = 0;
352 				} else if ((arhdr = elf_getarhdr(_elf)) == 0) {
353 					failure(file,
354 					    MSG_ORIG(MSG_ELF_GETARHDR));
355 					arhdr = 0;
356 				}
357 
358 				_offset = arsym->as_off;
359 				if (offset == 0)
360 					offset = _offset;
361 			}
362 
363 			(void) snprintf(index, MAXNDXSIZE,
364 			    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt));
365 			if (arsym->as_off)
366 				dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM1), index,
367 				    /* LINTED */
368 				    (int)arsym->as_off, arhdr ? arhdr->ar_name :
369 				    MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ?
370 				    demangle(arsym->as_name, flags) :
371 				    MSG_INTL(MSG_STR_NULL)));
372 			else
373 				dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM2), index,
374 				    /* LINTED */
375 				    (int)arsym->as_off);
376 		}
377 
378 		if (_elf)
379 			(void) elf_end(_elf);
380 
381 		/*
382 		 * If we only need the archive symbol table return.
383 		 */
384 		if ((flags & FLG_SYMBOLS) &&
385 		    match(1, MSG_ORIG(MSG_ELF_ARSYM), -1))
386 			return;
387 
388 		/*
389 		 * Reset elf descriptor in preparation for processing each
390 		 * member.
391 		 */
392 		if (offset)
393 			(void) elf_rand(elf, offset);
394 	}
395 
396 	/*
397 	 * Process each object within the archive.
398 	 */
399 	while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
400 		char	name[MAXPATHLEN];
401 
402 		if ((arhdr = elf_getarhdr(_elf)) == NULL) {
403 			failure(file, MSG_ORIG(MSG_ELF_GETARHDR));
404 			return;
405 		}
406 		if (*arhdr->ar_name != '/') {
407 			(void) snprintf(name, MAXPATHLEN,
408 			    MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name);
409 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name);
410 
411 			switch (elf_kind(_elf)) {
412 			case ELF_K_AR:
413 				archive(name, fd, _elf, flags, wfd);
414 				break;
415 			case ELF_K_ELF:
416 				decide(name, fd, _elf, flags, wfd);
417 				break;
418 			default:
419 				(void) fprintf(stderr,
420 				    MSG_INTL(MSG_ERR_BADFILE), name);
421 				break;
422 			}
423 		}
424 
425 		cmd = elf_next(_elf);
426 		(void) elf_end(_elf);
427 	}
428 }
429 
430 int
431 main(int argc, char **argv, char **envp)
432 {
433 	Elf		*elf;
434 	int		var, fd, wfd = 0;
435 	char		*wname = 0;
436 	uint_t		flags = 0;
437 	MATCH		match_data;
438 
439 	/*
440 	 * If we're on a 64-bit kernel, try to exec a full 64-bit version of
441 	 * the binary.  If successful, conv_check_native() won't return.
442 	 */
443 	(void) conv_check_native(argv, envp);
444 
445 	/*
446 	 * Establish locale.
447 	 */
448 	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
449 	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
450 
451 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
452 	(void) setvbuf(stderr, NULL, _IOLBF, 0);
453 
454 	opterr = 0;
455 	while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
456 		switch (var) {
457 		case 'C':
458 			flags |= FLG_DEMANGLE;
459 			break;
460 		case 'c':
461 			flags |= FLG_SHDR;
462 			break;
463 		case 'd':
464 			flags |= FLG_DYNAMIC;
465 			break;
466 		case 'e':
467 			flags |= FLG_EHDR;
468 			break;
469 		case 'G':
470 			flags |= FLG_GOT;
471 			break;
472 		case 'g':
473 			flags |= FLG_GROUP;
474 			break;
475 		case 'H':
476 			flags |= FLG_CAP;
477 			break;
478 		case 'h':
479 			flags |= FLG_HASH;
480 			break;
481 		case 'I':
482 			if (!process_index_opt(optarg, &match_data)) {
483 				(void) fprintf(stderr,
484 				    MSG_INTL(MSG_USAGE_BRIEF),
485 				    basename(argv[0]));
486 				return (1);
487 			}
488 			if (!add_match_record(argv[0], &match_data))
489 				return (1);
490 			break;
491 		case 'i':
492 			flags |= FLG_INTERP;
493 			break;
494 		case 'k':
495 			flags |= FLG_CHECKSUM;
496 			break;
497 		case 'l':
498 			flags |= FLG_LONGNAME;
499 			break;
500 		case 'm':
501 			flags |= FLG_MOVE;
502 			break;
503 		case 'N':
504 			match_data.type = MATCH_T_NAME;
505 			match_data.value.name = optarg;
506 			if (!add_match_record(argv[0], &match_data))
507 				return (1);
508 			break;
509 		case 'n':
510 			flags |= FLG_NOTE;
511 			break;
512 		case 'P':
513 			flags |= FLG_FAKESHDR;
514 			break;
515 		case 'p':
516 			flags |= FLG_PHDR;
517 			break;
518 		case 'r':
519 			flags |= FLG_RELOC;
520 			break;
521 		case 'S':
522 			flags |= FLG_SORT;
523 			break;
524 		case 's':
525 			flags |= FLG_SYMBOLS;
526 			break;
527 		case 'u':
528 			flags |= FLG_UNWIND;
529 			break;
530 		case 'v':
531 			flags |= FLG_VERSIONS;
532 			break;
533 		case 'w':
534 			wname = optarg;
535 			break;
536 		case 'y':
537 			flags |= FLG_SYMINFO;
538 			break;
539 		case '?':
540 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
541 			    basename(argv[0]));
542 			detail_usage();
543 			return (1);
544 		default:
545 			break;
546 		}
547 	}
548 
549 	/*
550 	 * Validate any arguments.
551 	 */
552 	if ((flags & ~(FLG_DEMANGLE | FLG_LONGNAME| FLG_FAKESHDR)) == 0) {
553 		if (!wname && (match_list == NULL)) {
554 			flags |= FLG_EVERYTHING;
555 		} else if (!wname || (match_list == NULL)) {
556 			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
557 			    basename(argv[0]));
558 			return (1);
559 		}
560 	}
561 
562 	if ((var = argc - optind) == 0) {
563 		(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
564 		    basename(argv[0]));
565 		return (1);
566 	}
567 
568 	/*
569 	 * If the -l/-C option is specified, set up the liblddbg.so.
570 	 */
571 	if (flags & FLG_LONGNAME)
572 		dbg_desc->d_extra |= DBG_E_LONG;
573 	if (flags & FLG_DEMANGLE)
574 		dbg_desc->d_extra |= DBG_E_DEMANGLE;
575 
576 	/*
577 	 * If the -w option has indicated an output file open it.  It's
578 	 * arguable whether this option has much use when multiple files are
579 	 * being processed.
580 	 */
581 	if (wname) {
582 		if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC),
583 		    0666)) < 0) {
584 			int err = errno;
585 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
586 			    wname, strerror(err));
587 			wfd = 0;
588 		}
589 	}
590 
591 	/*
592 	 * Open the input file and initialize the elf interface.
593 	 */
594 	for (; optind < argc; optind++) {
595 		const char	*file = argv[optind];
596 
597 		if ((fd = open(argv[optind], O_RDONLY)) == -1) {
598 			int err = errno;
599 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
600 			    file, strerror(err));
601 			continue;
602 		}
603 		(void) elf_version(EV_CURRENT);
604 		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
605 			failure(file, MSG_ORIG(MSG_ELF_BEGIN));
606 			(void) close(fd);
607 			continue;
608 		}
609 
610 		if (var > 1)
611 			dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file);
612 
613 		switch (elf_kind(elf)) {
614 		case ELF_K_AR:
615 			archive(file, fd, elf, flags, wfd);
616 			break;
617 		case ELF_K_ELF:
618 			decide(file, fd, elf, flags, wfd);
619 			break;
620 		default:
621 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file);
622 			break;
623 		}
624 
625 		(void) close(fd);
626 		(void) elf_end(elf);
627 	}
628 
629 	if (wfd)
630 		(void) close(wfd);
631 	return (0);
632 }
633