readelf.c revision 111658
1#include "file.h" 2 3#ifdef BUILTIN_ELF 4#include <string.h> 5#include <ctype.h> 6#include <stdlib.h> 7#ifdef HAVE_UNISTD_H 8#include <unistd.h> 9#endif 10 11#include "readelf.h" 12 13#ifndef lint 14FILE_RCSID("@(#)$Id: readelf.c,v 1.26 2003/02/25 15:30:00 christos Exp $") 15#endif 16 17#ifdef ELFCORE 18static void dophn_core(int, int, int, off_t, int, size_t); 19#endif 20static void dophn_exec(int, int, int, off_t, int, size_t); 21static void doshn(int, int, int, off_t, int, size_t); 22 23static uint16_t getu16(int, uint16_t); 24static uint32_t getu32(int, uint32_t); 25static uint64_t getu64(int, uint64_t); 26 27static uint16_t 28getu16(int swap, uint16_t value) 29{ 30 union { 31 uint16_t ui; 32 char c[2]; 33 } retval, tmpval; 34 35 if (swap) { 36 tmpval.ui = value; 37 38 retval.c[0] = tmpval.c[1]; 39 retval.c[1] = tmpval.c[0]; 40 41 return retval.ui; 42 } else 43 return value; 44} 45 46static uint32_t 47getu32(int swap, uint32_t value) 48{ 49 union { 50 uint32_t ui; 51 char c[4]; 52 } retval, tmpval; 53 54 if (swap) { 55 tmpval.ui = value; 56 57 retval.c[0] = tmpval.c[3]; 58 retval.c[1] = tmpval.c[2]; 59 retval.c[2] = tmpval.c[1]; 60 retval.c[3] = tmpval.c[0]; 61 62 return retval.ui; 63 } else 64 return value; 65} 66 67static uint64_t 68getu64(int swap, uint64_t value) 69{ 70 union { 71 uint64_t ui; 72 char c[8]; 73 } retval, tmpval; 74 75 if (swap) { 76 tmpval.ui = value; 77 78 retval.c[0] = tmpval.c[7]; 79 retval.c[1] = tmpval.c[6]; 80 retval.c[2] = tmpval.c[5]; 81 retval.c[3] = tmpval.c[4]; 82 retval.c[4] = tmpval.c[3]; 83 retval.c[5] = tmpval.c[2]; 84 retval.c[6] = tmpval.c[1]; 85 retval.c[7] = tmpval.c[0]; 86 87 return retval.ui; 88 } else 89 return value; 90} 91 92#define sh_addr (class == ELFCLASS32 \ 93 ? (void *) &sh32 \ 94 : (void *) &sh64) 95#define sh_size (class == ELFCLASS32 \ 96 ? sizeof sh32 \ 97 : sizeof sh64) 98#define shs_type (class == ELFCLASS32 \ 99 ? getu32(swap, sh32.sh_type) \ 100 : getu32(swap, sh64.sh_type)) 101#define ph_addr (class == ELFCLASS32 \ 102 ? (void *) &ph32 \ 103 : (void *) &ph64) 104#define ph_size (class == ELFCLASS32 \ 105 ? sizeof ph32 \ 106 : sizeof ph64) 107#define ph_type (class == ELFCLASS32 \ 108 ? getu32(swap, ph32.p_type) \ 109 : getu32(swap, ph64.p_type)) 110#define ph_offset (class == ELFCLASS32 \ 111 ? getu32(swap, ph32.p_offset) \ 112 : getu64(swap, ph64.p_offset)) 113#define ph_align (class == ELFCLASS32 \ 114 ? (ph32.p_align ? getu32(swap, ph32.p_align) : 4) \ 115 : (ph64.p_align ? getu64(swap, ph64.p_align) : 4)) 116#define nh_size (class == ELFCLASS32 \ 117 ? sizeof *nh32 \ 118 : sizeof *nh64) 119#define nh_type (class == ELFCLASS32 \ 120 ? getu32(swap, nh32->n_type) \ 121 : getu32(swap, nh64->n_type)) 122#define nh_namesz (class == ELFCLASS32 \ 123 ? getu32(swap, nh32->n_namesz) \ 124 : getu32(swap, nh64->n_namesz)) 125#define nh_descsz (class == ELFCLASS32 \ 126 ? getu32(swap, nh32->n_descsz) \ 127 : getu32(swap, nh64->n_descsz)) 128#define prpsoffsets(i) (class == ELFCLASS32 \ 129 ? prpsoffsets32[i] \ 130 : prpsoffsets64[i]) 131 132static void 133doshn(int class, int swap, int fd, off_t off, int num, size_t size) 134{ 135 Elf32_Shdr sh32; 136 Elf64_Shdr sh64; 137 138 if (size != sh_size) 139 error("corrupted section header size.\n"); 140 141 if (lseek(fd, off, SEEK_SET) == -1) 142 error("lseek failed (%s).\n", strerror(errno)); 143 144 for ( ; num; num--) { 145 if (read(fd, sh_addr, sh_size) == -1) 146 error("read failed (%s).\n", strerror(errno)); 147 if (shs_type == SHT_SYMTAB /* || shs_type == SHT_DYNSYM */) { 148 (void) printf (", not stripped"); 149 return; 150 } 151 } 152 (void) printf (", stripped"); 153} 154 155/* 156 * Look through the program headers of an executable image, searching 157 * for a PT_INTERP section; if one is found, it's dynamically linked, 158 * otherwise it's statically linked. 159 */ 160static void 161dophn_exec(int class, int swap, int fd, off_t off, int num, size_t size) 162{ 163 Elf32_Phdr ph32; 164 Elf32_Nhdr *nh32 = NULL; 165 Elf64_Phdr ph64; 166 Elf64_Nhdr *nh64 = NULL; 167 char *linking_style = "statically"; 168 char *shared_libraries = ""; 169 char nbuf[BUFSIZ]; 170 int bufsize; 171 size_t offset, nameoffset; 172 off_t savedoffset; 173 174 if (size != ph_size) 175 error("corrupted program header size.\n"); 176 if (lseek(fd, off, SEEK_SET) == -1) 177 error("lseek failed (%s).\n", strerror(errno)); 178 179 for ( ; num; num--) { 180 if (read(fd, ph_addr, ph_size) == -1) 181 error("read failed (%s).\n", strerror(errno)); 182 if ((savedoffset = lseek(fd, 0, SEEK_CUR)) == -1) 183 error("lseek failed (%s).\n", strerror(errno)); 184 185 switch (ph_type) { 186 case PT_DYNAMIC: 187 linking_style = "dynamically"; 188 break; 189 case PT_INTERP: 190 shared_libraries = " (uses shared libs)"; 191 break; 192 case PT_NOTE: 193 /* 194 * This is a PT_NOTE section; loop through all the notes 195 * in the section. 196 */ 197 if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1) 198 error("lseek failed (%s).\n", strerror(errno)); 199 bufsize = read(fd, nbuf, sizeof(nbuf)); 200 if (bufsize == -1) 201 error(": " "read failed (%s).\n", 202 strerror(errno)); 203 offset = 0; 204 for (;;) { 205 if (offset >= bufsize) 206 break; 207 if (class == ELFCLASS32) 208 nh32 = (Elf32_Nhdr *)&nbuf[offset]; 209 else 210 nh64 = (Elf64_Nhdr *)&nbuf[offset]; 211 offset += nh_size; 212 213 if (offset + nh_namesz >= bufsize) { 214 /* 215 * We're past the end of the buffer. 216 */ 217 break; 218 } 219 220 nameoffset = offset; 221 offset += nh_namesz; 222 offset = ((offset+ph_align-1)/ph_align)*ph_align; 223 224 if ((nh_namesz == 0) && (nh_descsz == 0)) { 225 /* 226 * We're out of note headers. 227 */ 228 break; 229 } 230 231 if (offset + nh_descsz >= bufsize) 232 break; 233 234 if (nh_namesz == 4 && 235 strcmp(&nbuf[nameoffset], "GNU") == 0 && 236 nh_type == NT_GNU_VERSION && 237 nh_descsz == 16) { 238 uint32_t *desc = 239 (uint32_t *)&nbuf[offset]; 240 241 printf(", for GNU/"); 242 switch (getu32(swap, desc[0])) { 243 case GNU_OS_LINUX: 244 printf("Linux"); 245 break; 246 case GNU_OS_HURD: 247 printf("Hurd"); 248 break; 249 case GNU_OS_SOLARIS: 250 printf("Solaris"); 251 break; 252 default: 253 printf("<unknown>"); 254 } 255 printf(" %d.%d.%d", 256 getu32(swap, desc[1]), 257 getu32(swap, desc[2]), 258 getu32(swap, desc[3])); 259 } 260 261 if (nh_namesz == 7 && 262 strcmp(&nbuf[nameoffset], "NetBSD") == 0 && 263 nh_type == NT_NETBSD_VERSION && 264 nh_descsz == 4) { 265 printf(", for NetBSD"); 266 /* 267 * Version number is stuck at 199905, 268 * and hence is basically content-free. 269 */ 270 } 271 272 if (nh_namesz == 8 && 273 strcmp(&nbuf[nameoffset], "FreeBSD") == 0 && 274 nh_type == NT_FREEBSD_VERSION && 275 nh_descsz == 4) { 276 uint32_t desc = getu32(swap, 277 *(uint32_t *)&nbuf[offset]); 278 printf(", for FreeBSD"); 279 /* 280 * Contents is __FreeBSD_version, 281 * whose relation to OS versions is 282 * defined by a huge table in the 283 * Porters' Handbook. Happily, the 284 * first three digits are the version 285 * number, at least in versions of 286 * FreeBSD that use this note. 287 */ 288 289 printf(" %d.%d", desc / 100000, 290 desc / 10000 % 10); 291 if (desc / 1000 % 10 > 0) 292 printf(".%d", 293 desc / 1000 % 10); 294 } 295 296 if (nh_namesz == 8 && 297 strcmp(&nbuf[nameoffset], "OpenBSD") == 0 && 298 nh_type == NT_OPENBSD_VERSION && 299 nh_descsz == 4) { 300 printf(", for OpenBSD"); 301 /* Content of note is always 0 */ 302 } 303 } 304 if ((lseek(fd, savedoffset + offset, SEEK_SET)) == -1) 305 error("lseek failed (%s).\n", strerror(errno)); 306 break; 307 } 308 } 309 printf(", %s linked%s", linking_style, shared_libraries); 310} 311 312#ifdef ELFCORE 313size_t prpsoffsets32[] = { 314 8, /* FreeBSD */ 315 28, /* Linux 2.0.36 */ 316 32, /* Linux (I forget which kernel version) */ 317 84, /* SunOS 5.x */ 318}; 319 320size_t prpsoffsets64[] = { 321 120, /* SunOS 5.x, 64-bit */ 322}; 323 324#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) 325#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) 326 327#define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) 328 329/* 330 * Look through the program headers of an executable image, searching 331 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or 332 * "FreeBSD"; if one is found, try looking in various places in its 333 * contents for a 16-character string containing only printable 334 * characters - if found, that string should be the name of the program 335 * that dropped core. Note: right after that 16-character string is, 336 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and 337 * Linux, a longer string (80 characters, in 5.x, probably other 338 * SVR4-flavored systems, and Linux) containing the start of the 339 * command line for that program. 340 * 341 * The signal number probably appears in a section of type NT_PRSTATUS, 342 * but that's also rather OS-dependent, in ways that are harder to 343 * dissect with heuristics, so I'm not bothering with the signal number. 344 * (I suppose the signal number could be of interest in situations where 345 * you don't have the binary of the program that dropped core; if you 346 * *do* have that binary, the debugger will probably tell you what 347 * signal it was.) 348 */ 349 350#define OS_STYLE_SVR4 0 351#define OS_STYLE_FREEBSD 1 352#define OS_STYLE_NETBSD 2 353 354static const char *os_style_names[] = { 355 "SVR4", 356 "FreeBSD", 357 "NetBSD", 358}; 359 360static void 361dophn_core(int class, int swap, int fd, off_t off, int num, size_t size) 362{ 363 Elf32_Phdr ph32; 364 Elf32_Nhdr *nh32 = NULL; 365 Elf64_Phdr ph64; 366 Elf64_Nhdr *nh64 = NULL; 367 size_t offset, nameoffset, noffset, reloffset; 368 unsigned char c; 369 int i, j; 370 char nbuf[BUFSIZ]; 371 int bufsize; 372 int os_style = -1; 373 374 if (size != ph_size) 375 error("corrupted program header size.\n"); 376 /* 377 * Loop through all the program headers. 378 */ 379 for ( ; num; num--) { 380 if (lseek(fd, off, SEEK_SET) == -1) 381 error("lseek failed (%s).\n", strerror(errno)); 382 if (read(fd, ph_addr, ph_size) == -1) 383 error("read failed (%s).\n", strerror(errno)); 384 off += size; 385 if (ph_type != PT_NOTE) 386 continue; 387 388 /* 389 * This is a PT_NOTE section; loop through all the notes 390 * in the section. 391 */ 392 if (lseek(fd, (off_t) ph_offset, SEEK_SET) == -1) 393 error("lseek failed (%s).\n", strerror(errno)); 394 bufsize = read(fd, nbuf, BUFSIZ); 395 if (bufsize == -1) 396 error(": " "read failed (%s).\n", strerror(errno)); 397 offset = 0; 398 for (;;) { 399 if (offset >= bufsize) 400 break; 401 if (class == ELFCLASS32) 402 nh32 = (Elf32_Nhdr *)&nbuf[offset]; 403 else 404 nh64 = (Elf64_Nhdr *)&nbuf[offset]; 405 offset += nh_size; 406 407 /* 408 * Check whether this note has the name "CORE" or 409 * "FreeBSD", or "NetBSD-CORE". 410 */ 411 if (offset + nh_namesz >= bufsize) { 412 /* 413 * We're past the end of the buffer. 414 */ 415 break; 416 } 417 418 nameoffset = offset; 419 offset += nh_namesz; 420 offset = ((offset + 3)/4)*4; 421 422 /* 423 * Sigh. The 2.0.36 kernel in Debian 2.1, at 424 * least, doesn't correctly implement name 425 * sections, in core dumps, as specified by 426 * the "Program Linking" section of "UNIX(R) System 427 * V Release 4 Programmer's Guide: ANSI C and 428 * Programming Support Tools", because my copy 429 * clearly says "The first 'namesz' bytes in 'name' 430 * contain a *null-terminated* [emphasis mine] 431 * character representation of the entry's owner 432 * or originator", but the 2.0.36 kernel code 433 * doesn't include the terminating null in the 434 * name.... 435 */ 436 if (os_style == -1) { 437 if ((nh_namesz == 4 && 438 strncmp(&nbuf[nameoffset], 439 "CORE", 4) == 0) || 440 (nh_namesz == 5 && 441 strcmp(&nbuf[nameoffset], 442 "CORE") == 0)) { 443 os_style = OS_STYLE_SVR4; 444 } else 445 if ((nh_namesz == 8 && 446 strcmp(&nbuf[nameoffset], 447 "FreeBSD") == 0)) { 448 os_style = OS_STYLE_FREEBSD; 449 } else 450 if ((nh_namesz >= 11 && 451 strncmp(&nbuf[nameoffset], 452 "NetBSD-CORE", 11) == 0)) { 453 os_style = OS_STYLE_NETBSD; 454 } else 455 continue; 456 printf(", %s-style", os_style_names[os_style]); 457 } 458 459 if (os_style == OS_STYLE_NETBSD && 460 nh_type == NT_NETBSD_CORE_PROCINFO) { 461 uint32_t signo; 462 463 /* 464 * Extract the program name. It is at 465 * offset 0x7c, and is up to 32-bytes, 466 * including the terminating NUL. 467 */ 468 printf(", from '%.31s'", &nbuf[offset + 0x7c]); 469 470 /* 471 * Extract the signal number. It is at 472 * offset 0x08. 473 */ 474 memcpy(&signo, &nbuf[offset + 0x08], 475 sizeof(signo)); 476 printf(" (signal %u)", getu32(swap, signo)); 477 } else 478 if (os_style != OS_STYLE_NETBSD && 479 nh_type == NT_PRPSINFO) { 480 /* 481 * Extract the program name. We assume 482 * it to be 16 characters (that's what it 483 * is in SunOS 5.x and Linux). 484 * 485 * Unfortunately, it's at a different offset 486 * in varous OSes, so try multiple offsets. 487 * If the characters aren't all printable, 488 * reject it. 489 */ 490 for (i = 0; i < NOFFSETS; i++) { 491 reloffset = prpsoffsets(i); 492 noffset = offset + reloffset; 493 for (j = 0; j < 16; 494 j++, noffset++, reloffset++) { 495 /* 496 * Make sure we're not past 497 * the end of the buffer; if 498 * we are, just give up. 499 */ 500 if (noffset >= bufsize) 501 goto tryanother; 502 503 /* 504 * Make sure we're not past 505 * the end of the contents; 506 * if we are, this obviously 507 * isn't the right offset. 508 */ 509 if (reloffset >= nh_descsz) 510 goto tryanother; 511 512 c = nbuf[noffset]; 513 if (c == '\0') { 514 /* 515 * A '\0' at the 516 * beginning is 517 * obviously wrong. 518 * Any other '\0' 519 * means we're done. 520 */ 521 if (j == 0) 522 goto tryanother; 523 else 524 break; 525 } else { 526 /* 527 * A nonprintable 528 * character is also 529 * wrong. 530 */ 531#define isquote(c) (strchr("'\"`", (c)) != NULL) 532 if (!isprint(c) || 533 isquote(c)) 534 goto tryanother; 535 } 536 } 537 538 /* 539 * Well, that worked. 540 */ 541 printf(", from '%.16s'", 542 &nbuf[offset + prpsoffsets(i)]); 543 break; 544 545 tryanother: 546 ; 547 } 548 break; 549 } 550 offset += nh_descsz; 551 offset = ((offset + 3)/4)*4; 552 } 553 } 554} 555#endif 556 557void 558tryelf(int fd, unsigned char *buf, int nbytes) 559{ 560 union { 561 int32_t l; 562 char c[sizeof (int32_t)]; 563 } u; 564 int class; 565 int swap; 566 567 /* 568 * If we can't seek, it must be a pipe, socket or fifo. 569 */ 570 if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) 571 fd = pipe2file(fd, buf, nbytes); 572 573 /* 574 * ELF executables have multiple section headers in arbitrary 575 * file locations and thus file(1) cannot determine it from easily. 576 * Instead we traverse thru all section headers until a symbol table 577 * one is found or else the binary is stripped. 578 */ 579 if (buf[EI_MAG0] != ELFMAG0 580 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) 581 || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) 582 return; 583 584 585 class = buf[4]; 586 587 if (class == ELFCLASS32) { 588 Elf32_Ehdr elfhdr; 589 if (nbytes <= sizeof (Elf32_Ehdr)) 590 return; 591 592 593 u.l = 1; 594 (void) memcpy(&elfhdr, buf, sizeof elfhdr); 595 swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5]; 596 597 if (getu16(swap, elfhdr.e_type) == ET_CORE) 598#ifdef ELFCORE 599 dophn_core(class, swap, 600 fd, 601 getu32(swap, elfhdr.e_phoff), 602 getu16(swap, elfhdr.e_phnum), 603 getu16(swap, elfhdr.e_phentsize)); 604#else 605 ; 606#endif 607 else { 608 if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 609 dophn_exec(class, swap, 610 fd, 611 getu32(swap, elfhdr.e_phoff), 612 getu16(swap, elfhdr.e_phnum), 613 getu16(swap, elfhdr.e_phentsize)); 614 } 615 doshn(class, swap, 616 fd, 617 getu32(swap, elfhdr.e_shoff), 618 getu16(swap, elfhdr.e_shnum), 619 getu16(swap, elfhdr.e_shentsize)); 620 } 621 return; 622 } 623 624 if (class == ELFCLASS64) { 625 Elf64_Ehdr elfhdr; 626 if (nbytes <= sizeof (Elf64_Ehdr)) 627 return; 628 629 630 u.l = 1; 631 (void) memcpy(&elfhdr, buf, sizeof elfhdr); 632 swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5]; 633 634 if (getu16(swap, elfhdr.e_type) == ET_CORE) 635#ifdef ELFCORE 636 dophn_core(class, swap, 637 fd, 638#ifdef USE_ARRAY_FOR_64BIT_TYPES 639 getu32(swap, elfhdr.e_phoff[1]), 640#else 641 getu64(swap, elfhdr.e_phoff), 642#endif 643 getu16(swap, elfhdr.e_phnum), 644 getu16(swap, elfhdr.e_phentsize)); 645#else 646 ; 647#endif 648 else 649 { 650 if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 651 dophn_exec(class, swap, 652 fd, 653#ifdef USE_ARRAY_FOR_64BIT_TYPES 654 getu32(swap, elfhdr.e_phoff[1]), 655#else 656 getu64(swap, elfhdr.e_phoff), 657#endif 658 getu16(swap, elfhdr.e_phnum), 659 getu16(swap, elfhdr.e_phentsize)); 660 } 661 doshn(class, swap, 662 fd, 663#ifdef USE_ARRAY_FOR_64BIT_TYPES 664 getu32(swap, elfhdr.e_shoff[1]), 665#else 666 getu64(swap, elfhdr.e_shoff), 667#endif 668 getu16(swap, elfhdr.e_shnum), 669 getu16(swap, elfhdr.e_shentsize)); 670 } 671 return; 672 } 673} 674#endif 675