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