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