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