/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Dump an elf file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <_elfdump.h> const Cache cache_init = {NULL, NULL, NULL, NULL, 0}; /* MATCH is used to retain information about -N and -I options */ typedef enum { MATCH_T_NAME, /* Record contains a name */ MATCH_T_NDX, /* Record contains a single index */ MATCH_T_RANGE /* Record contains an index range */ } MATCH_T; typedef struct _match { struct _match *next; /* Pointer to next item in list */ MATCH_T type; union { const char *name; /* MATCH_T_NAME */ struct { /* MATCH_T_NDX and MATCH_T_RANGE */ int start; int end; /* Only for MATCH_T_RANGE */ } ndx; } value; } MATCH; /* List of MATCH records used by match() to implement -N and -I options */ static MATCH *match_list = NULL; const char * _elfdump_msg(Msg mid) { return (gettext(MSG_ORIG(mid))); } /* * Determine whether a symbol name should be demangled. */ const char * demangle(const char *name, uint_t flags) { if (flags & FLG_DEMANGLE) return (Elf_demangle_name(name)); else return ((char *)name); } /* * Define our own standard error routine. */ void failure(const char *file, const char *func) { (void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE), file, func, elf_errmsg(elf_errno())); } /* * The full usage message */ static void detail_usage() { (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22)); (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23)); } /* * Convert the ASCII representation of an index, or index range, into * binary form, and store it in rec: * * index: An positive or 0 valued integer * range: Two indexes, separated by a ':' character, denoting * a range of allowed values. If the second value is omitted, * any values equal to or greater than the first will match. * * exit: * On success, *rec is filled in with a MATCH_T_NDX or MATCH_T_RANGE * value, and this function returns (1). On failure, the contents * of *rec are undefined, and (0) is returned. */ int process_index_opt(const char *str, MATCH *rec) { #define SKIP_BLANK for (; *str && isspace(*str); str++) char *endptr; rec->value.ndx.start = strtol(str, &endptr, 10); /* Value must use some of the input, and be 0 or positive */ if ((str == endptr) || (rec->value.ndx.start < 0)) return (0); str = endptr; SKIP_BLANK; if (*str != ':') { rec->type = MATCH_T_NDX; } else { str++; /* Skip the ':' */ rec->type = MATCH_T_RANGE; SKIP_BLANK; if (*str == '\0') { rec->value.ndx.end = -1; /* Indicates "to end" */ } else { rec->value.ndx.end = strtol(str, &endptr, 10); if ((str == endptr) || (rec->value.ndx.end < 0)) return (0); str = endptr; SKIP_BLANK; } } /* Syntax error if anything is left over */ if (*str != '\0') return (0); return (1); #undef SKIP_BLANK } /* * Returns True (1) if the item with the given name or index should * be displayed, and False (0) if it should not be. * * entry: * strict - A strict match requires an explicit match to * a user specified -I or -N option. A non-strict match * succeeds if the match list is empty. * name - Name of item under consideration, or NULL if the name * should not be considered. * ndx - if (ndx >= 0) index of item under consideration. * A negative value indicates that the item has no index. * * exit: * True will be returned if the given name/index matches those given * by one of the -N or -I command line options, or if no such option * was used in the command invocation. */ int match(int strict, const char *name, int ndx) { MATCH *list; /* If no match options were specified, allow everything */ if (!strict && (match_list == NULL)) return (1); /* Run through the match records and check for a hit */ for (list = match_list; list; list = list->next) { switch (list->type) { case MATCH_T_NAME: if ((name != NULL) && (strcmp(list->value.name, name) == 0)) return (1); break; case MATCH_T_NDX: if (ndx == list->value.ndx.start) return (1); break; case MATCH_T_RANGE: /* * A range end value less than 0 means that any value * above the start is acceptible. */ if ((ndx >= list->value.ndx.start) && ((list->value.ndx.end < 0) || (ndx <= list->value.ndx.end))) return (1); break; } } /* Nothing matched */ return (0); } /* * Add an entry to match_list for use by match(). * * Return True (1) for success. On failure, an error is written * to stderr, and False (0) is returned. */ static int add_match_record(char *argv0, MATCH *data) { MATCH *rec; MATCH *list; if ((rec = malloc(sizeof (*rec))) == NULL) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC), basename(argv0), strerror(err)); return (0); } *rec = *data; /* Insert at end of match_list */ if (match_list == NULL) { match_list = rec; } else { for (list = match_list; list->next != NULL; list = list->next) ; list->next = rec; } rec->next = NULL; return (1); } static void decide(const char *file, Elf *elf, uint_t flags, int wfd) { if (gelf_getclass(elf) == ELFCLASS64) regular64(file, elf, flags, wfd); else regular32(file, elf, flags, wfd); } static void archive(const char *file, int fd, Elf *elf, uint_t flags, int wfd) { Elf_Cmd cmd = ELF_C_READ; Elf_Arhdr *arhdr; Elf *_elf = 0; size_t ptr; Elf_Arsym *arsym = 0; /* * Determine if the archive symbol table itself is required. */ if ((flags & FLG_SYMBOLS) && match(0, MSG_ORIG(MSG_ELF_ARSYM), -1)) { /* * Get the archive symbol table. */ if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) { /* * The arsym could be 0 even though there was no error. * Print the error message only when there was * real error from elf_getarsym(). */ failure(file, MSG_ORIG(MSG_ELF_GETARSYM)); return; } } /* * Print the archive symbol table only when the archive symbol * table exists and it was requested to print. */ if (arsym) { size_t cnt; char index[MAXNDXSIZE]; size_t offset = 0, _offset = 0; /* * Print out all the symbol entries. */ dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB)); dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS)); for (cnt = 0; cnt < ptr; cnt++, arsym++) { /* * For each object obtain an elf descriptor so that we * can establish the members name. Note, we have had * archives where the archive header has not been * obtainable so be lenient with errors. */ if ((offset == 0) || ((arsym->as_off != 0) && (arsym->as_off != _offset))) { if (_elf) (void) elf_end(_elf); if (elf_rand(elf, arsym->as_off) != arsym->as_off) { failure(file, MSG_ORIG(MSG_ELF_RAND)); arhdr = 0; } else if ((_elf = elf_begin(fd, ELF_C_READ, elf)) == 0) { failure(file, MSG_ORIG(MSG_ELF_BEGIN)); arhdr = 0; } else if ((arhdr = elf_getarhdr(_elf)) == 0) { failure(file, MSG_ORIG(MSG_ELF_GETARHDR)); arhdr = 0; } _offset = arsym->as_off; if (offset == 0) offset = _offset; } (void) snprintf(index, MAXNDXSIZE, MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt)); if (arsym->as_off) dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM1), index, /* LINTED */ (int)arsym->as_off, arhdr ? arhdr->ar_name : MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ? demangle(arsym->as_name, flags) : MSG_INTL(MSG_STR_NULL))); else dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM2), index, /* LINTED */ (int)arsym->as_off); } if (_elf) (void) elf_end(_elf); /* * If we only need the archive symbol table return. */ if ((flags & FLG_SYMBOLS) && match(1, MSG_ORIG(MSG_ELF_ARSYM), -1)) return; /* * Reset elf descriptor in preparation for processing each * member. */ if (offset) (void) elf_rand(elf, offset); } /* * Process each object within the archive. */ while ((_elf = elf_begin(fd, cmd, elf)) != NULL) { char name[MAXPATHLEN]; if ((arhdr = elf_getarhdr(_elf)) == NULL) { failure(file, MSG_ORIG(MSG_ELF_GETARHDR)); return; } if (*arhdr->ar_name != '/') { (void) snprintf(name, MAXPATHLEN, MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name); dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name); switch (elf_kind(_elf)) { case ELF_K_AR: archive(name, fd, _elf, flags, wfd); break; case ELF_K_ELF: decide(name, _elf, flags, wfd); break; default: (void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), name); break; } } cmd = elf_next(_elf); (void) elf_end(_elf); } } int main(int argc, char **argv, char **envp) { Elf *elf; int var, fd, wfd = 0; char *wname = 0; uint_t flags = 0; MATCH match_data; /* * If we're on a 64-bit kernel, try to exec a full 64-bit version of * the binary. If successful, conv_check_native() won't return. */ (void) conv_check_native(argv, envp); /* * Establish locale. */ (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); (void) setvbuf(stdout, NULL, _IOLBF, 0); (void) setvbuf(stderr, NULL, _IOLBF, 0); opterr = 0; while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { switch (var) { case 'C': flags |= FLG_DEMANGLE; break; case 'c': flags |= FLG_SHDR; break; case 'd': flags |= FLG_DYNAMIC; break; case 'e': flags |= FLG_EHDR; break; case 'G': flags |= FLG_GOT; break; case 'g': flags |= FLG_GROUP; break; case 'H': flags |= FLG_CAP; break; case 'h': flags |= FLG_HASH; break; case 'I': if (!process_index_opt(optarg, &match_data)) { (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), basename(argv[0])); return (1); } if (!add_match_record(argv[0], &match_data)) return (1); break; case 'i': flags |= FLG_INTERP; break; case 'k': flags |= FLG_CHECKSUM; break; case 'l': flags |= FLG_LONGNAME; break; case 'm': flags |= FLG_MOVE; break; case 'N': match_data.type = MATCH_T_NAME; match_data.value.name = optarg; if (!add_match_record(argv[0], &match_data)) return (1); break; case 'n': flags |= FLG_NOTE; break; case 'p': flags |= FLG_PHDR; break; case 'r': flags |= FLG_RELOC; break; case 'S': flags |= FLG_SORT; break; case 's': flags |= FLG_SYMBOLS; break; case 'u': flags |= FLG_UNWIND; break; case 'v': flags |= FLG_VERSIONS; break; case 'w': wname = optarg; break; case 'y': flags |= FLG_SYMINFO; break; case '?': (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), basename(argv[0])); detail_usage(); return (1); default: break; } } /* * Validate any arguments. */ if ((flags & ~(FLG_DEMANGLE | FLG_LONGNAME)) == 0) { if (!wname && (match_list == NULL)) { flags |= FLG_EVERYTHING; } else if (!wname || (match_list == NULL)) { (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), basename(argv[0])); return (1); } } if ((var = argc - optind) == 0) { (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), basename(argv[0])); return (1); } /* * If the -l/-C option is specified, set up the liblddbg.so. */ if (flags & FLG_LONGNAME) dbg_desc->d_extra |= DBG_E_LONG; if (flags & FLG_DEMANGLE) dbg_desc->d_extra |= DBG_E_DEMANGLE; /* * If the -w option has indicated an output file open it. It's * arguable whether this option has much use when multiple files are * being processed. */ if (wname) { if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), wname, strerror(err)); wfd = 0; } } /* * Open the input file and initialize the elf interface. */ for (; optind < argc; optind++) { const char *file = argv[optind]; if ((fd = open(argv[optind], O_RDONLY)) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), file, strerror(err)); continue; } (void) elf_version(EV_CURRENT); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { failure(file, MSG_ORIG(MSG_ELF_BEGIN)); (void) close(fd); continue; } if (var > 1) dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file); switch (elf_kind(elf)) { case ELF_K_AR: archive(file, fd, elf, flags, wfd); break; case ELF_K_ELF: decide(file, elf, flags, wfd); break; default: (void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file); break; } (void) close(fd); (void) elf_end(elf); } if (wfd) (void) close(wfd); return (0); }