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