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