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