readelf.c revision 275670
1/* 2 * Copyright (c) Christos Zoulas 2003. 3 * All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27#include "file.h" 28 29#ifndef lint 30FILE_RCSID("@(#)$File: readelf.c,v 1.103 2014/05/02 02:25:10 christos Exp $") 31#endif 32 33#ifdef BUILTIN_ELF 34#include <string.h> 35#include <ctype.h> 36#include <stdlib.h> 37#ifdef HAVE_UNISTD_H 38#include <unistd.h> 39#endif 40 41#include "readelf.h" 42#include "magic.h" 43 44#ifdef ELFCORE 45private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t, 46 off_t, int *); 47#endif 48private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t, 49 off_t, int *, int); 50private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, 51 off_t, int *, int, int); 52private size_t donote(struct magic_set *, void *, size_t, size_t, int, 53 int, size_t, int *); 54 55#define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) 56 57#define isquote(c) (strchr("'\"`", (c)) != NULL) 58 59private uint16_t getu16(int, uint16_t); 60private uint32_t getu32(int, uint32_t); 61private uint64_t getu64(int, uint64_t); 62 63#define MAX_PHNUM 256 64#define MAX_SHNUM 1024 65 66private int 67toomany(struct magic_set *ms, const char *name, uint16_t num) 68{ 69 if (file_printf(ms, ", too many %s header sections (%u)", name, num 70 ) == -1) 71 return -1; 72 return 0; 73} 74 75private uint16_t 76getu16(int swap, uint16_t value) 77{ 78 union { 79 uint16_t ui; 80 char c[2]; 81 } retval, tmpval; 82 83 if (swap) { 84 tmpval.ui = value; 85 86 retval.c[0] = tmpval.c[1]; 87 retval.c[1] = tmpval.c[0]; 88 89 return retval.ui; 90 } else 91 return value; 92} 93 94private uint32_t 95getu32(int swap, uint32_t value) 96{ 97 union { 98 uint32_t ui; 99 char c[4]; 100 } retval, tmpval; 101 102 if (swap) { 103 tmpval.ui = value; 104 105 retval.c[0] = tmpval.c[3]; 106 retval.c[1] = tmpval.c[2]; 107 retval.c[2] = tmpval.c[1]; 108 retval.c[3] = tmpval.c[0]; 109 110 return retval.ui; 111 } else 112 return value; 113} 114 115private uint64_t 116getu64(int swap, uint64_t value) 117{ 118 union { 119 uint64_t ui; 120 char c[8]; 121 } retval, tmpval; 122 123 if (swap) { 124 tmpval.ui = value; 125 126 retval.c[0] = tmpval.c[7]; 127 retval.c[1] = tmpval.c[6]; 128 retval.c[2] = tmpval.c[5]; 129 retval.c[3] = tmpval.c[4]; 130 retval.c[4] = tmpval.c[3]; 131 retval.c[5] = tmpval.c[2]; 132 retval.c[6] = tmpval.c[1]; 133 retval.c[7] = tmpval.c[0]; 134 135 return retval.ui; 136 } else 137 return value; 138} 139 140#define elf_getu16(swap, value) getu16(swap, value) 141#define elf_getu32(swap, value) getu32(swap, value) 142#define elf_getu64(swap, value) getu64(swap, value) 143 144#define xsh_addr (clazz == ELFCLASS32 \ 145 ? (void *)&sh32 \ 146 : (void *)&sh64) 147#define xsh_sizeof (clazz == ELFCLASS32 \ 148 ? sizeof(sh32) \ 149 : sizeof(sh64)) 150#define xsh_size (size_t)(clazz == ELFCLASS32 \ 151 ? elf_getu32(swap, sh32.sh_size) \ 152 : elf_getu64(swap, sh64.sh_size)) 153#define xsh_offset (off_t)(clazz == ELFCLASS32 \ 154 ? elf_getu32(swap, sh32.sh_offset) \ 155 : elf_getu64(swap, sh64.sh_offset)) 156#define xsh_type (clazz == ELFCLASS32 \ 157 ? elf_getu32(swap, sh32.sh_type) \ 158 : elf_getu32(swap, sh64.sh_type)) 159#define xsh_name (clazz == ELFCLASS32 \ 160 ? elf_getu32(swap, sh32.sh_name) \ 161 : elf_getu32(swap, sh64.sh_name)) 162#define xph_addr (clazz == ELFCLASS32 \ 163 ? (void *) &ph32 \ 164 : (void *) &ph64) 165#define xph_sizeof (clazz == ELFCLASS32 \ 166 ? sizeof(ph32) \ 167 : sizeof(ph64)) 168#define xph_type (clazz == ELFCLASS32 \ 169 ? elf_getu32(swap, ph32.p_type) \ 170 : elf_getu32(swap, ph64.p_type)) 171#define xph_offset (off_t)(clazz == ELFCLASS32 \ 172 ? elf_getu32(swap, ph32.p_offset) \ 173 : elf_getu64(swap, ph64.p_offset)) 174#define xph_align (size_t)((clazz == ELFCLASS32 \ 175 ? (off_t) (ph32.p_align ? \ 176 elf_getu32(swap, ph32.p_align) : 4) \ 177 : (off_t) (ph64.p_align ? \ 178 elf_getu64(swap, ph64.p_align) : 4))) 179#define xph_filesz (size_t)((clazz == ELFCLASS32 \ 180 ? elf_getu32(swap, ph32.p_filesz) \ 181 : elf_getu64(swap, ph64.p_filesz))) 182#define xnh_addr (clazz == ELFCLASS32 \ 183 ? (void *)&nh32 \ 184 : (void *)&nh64) 185#define xph_memsz (size_t)((clazz == ELFCLASS32 \ 186 ? elf_getu32(swap, ph32.p_memsz) \ 187 : elf_getu64(swap, ph64.p_memsz))) 188#define xnh_sizeof (clazz == ELFCLASS32 \ 189 ? sizeof nh32 \ 190 : sizeof nh64) 191#define xnh_type (clazz == ELFCLASS32 \ 192 ? elf_getu32(swap, nh32.n_type) \ 193 : elf_getu32(swap, nh64.n_type)) 194#define xnh_namesz (clazz == ELFCLASS32 \ 195 ? elf_getu32(swap, nh32.n_namesz) \ 196 : elf_getu32(swap, nh64.n_namesz)) 197#define xnh_descsz (clazz == ELFCLASS32 \ 198 ? elf_getu32(swap, nh32.n_descsz) \ 199 : elf_getu32(swap, nh64.n_descsz)) 200#define prpsoffsets(i) (clazz == ELFCLASS32 \ 201 ? prpsoffsets32[i] \ 202 : prpsoffsets64[i]) 203#define xcap_addr (clazz == ELFCLASS32 \ 204 ? (void *)&cap32 \ 205 : (void *)&cap64) 206#define xcap_sizeof (clazz == ELFCLASS32 \ 207 ? sizeof cap32 \ 208 : sizeof cap64) 209#define xcap_tag (clazz == ELFCLASS32 \ 210 ? elf_getu32(swap, cap32.c_tag) \ 211 : elf_getu64(swap, cap64.c_tag)) 212#define xcap_val (clazz == ELFCLASS32 \ 213 ? elf_getu32(swap, cap32.c_un.c_val) \ 214 : elf_getu64(swap, cap64.c_un.c_val)) 215 216#ifdef ELFCORE 217/* 218 * Try larger offsets first to avoid false matches 219 * from earlier data that happen to look like strings. 220 */ 221static const size_t prpsoffsets32[] = { 222#ifdef USE_NT_PSINFO 223 104, /* SunOS 5.x (command line) */ 224 88, /* SunOS 5.x (short name) */ 225#endif /* USE_NT_PSINFO */ 226 227 100, /* SunOS 5.x (command line) */ 228 84, /* SunOS 5.x (short name) */ 229 230 44, /* Linux (command line) */ 231 28, /* Linux 2.0.36 (short name) */ 232 233 8, /* FreeBSD */ 234}; 235 236static const size_t prpsoffsets64[] = { 237#ifdef USE_NT_PSINFO 238 152, /* SunOS 5.x (command line) */ 239 136, /* SunOS 5.x (short name) */ 240#endif /* USE_NT_PSINFO */ 241 242 136, /* SunOS 5.x, 64-bit (command line) */ 243 120, /* SunOS 5.x, 64-bit (short name) */ 244 245 56, /* Linux (command line) */ 246 40, /* Linux (tested on core from 2.4.x, short name) */ 247 248 16, /* FreeBSD, 64-bit */ 249}; 250 251#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) 252#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) 253 254#define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) 255 256/* 257 * Look through the program headers of an executable image, searching 258 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or 259 * "FreeBSD"; if one is found, try looking in various places in its 260 * contents for a 16-character string containing only printable 261 * characters - if found, that string should be the name of the program 262 * that dropped core. Note: right after that 16-character string is, 263 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and 264 * Linux, a longer string (80 characters, in 5.x, probably other 265 * SVR4-flavored systems, and Linux) containing the start of the 266 * command line for that program. 267 * 268 * SunOS 5.x core files contain two PT_NOTE sections, with the types 269 * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the 270 * same info about the command name and command line, so it probably 271 * isn't worthwhile to look for NT_PSINFO, but the offsets are provided 272 * above (see USE_NT_PSINFO), in case we ever decide to do so. The 273 * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent; 274 * the SunOS 5.x file command relies on this (and prefers the latter). 275 * 276 * The signal number probably appears in a section of type NT_PRSTATUS, 277 * but that's also rather OS-dependent, in ways that are harder to 278 * dissect with heuristics, so I'm not bothering with the signal number. 279 * (I suppose the signal number could be of interest in situations where 280 * you don't have the binary of the program that dropped core; if you 281 * *do* have that binary, the debugger will probably tell you what 282 * signal it was.) 283 */ 284 285#define OS_STYLE_SVR4 0 286#define OS_STYLE_FREEBSD 1 287#define OS_STYLE_NETBSD 2 288 289private const char os_style_names[][8] = { 290 "SVR4", 291 "FreeBSD", 292 "NetBSD", 293}; 294 295#define FLAGS_DID_CORE 0x01 296#define FLAGS_DID_NOTE 0x02 297#define FLAGS_DID_BUILD_ID 0x04 298#define FLAGS_DID_CORE_STYLE 0x08 299#define FLAGS_IS_CORE 0x10 300 301private int 302dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 303 int num, size_t size, off_t fsize, int *flags) 304{ 305 Elf32_Phdr ph32; 306 Elf64_Phdr ph64; 307 size_t offset, len; 308 unsigned char nbuf[BUFSIZ]; 309 ssize_t bufsize; 310 311 if (size != xph_sizeof) { 312 if (file_printf(ms, ", corrupted program header size") == -1) 313 return -1; 314 return 0; 315 } 316 317 /* 318 * Loop through all the program headers. 319 */ 320 for ( ; num; num--) { 321 if (pread(fd, xph_addr, xph_sizeof, off) == -1) { 322 file_badread(ms); 323 return -1; 324 } 325 off += size; 326 327 if (xph_offset > fsize) { 328 /* Perhaps warn here */ 329 continue; 330 } 331 332 if (xph_type != PT_NOTE) 333 continue; 334 335 /* 336 * This is a PT_NOTE section; loop through all the notes 337 * in the section. 338 */ 339 len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf); 340 if ((bufsize = pread(fd, nbuf, len, xph_offset)) == -1) { 341 file_badread(ms); 342 return -1; 343 } 344 offset = 0; 345 for (;;) { 346 if (offset >= (size_t)bufsize) 347 break; 348 offset = donote(ms, nbuf, offset, (size_t)bufsize, 349 clazz, swap, 4, flags); 350 if (offset == 0) 351 break; 352 353 } 354 } 355 return 0; 356} 357#endif 358 359static void 360do_note_netbsd_version(struct magic_set *ms, int swap, void *v) 361{ 362 uint32_t desc; 363 (void)memcpy(&desc, v, sizeof(desc)); 364 desc = elf_getu32(swap, desc); 365 366 if (file_printf(ms, ", for NetBSD") == -1) 367 return; 368 /* 369 * The version number used to be stuck as 199905, and was thus 370 * basically content-free. Newer versions of NetBSD have fixed 371 * this and now use the encoding of __NetBSD_Version__: 372 * 373 * MMmmrrpp00 374 * 375 * M = major version 376 * m = minor version 377 * r = release ["",A-Z,Z[A-Z] but numeric] 378 * p = patchlevel 379 */ 380 if (desc > 100000000U) { 381 uint32_t ver_patch = (desc / 100) % 100; 382 uint32_t ver_rel = (desc / 10000) % 100; 383 uint32_t ver_min = (desc / 1000000) % 100; 384 uint32_t ver_maj = desc / 100000000; 385 386 if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1) 387 return; 388 if (ver_rel == 0 && ver_patch != 0) { 389 if (file_printf(ms, ".%u", ver_patch) == -1) 390 return; 391 } else if (ver_rel != 0) { 392 while (ver_rel > 26) { 393 if (file_printf(ms, "Z") == -1) 394 return; 395 ver_rel -= 26; 396 } 397 if (file_printf(ms, "%c", 'A' + ver_rel - 1) 398 == -1) 399 return; 400 } 401 } 402} 403 404static void 405do_note_freebsd_version(struct magic_set *ms, int swap, void *v) 406{ 407 uint32_t desc; 408 409 (void)memcpy(&desc, v, sizeof(desc)); 410 desc = elf_getu32(swap, desc); 411 if (file_printf(ms, ", for FreeBSD") == -1) 412 return; 413 414 /* 415 * Contents is __FreeBSD_version, whose relation to OS 416 * versions is defined by a huge table in the Porter's 417 * Handbook. This is the general scheme: 418 * 419 * Releases: 420 * Mmp000 (before 4.10) 421 * Mmi0p0 (before 5.0) 422 * Mmm0p0 423 * 424 * Development branches: 425 * Mmpxxx (before 4.6) 426 * Mmp1xx (before 4.10) 427 * Mmi1xx (before 5.0) 428 * M000xx (pre-M.0) 429 * Mmm1xx 430 * 431 * M = major version 432 * m = minor version 433 * i = minor version increment (491000 -> 4.10) 434 * p = patchlevel 435 * x = revision 436 * 437 * The first release of FreeBSD to use ELF by default 438 * was version 3.0. 439 */ 440 if (desc == 460002) { 441 if (file_printf(ms, " 4.6.2") == -1) 442 return; 443 } else if (desc < 460100) { 444 if (file_printf(ms, " %d.%d", desc / 100000, 445 desc / 10000 % 10) == -1) 446 return; 447 if (desc / 1000 % 10 > 0) 448 if (file_printf(ms, ".%d", desc / 1000 % 10) == -1) 449 return; 450 if ((desc % 1000 > 0) || (desc % 100000 == 0)) 451 if (file_printf(ms, " (%d)", desc) == -1) 452 return; 453 } else if (desc < 500000) { 454 if (file_printf(ms, " %d.%d", desc / 100000, 455 desc / 10000 % 10 + desc / 1000 % 10) == -1) 456 return; 457 if (desc / 100 % 10 > 0) { 458 if (file_printf(ms, " (%d)", desc) == -1) 459 return; 460 } else if (desc / 10 % 10 > 0) { 461 if (file_printf(ms, ".%d", desc / 10 % 10) == -1) 462 return; 463 } 464 } else { 465 if (file_printf(ms, " %d.%d", desc / 100000, 466 desc / 1000 % 100) == -1) 467 return; 468 if ((desc / 100 % 10 > 0) || 469 (desc % 100000 / 100 == 0)) { 470 if (file_printf(ms, " (%d)", desc) == -1) 471 return; 472 } else if (desc / 10 % 10 > 0) { 473 if (file_printf(ms, ".%d", desc / 10 % 10) == -1) 474 return; 475 } 476 } 477} 478 479private size_t 480donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, 481 int clazz, int swap, size_t align, int *flags) 482{ 483 Elf32_Nhdr nh32; 484 Elf64_Nhdr nh64; 485 size_t noff, doff; 486#ifdef ELFCORE 487 int os_style = -1; 488#endif 489 uint32_t namesz, descsz; 490 unsigned char *nbuf = CAST(unsigned char *, vbuf); 491 492 if (xnh_sizeof + offset > size) { 493 /* 494 * We're out of note headers. 495 */ 496 return xnh_sizeof + offset; 497 } 498 499 (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof); 500 offset += xnh_sizeof; 501 502 namesz = xnh_namesz; 503 descsz = xnh_descsz; 504 if ((namesz == 0) && (descsz == 0)) { 505 /* 506 * We're out of note headers. 507 */ 508 return (offset >= size) ? offset : size; 509 } 510 511 if (namesz & 0x80000000) { 512 (void)file_printf(ms, ", bad note name size 0x%lx", 513 (unsigned long)namesz); 514 return 0; 515 } 516 517 if (descsz & 0x80000000) { 518 (void)file_printf(ms, ", bad note description size 0x%lx", 519 (unsigned long)descsz); 520 return 0; 521 } 522 523 524 noff = offset; 525 doff = ELF_ALIGN(offset + namesz); 526 527 if (offset + namesz > size) { 528 /* 529 * We're past the end of the buffer. 530 */ 531 return doff; 532 } 533 534 offset = ELF_ALIGN(doff + descsz); 535 if (doff + descsz > size) { 536 /* 537 * We're past the end of the buffer. 538 */ 539 return (offset >= size) ? offset : size; 540 } 541 542 if ((*flags & (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) == 543 (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) 544 goto core; 545 546 if (namesz == 5 && strcmp((char *)&nbuf[noff], "SuSE") == 0 && 547 xnh_type == NT_GNU_VERSION && descsz == 2) { 548 file_printf(ms, ", for SuSE %d.%d", nbuf[doff], nbuf[doff + 1]); 549 } 550 if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && 551 xnh_type == NT_GNU_VERSION && descsz == 16) { 552 uint32_t desc[4]; 553 (void)memcpy(desc, &nbuf[doff], sizeof(desc)); 554 555 if (file_printf(ms, ", for GNU/") == -1) 556 return size; 557 switch (elf_getu32(swap, desc[0])) { 558 case GNU_OS_LINUX: 559 if (file_printf(ms, "Linux") == -1) 560 return size; 561 break; 562 case GNU_OS_HURD: 563 if (file_printf(ms, "Hurd") == -1) 564 return size; 565 break; 566 case GNU_OS_SOLARIS: 567 if (file_printf(ms, "Solaris") == -1) 568 return size; 569 break; 570 case GNU_OS_KFREEBSD: 571 if (file_printf(ms, "kFreeBSD") == -1) 572 return size; 573 break; 574 case GNU_OS_KNETBSD: 575 if (file_printf(ms, "kNetBSD") == -1) 576 return size; 577 break; 578 default: 579 if (file_printf(ms, "<unknown>") == -1) 580 return size; 581 } 582 if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]), 583 elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1) 584 return size; 585 *flags |= FLAGS_DID_NOTE; 586 return size; 587 } 588 589 if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && 590 xnh_type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) { 591 uint8_t desc[20]; 592 uint32_t i; 593 if (file_printf(ms, ", BuildID[%s]=", descsz == 16 ? "md5/uuid" : 594 "sha1") == -1) 595 return size; 596 (void)memcpy(desc, &nbuf[doff], descsz); 597 for (i = 0; i < descsz; i++) 598 if (file_printf(ms, "%02x", desc[i]) == -1) 599 return size; 600 *flags |= FLAGS_DID_BUILD_ID; 601 } 602 603 if (namesz == 4 && strcmp((char *)&nbuf[noff], "PaX") == 0 && 604 xnh_type == NT_NETBSD_PAX && descsz == 4) { 605 static const char *pax[] = { 606 "+mprotect", 607 "-mprotect", 608 "+segvguard", 609 "-segvguard", 610 "+ASLR", 611 "-ASLR", 612 }; 613 uint32_t desc; 614 size_t i; 615 int did = 0; 616 617 (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); 618 desc = elf_getu32(swap, desc); 619 620 if (desc && file_printf(ms, ", PaX: ") == -1) 621 return size; 622 623 for (i = 0; i < __arraycount(pax); i++) { 624 if (((1 << i) & desc) == 0) 625 continue; 626 if (file_printf(ms, "%s%s", did++ ? "," : "", 627 pax[i]) == -1) 628 return size; 629 } 630 } 631 632 if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) { 633 switch (xnh_type) { 634 case NT_NETBSD_VERSION: 635 if (descsz == 4) { 636 do_note_netbsd_version(ms, swap, &nbuf[doff]); 637 *flags |= FLAGS_DID_NOTE; 638 return size; 639 } 640 break; 641 case NT_NETBSD_MARCH: 642 if (file_printf(ms, ", compiled for: %.*s", (int)descsz, 643 (const char *)&nbuf[doff]) == -1) 644 return size; 645 break; 646 case NT_NETBSD_CMODEL: 647 if (file_printf(ms, ", compiler model: %.*s", 648 (int)descsz, (const char *)&nbuf[doff]) == -1) 649 return size; 650 break; 651 default: 652 if (file_printf(ms, ", note=%u", xnh_type) == -1) 653 return size; 654 break; 655 } 656 return size; 657 } 658 659 if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0) { 660 if (xnh_type == NT_FREEBSD_VERSION && descsz == 4) { 661 do_note_freebsd_version(ms, swap, &nbuf[doff]); 662 *flags |= FLAGS_DID_NOTE; 663 return size; 664 } 665 } 666 667 if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 && 668 xnh_type == NT_OPENBSD_VERSION && descsz == 4) { 669 if (file_printf(ms, ", for OpenBSD") == -1) 670 return size; 671 /* Content of note is always 0 */ 672 *flags |= FLAGS_DID_NOTE; 673 return size; 674 } 675 676 if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 && 677 xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) { 678 uint32_t desc; 679 if (file_printf(ms, ", for DragonFly") == -1) 680 return size; 681 (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); 682 desc = elf_getu32(swap, desc); 683 if (file_printf(ms, " %d.%d.%d", desc / 100000, 684 desc / 10000 % 10, desc % 10000) == -1) 685 return size; 686 *flags |= FLAGS_DID_NOTE; 687 return size; 688 } 689 690core: 691 /* 692 * Sigh. The 2.0.36 kernel in Debian 2.1, at 693 * least, doesn't correctly implement name 694 * sections, in core dumps, as specified by 695 * the "Program Linking" section of "UNIX(R) System 696 * V Release 4 Programmer's Guide: ANSI C and 697 * Programming Support Tools", because my copy 698 * clearly says "The first 'namesz' bytes in 'name' 699 * contain a *null-terminated* [emphasis mine] 700 * character representation of the entry's owner 701 * or originator", but the 2.0.36 kernel code 702 * doesn't include the terminating null in the 703 * name.... 704 */ 705 if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) || 706 (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) { 707 os_style = OS_STYLE_SVR4; 708 } 709 710 if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) { 711 os_style = OS_STYLE_FREEBSD; 712 } 713 714 if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11) 715 == 0)) { 716 os_style = OS_STYLE_NETBSD; 717 } 718 719#ifdef ELFCORE 720 if ((*flags & FLAGS_DID_CORE) != 0) 721 return size; 722 723 if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) { 724 if (file_printf(ms, ", %s-style", os_style_names[os_style]) 725 == -1) 726 return size; 727 *flags |= FLAGS_DID_CORE_STYLE; 728 } 729 730 switch (os_style) { 731 case OS_STYLE_NETBSD: 732 if (xnh_type == NT_NETBSD_CORE_PROCINFO) { 733 uint32_t signo; 734 /* 735 * Extract the program name. It is at 736 * offset 0x7c, and is up to 32-bytes, 737 * including the terminating NUL. 738 */ 739 if (file_printf(ms, ", from '%.31s'", 740 &nbuf[doff + 0x7c]) == -1) 741 return size; 742 743 /* 744 * Extract the signal number. It is at 745 * offset 0x08. 746 */ 747 (void)memcpy(&signo, &nbuf[doff + 0x08], 748 sizeof(signo)); 749 if (file_printf(ms, " (signal %u)", 750 elf_getu32(swap, signo)) == -1) 751 return size; 752 *flags |= FLAGS_DID_CORE; 753 return size; 754 } 755 break; 756 757 default: 758 if (xnh_type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) { 759 size_t i, j; 760 unsigned char c; 761 /* 762 * Extract the program name. We assume 763 * it to be 16 characters (that's what it 764 * is in SunOS 5.x and Linux). 765 * 766 * Unfortunately, it's at a different offset 767 * in various OSes, so try multiple offsets. 768 * If the characters aren't all printable, 769 * reject it. 770 */ 771 for (i = 0; i < NOFFSETS; i++) { 772 unsigned char *cname, *cp; 773 size_t reloffset = prpsoffsets(i); 774 size_t noffset = doff + reloffset; 775 size_t k; 776 for (j = 0; j < 16; j++, noffset++, 777 reloffset++) { 778 /* 779 * Make sure we're not past 780 * the end of the buffer; if 781 * we are, just give up. 782 */ 783 if (noffset >= size) 784 goto tryanother; 785 786 /* 787 * Make sure we're not past 788 * the end of the contents; 789 * if we are, this obviously 790 * isn't the right offset. 791 */ 792 if (reloffset >= descsz) 793 goto tryanother; 794 795 c = nbuf[noffset]; 796 if (c == '\0') { 797 /* 798 * A '\0' at the 799 * beginning is 800 * obviously wrong. 801 * Any other '\0' 802 * means we're done. 803 */ 804 if (j == 0) 805 goto tryanother; 806 else 807 break; 808 } else { 809 /* 810 * A nonprintable 811 * character is also 812 * wrong. 813 */ 814 if (!isprint(c) || isquote(c)) 815 goto tryanother; 816 } 817 } 818 /* 819 * Well, that worked. 820 */ 821 822 /* 823 * Try next offsets, in case this match is 824 * in the middle of a string. 825 */ 826 for (k = i + 1 ; k < NOFFSETS ; k++) { 827 size_t no; 828 int adjust = 1; 829 if (prpsoffsets(k) >= prpsoffsets(i)) 830 continue; 831 for (no = doff + prpsoffsets(k); 832 no < doff + prpsoffsets(i); no++) 833 adjust = adjust 834 && isprint(nbuf[no]); 835 if (adjust) 836 i = k; 837 } 838 839 cname = (unsigned char *) 840 &nbuf[doff + prpsoffsets(i)]; 841 for (cp = cname; *cp && isprint(*cp); cp++) 842 continue; 843 /* 844 * Linux apparently appends a space at the end 845 * of the command line: remove it. 846 */ 847 while (cp > cname && isspace(cp[-1])) 848 cp--; 849 if (file_printf(ms, ", from '%.*s'", 850 (int)(cp - cname), cname) == -1) 851 return size; 852 *flags |= FLAGS_DID_CORE; 853 return size; 854 855 tryanother: 856 ; 857 } 858 } 859 break; 860 } 861#endif 862 return offset; 863} 864 865/* SunOS 5.x hardware capability descriptions */ 866typedef struct cap_desc { 867 uint64_t cd_mask; 868 const char *cd_name; 869} cap_desc_t; 870 871static const cap_desc_t cap_desc_sparc[] = { 872 { AV_SPARC_MUL32, "MUL32" }, 873 { AV_SPARC_DIV32, "DIV32" }, 874 { AV_SPARC_FSMULD, "FSMULD" }, 875 { AV_SPARC_V8PLUS, "V8PLUS" }, 876 { AV_SPARC_POPC, "POPC" }, 877 { AV_SPARC_VIS, "VIS" }, 878 { AV_SPARC_VIS2, "VIS2" }, 879 { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" }, 880 { AV_SPARC_FMAF, "FMAF" }, 881 { AV_SPARC_FJFMAU, "FJFMAU" }, 882 { AV_SPARC_IMA, "IMA" }, 883 { 0, NULL } 884}; 885 886static const cap_desc_t cap_desc_386[] = { 887 { AV_386_FPU, "FPU" }, 888 { AV_386_TSC, "TSC" }, 889 { AV_386_CX8, "CX8" }, 890 { AV_386_SEP, "SEP" }, 891 { AV_386_AMD_SYSC, "AMD_SYSC" }, 892 { AV_386_CMOV, "CMOV" }, 893 { AV_386_MMX, "MMX" }, 894 { AV_386_AMD_MMX, "AMD_MMX" }, 895 { AV_386_AMD_3DNow, "AMD_3DNow" }, 896 { AV_386_AMD_3DNowx, "AMD_3DNowx" }, 897 { AV_386_FXSR, "FXSR" }, 898 { AV_386_SSE, "SSE" }, 899 { AV_386_SSE2, "SSE2" }, 900 { AV_386_PAUSE, "PAUSE" }, 901 { AV_386_SSE3, "SSE3" }, 902 { AV_386_MON, "MON" }, 903 { AV_386_CX16, "CX16" }, 904 { AV_386_AHF, "AHF" }, 905 { AV_386_TSCP, "TSCP" }, 906 { AV_386_AMD_SSE4A, "AMD_SSE4A" }, 907 { AV_386_POPCNT, "POPCNT" }, 908 { AV_386_AMD_LZCNT, "AMD_LZCNT" }, 909 { AV_386_SSSE3, "SSSE3" }, 910 { AV_386_SSE4_1, "SSE4.1" }, 911 { AV_386_SSE4_2, "SSE4.2" }, 912 { 0, NULL } 913}; 914 915private int 916doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, 917 size_t size, off_t fsize, int *flags, int mach, int strtab) 918{ 919 Elf32_Shdr sh32; 920 Elf64_Shdr sh64; 921 int stripped = 1; 922 size_t nbadcap = 0; 923 void *nbuf; 924 off_t noff, coff, name_off; 925 uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */ 926 uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */ 927 char name[50]; 928 929 if (size != xsh_sizeof) { 930 if (file_printf(ms, ", corrupted section header size") == -1) 931 return -1; 932 return 0; 933 } 934 935 /* Read offset of name section to be able to read section names later */ 936 if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) == -1) { 937 file_badread(ms); 938 return -1; 939 } 940 name_off = xsh_offset; 941 942 for ( ; num; num--) { 943 /* Read the name of this section. */ 944 if (pread(fd, name, sizeof(name), name_off + xsh_name) == -1) { 945 file_badread(ms); 946 return -1; 947 } 948 name[sizeof(name) - 1] = '\0'; 949 if (strcmp(name, ".debug_info") == 0) 950 stripped = 0; 951 952 if (pread(fd, xsh_addr, xsh_sizeof, off) == -1) { 953 file_badread(ms); 954 return -1; 955 } 956 off += size; 957 958 /* Things we can determine before we seek */ 959 switch (xsh_type) { 960 case SHT_SYMTAB: 961#if 0 962 case SHT_DYNSYM: 963#endif 964 stripped = 0; 965 break; 966 default: 967 if (xsh_offset > fsize) { 968 /* Perhaps warn here */ 969 continue; 970 } 971 break; 972 } 973 974 /* Things we can determine when we seek */ 975 switch (xsh_type) { 976 case SHT_NOTE: 977 if ((nbuf = malloc(xsh_size)) == NULL) { 978 file_error(ms, errno, "Cannot allocate memory" 979 " for note"); 980 return -1; 981 } 982 if (pread(fd, nbuf, xsh_size, xsh_offset) == -1) { 983 file_badread(ms); 984 free(nbuf); 985 return -1; 986 } 987 988 noff = 0; 989 for (;;) { 990 if (noff >= (off_t)xsh_size) 991 break; 992 noff = donote(ms, nbuf, (size_t)noff, 993 xsh_size, clazz, swap, 4, flags); 994 if (noff == 0) 995 break; 996 } 997 free(nbuf); 998 break; 999 case SHT_SUNW_cap: 1000 switch (mach) { 1001 case EM_SPARC: 1002 case EM_SPARCV9: 1003 case EM_IA_64: 1004 case EM_386: 1005 case EM_AMD64: 1006 break; 1007 default: 1008 goto skip; 1009 } 1010 1011 if (nbadcap > 5) 1012 break; 1013 if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) { 1014 file_badseek(ms); 1015 return -1; 1016 } 1017 coff = 0; 1018 for (;;) { 1019 Elf32_Cap cap32; 1020 Elf64_Cap cap64; 1021 char cbuf[/*CONSTCOND*/ 1022 MAX(sizeof cap32, sizeof cap64)]; 1023 if ((coff += xcap_sizeof) > (off_t)xsh_size) 1024 break; 1025 if (read(fd, cbuf, (size_t)xcap_sizeof) != 1026 (ssize_t)xcap_sizeof) { 1027 file_badread(ms); 1028 return -1; 1029 } 1030 if (cbuf[0] == 'A') { 1031#ifdef notyet 1032 char *p = cbuf + 1; 1033 uint32_t len, tag; 1034 memcpy(&len, p, sizeof(len)); 1035 p += 4; 1036 len = getu32(swap, len); 1037 if (memcmp("gnu", p, 3) != 0) { 1038 if (file_printf(ms, 1039 ", unknown capability %.3s", p) 1040 == -1) 1041 return -1; 1042 break; 1043 } 1044 p += strlen(p) + 1; 1045 tag = *p++; 1046 memcpy(&len, p, sizeof(len)); 1047 p += 4; 1048 len = getu32(swap, len); 1049 if (tag != 1) { 1050 if (file_printf(ms, ", unknown gnu" 1051 " capability tag %d", tag) 1052 == -1) 1053 return -1; 1054 break; 1055 } 1056 // gnu attributes 1057#endif 1058 break; 1059 } 1060 (void)memcpy(xcap_addr, cbuf, xcap_sizeof); 1061 switch (xcap_tag) { 1062 case CA_SUNW_NULL: 1063 break; 1064 case CA_SUNW_HW_1: 1065 cap_hw1 |= xcap_val; 1066 break; 1067 case CA_SUNW_SF_1: 1068 cap_sf1 |= xcap_val; 1069 break; 1070 default: 1071 if (file_printf(ms, 1072 ", with unknown capability " 1073 "0x%" INT64_T_FORMAT "x = 0x%" 1074 INT64_T_FORMAT "x", 1075 (unsigned long long)xcap_tag, 1076 (unsigned long long)xcap_val) == -1) 1077 return -1; 1078 if (nbadcap++ > 2) 1079 coff = xsh_size; 1080 break; 1081 } 1082 } 1083 /*FALLTHROUGH*/ 1084 skip: 1085 default: 1086 break; 1087 } 1088 } 1089 1090 if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) 1091 return -1; 1092 if (cap_hw1) { 1093 const cap_desc_t *cdp; 1094 switch (mach) { 1095 case EM_SPARC: 1096 case EM_SPARC32PLUS: 1097 case EM_SPARCV9: 1098 cdp = cap_desc_sparc; 1099 break; 1100 case EM_386: 1101 case EM_IA_64: 1102 case EM_AMD64: 1103 cdp = cap_desc_386; 1104 break; 1105 default: 1106 cdp = NULL; 1107 break; 1108 } 1109 if (file_printf(ms, ", uses") == -1) 1110 return -1; 1111 if (cdp) { 1112 while (cdp->cd_name) { 1113 if (cap_hw1 & cdp->cd_mask) { 1114 if (file_printf(ms, 1115 " %s", cdp->cd_name) == -1) 1116 return -1; 1117 cap_hw1 &= ~cdp->cd_mask; 1118 } 1119 ++cdp; 1120 } 1121 if (cap_hw1) 1122 if (file_printf(ms, 1123 " unknown hardware capability 0x%" 1124 INT64_T_FORMAT "x", 1125 (unsigned long long)cap_hw1) == -1) 1126 return -1; 1127 } else { 1128 if (file_printf(ms, 1129 " hardware capability 0x%" INT64_T_FORMAT "x", 1130 (unsigned long long)cap_hw1) == -1) 1131 return -1; 1132 } 1133 } 1134 if (cap_sf1) { 1135 if (cap_sf1 & SF1_SUNW_FPUSED) { 1136 if (file_printf(ms, 1137 (cap_sf1 & SF1_SUNW_FPKNWN) 1138 ? ", uses frame pointer" 1139 : ", not known to use frame pointer") == -1) 1140 return -1; 1141 } 1142 cap_sf1 &= ~SF1_SUNW_MASK; 1143 if (cap_sf1) 1144 if (file_printf(ms, 1145 ", with unknown software capability 0x%" 1146 INT64_T_FORMAT "x", 1147 (unsigned long long)cap_sf1) == -1) 1148 return -1; 1149 } 1150 return 0; 1151} 1152 1153/* 1154 * Look through the program headers of an executable image, searching 1155 * for a PT_INTERP section; if one is found, it's dynamically linked, 1156 * otherwise it's statically linked. 1157 */ 1158private int 1159dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, 1160 int num, size_t size, off_t fsize, int *flags, int sh_num) 1161{ 1162 Elf32_Phdr ph32; 1163 Elf64_Phdr ph64; 1164 const char *linking_style = "statically"; 1165 const char *shared_libraries = ""; 1166 unsigned char nbuf[BUFSIZ]; 1167 ssize_t bufsize; 1168 size_t offset, align, len; 1169 1170 if (size != xph_sizeof) { 1171 if (file_printf(ms, ", corrupted program header size") == -1) 1172 return -1; 1173 return 0; 1174 } 1175 1176 for ( ; num; num--) { 1177 if (pread(fd, xph_addr, xph_sizeof, off) == -1) { 1178 file_badread(ms); 1179 return -1; 1180 } 1181 1182 off += size; 1183 1184 /* Things we can determine before we seek */ 1185 switch (xph_type) { 1186 case PT_DYNAMIC: 1187 linking_style = "dynamically"; 1188 break; 1189 case PT_INTERP: 1190 shared_libraries = " (uses shared libs)"; 1191 break; 1192 default: 1193 if (xph_offset > fsize) { 1194 /* Maybe warn here? */ 1195 continue; 1196 } 1197 break; 1198 } 1199 1200 /* Things we can determine when we seek */ 1201 switch (xph_type) { 1202 case PT_NOTE: 1203 if ((align = xph_align) & 0x80000000UL) { 1204 if (file_printf(ms, 1205 ", invalid note alignment 0x%lx", 1206 (unsigned long)align) == -1) 1207 return -1; 1208 align = 4; 1209 } 1210 if (sh_num) 1211 break; 1212 /* 1213 * This is a PT_NOTE section; loop through all the notes 1214 * in the section. 1215 */ 1216 len = xph_filesz < sizeof(nbuf) ? xph_filesz 1217 : sizeof(nbuf); 1218 bufsize = pread(fd, nbuf, len, xph_offset); 1219 if (bufsize == -1) { 1220 file_badread(ms); 1221 return -1; 1222 } 1223 offset = 0; 1224 for (;;) { 1225 if (offset >= (size_t)bufsize) 1226 break; 1227 offset = donote(ms, nbuf, offset, 1228 (size_t)bufsize, clazz, swap, align, 1229 flags); 1230 if (offset == 0) 1231 break; 1232 } 1233 break; 1234 default: 1235 break; 1236 } 1237 } 1238 if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries) 1239 == -1) 1240 return -1; 1241 return 0; 1242} 1243 1244 1245protected int 1246file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, 1247 size_t nbytes) 1248{ 1249 union { 1250 int32_t l; 1251 char c[sizeof (int32_t)]; 1252 } u; 1253 int clazz; 1254 int swap; 1255 struct stat st; 1256 off_t fsize; 1257 int flags = 0; 1258 Elf32_Ehdr elf32hdr; 1259 Elf64_Ehdr elf64hdr; 1260 uint16_t type, phnum, shnum; 1261 1262 if (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) 1263 return 0; 1264 /* 1265 * ELF executables have multiple section headers in arbitrary 1266 * file locations and thus file(1) cannot determine it from easily. 1267 * Instead we traverse thru all section headers until a symbol table 1268 * one is found or else the binary is stripped. 1269 * Return immediately if it's not ELF (so we avoid pipe2file unless needed). 1270 */ 1271 if (buf[EI_MAG0] != ELFMAG0 1272 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) 1273 || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) 1274 return 0; 1275 1276 /* 1277 * If we cannot seek, it must be a pipe, socket or fifo. 1278 */ 1279 if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) 1280 fd = file_pipe2file(ms, fd, buf, nbytes); 1281 1282 if (fstat(fd, &st) == -1) { 1283 file_badread(ms); 1284 return -1; 1285 } 1286 fsize = st.st_size; 1287 1288 clazz = buf[EI_CLASS]; 1289 1290 switch (clazz) { 1291 case ELFCLASS32: 1292#undef elf_getu 1293#define elf_getu(a, b) elf_getu32(a, b) 1294#undef elfhdr 1295#define elfhdr elf32hdr 1296#include "elfclass.h" 1297 case ELFCLASS64: 1298#undef elf_getu 1299#define elf_getu(a, b) elf_getu64(a, b) 1300#undef elfhdr 1301#define elfhdr elf64hdr 1302#include "elfclass.h" 1303 default: 1304 if (file_printf(ms, ", unknown class %d", clazz) == -1) 1305 return -1; 1306 break; 1307 } 1308 return 0; 1309} 1310#endif 1311