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