readelf.c revision 159764
1133359Sobrien/* 2133359Sobrien * Copyright (c) Christos Zoulas 2003. 3133359Sobrien * All Rights Reserved. 4133359Sobrien * 5133359Sobrien * Redistribution and use in source and binary forms, with or without 6133359Sobrien * modification, are permitted provided that the following conditions 7133359Sobrien * are met: 8133359Sobrien * 1. Redistributions of source code must retain the above copyright 9133359Sobrien * notice immediately at the beginning of the file, without modification, 10133359Sobrien * this list of conditions, and the following disclaimer. 11133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright 12133359Sobrien * notice, this list of conditions and the following disclaimer in the 13133359Sobrien * documentation and/or other materials provided with the distribution. 14133359Sobrien * 15133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25133359Sobrien * SUCH DAMAGE. 26133359Sobrien */ 2768349Sobrien#include "file.h" 2868349Sobrien 2968349Sobrien#ifdef BUILTIN_ELF 3068349Sobrien#include <string.h> 3168349Sobrien#include <ctype.h> 3268349Sobrien#include <stdlib.h> 3368349Sobrien#ifdef HAVE_UNISTD_H 3468349Sobrien#include <unistd.h> 3568349Sobrien#endif 3668349Sobrien 3768349Sobrien#include "readelf.h" 3868349Sobrien 3968349Sobrien#ifndef lint 40159764SobrienFILE_RCSID("@(#)$Id: readelf.c,v 1.54 2006/01/13 00:45:21 christos Exp $") 4168349Sobrien#endif 4268349Sobrien 4368349Sobrien#ifdef ELFCORE 44133359Sobrienprivate int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t); 4568349Sobrien#endif 46133359Sobrienprivate int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t); 47133359Sobrienprivate int doshn(struct magic_set *, int, int, int, off_t, int, size_t); 48133359Sobrienprivate size_t donote(struct magic_set *, unsigned char *, size_t, size_t, int, 49159764Sobrien int, size_t, int *); 5068349Sobrien 51133359Sobrien#define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) 5268349Sobrien 53159764Sobrien#define isquote(c) (strchr("'\"`", (c)) != NULL) 54159764Sobrien 55133359Sobrienprivate uint16_t getu16(int, uint16_t); 56133359Sobrienprivate uint32_t getu32(int, uint32_t); 57133359Sobrienprivate uint64_t getu64(int, uint64_t); 58133359Sobrien 59133359Sobrienprivate uint16_t 60103373Sobriengetu16(int swap, uint16_t value) 6168349Sobrien{ 6268349Sobrien union { 6368349Sobrien uint16_t ui; 6468349Sobrien char c[2]; 6568349Sobrien } retval, tmpval; 6668349Sobrien 6768349Sobrien if (swap) { 6868349Sobrien tmpval.ui = value; 6968349Sobrien 7068349Sobrien retval.c[0] = tmpval.c[1]; 7168349Sobrien retval.c[1] = tmpval.c[0]; 7268349Sobrien 7368349Sobrien return retval.ui; 7468349Sobrien } else 7568349Sobrien return value; 7668349Sobrien} 7768349Sobrien 78133359Sobrienprivate uint32_t 79103373Sobriengetu32(int swap, uint32_t value) 8068349Sobrien{ 8168349Sobrien union { 8268349Sobrien uint32_t ui; 8368349Sobrien char c[4]; 8468349Sobrien } retval, tmpval; 8568349Sobrien 8668349Sobrien if (swap) { 8768349Sobrien tmpval.ui = value; 8868349Sobrien 8968349Sobrien retval.c[0] = tmpval.c[3]; 9068349Sobrien retval.c[1] = tmpval.c[2]; 9168349Sobrien retval.c[2] = tmpval.c[1]; 9268349Sobrien retval.c[3] = tmpval.c[0]; 9368349Sobrien 9468349Sobrien return retval.ui; 9568349Sobrien } else 9668349Sobrien return value; 9768349Sobrien} 9868349Sobrien 99133359Sobrienprivate uint64_t 100103373Sobriengetu64(int swap, uint64_t value) 10168349Sobrien{ 10268349Sobrien union { 10368349Sobrien uint64_t ui; 10468349Sobrien char c[8]; 10568349Sobrien } retval, tmpval; 10668349Sobrien 10768349Sobrien if (swap) { 10868349Sobrien tmpval.ui = value; 10968349Sobrien 11068349Sobrien retval.c[0] = tmpval.c[7]; 11168349Sobrien retval.c[1] = tmpval.c[6]; 11268349Sobrien retval.c[2] = tmpval.c[5]; 11368349Sobrien retval.c[3] = tmpval.c[4]; 11468349Sobrien retval.c[4] = tmpval.c[3]; 11568349Sobrien retval.c[5] = tmpval.c[2]; 11668349Sobrien retval.c[6] = tmpval.c[1]; 11768349Sobrien retval.c[7] = tmpval.c[0]; 11868349Sobrien 11968349Sobrien return retval.ui; 12068349Sobrien } else 12168349Sobrien return value; 12268349Sobrien} 12368349Sobrien 124159764Sobrien#ifdef USE_ARRAY_FOR_64BIT_TYPES 125159764Sobrien# define elf_getu64(swap, array) \ 126159764Sobrien ((swap ? ((uint64_t)getu32(swap, array[0])) << 32 : getu32(swap, array[0])) + \ 127159764Sobrien (swap ? getu32(swap, array[1]) : ((uint64_t)getu32(swap, array[1]) << 32))) 128159764Sobrien#else 129159764Sobrien# define elf_getu64(swap, value) getu64(swap, value) 130159764Sobrien#endif 131159764Sobrien 132159764Sobrien#define xsh_addr (class == ELFCLASS32 \ 13368349Sobrien ? (void *) &sh32 \ 13468349Sobrien : (void *) &sh64) 135159764Sobrien#define xsh_sizeof (class == ELFCLASS32 \ 136111658Sobrien ? sizeof sh32 \ 137111658Sobrien : sizeof sh64) 138159764Sobrien#define xsh_size (class == ELFCLASS32 \ 139159764Sobrien ? getu32(swap, sh32.sh_size) \ 140159764Sobrien : getu64(swap, sh64.sh_size)) 141159764Sobrien#define xsh_offset (class == ELFCLASS32 \ 142159764Sobrien ? getu32(swap, sh32.sh_offset) \ 143159764Sobrien : getu64(swap, sh64.sh_offset)) 144159764Sobrien#define xsh_type (class == ELFCLASS32 \ 14568349Sobrien ? getu32(swap, sh32.sh_type) \ 14668349Sobrien : getu32(swap, sh64.sh_type)) 147159764Sobrien#define xph_addr (class == ELFCLASS32 \ 14868349Sobrien ? (void *) &ph32 \ 14968349Sobrien : (void *) &ph64) 150159764Sobrien#define xph_sizeof (class == ELFCLASS32 \ 151111658Sobrien ? sizeof ph32 \ 152111658Sobrien : sizeof ph64) 153159764Sobrien#define xph_type (class == ELFCLASS32 \ 15468349Sobrien ? getu32(swap, ph32.p_type) \ 15568349Sobrien : getu32(swap, ph64.p_type)) 156159764Sobrien#define xph_offset (class == ELFCLASS32 \ 15768349Sobrien ? getu32(swap, ph32.p_offset) \ 15868349Sobrien : getu64(swap, ph64.p_offset)) 159159764Sobrien#define xph_align (size_t)((class == ELFCLASS32 \ 160133359Sobrien ? (off_t) (ph32.p_align ? \ 161133359Sobrien getu32(swap, ph32.p_align) : 4) \ 162133359Sobrien : (off_t) (ph64.p_align ? \ 163133359Sobrien getu64(swap, ph64.p_align) : 4))) 164159764Sobrien#define xph_filesz (size_t)((class == ELFCLASS32 \ 165133359Sobrien ? getu32(swap, ph32.p_filesz) \ 166133359Sobrien : getu64(swap, ph64.p_filesz))) 167159764Sobrien#define xnh_addr (class == ELFCLASS32 \ 168159764Sobrien ? (void *) &nh32 \ 169159764Sobrien : (void *) &nh64) 170159764Sobrien#define xph_memsz (size_t)((class == ELFCLASS32 \ 171133359Sobrien ? getu32(swap, ph32.p_memsz) \ 172133359Sobrien : getu64(swap, ph64.p_memsz))) 173159764Sobrien#define xnh_sizeof (class == ELFCLASS32 \ 174133359Sobrien ? sizeof nh32 \ 175133359Sobrien : sizeof nh64) 176159764Sobrien#define xnh_type (class == ELFCLASS32 \ 177133359Sobrien ? getu32(swap, nh32.n_type) \ 178133359Sobrien : getu32(swap, nh64.n_type)) 179159764Sobrien#define xnh_namesz (class == ELFCLASS32 \ 180133359Sobrien ? getu32(swap, nh32.n_namesz) \ 181133359Sobrien : getu32(swap, nh64.n_namesz)) 182159764Sobrien#define xnh_descsz (class == ELFCLASS32 \ 183133359Sobrien ? getu32(swap, nh32.n_descsz) \ 184133359Sobrien : getu32(swap, nh64.n_descsz)) 18568349Sobrien#define prpsoffsets(i) (class == ELFCLASS32 \ 18668349Sobrien ? prpsoffsets32[i] \ 18768349Sobrien : prpsoffsets64[i]) 18868349Sobrien 18968349Sobrien#ifdef ELFCORE 19068349Sobriensize_t prpsoffsets32[] = { 19168349Sobrien 8, /* FreeBSD */ 19268349Sobrien 28, /* Linux 2.0.36 */ 19368349Sobrien 32, /* Linux (I forget which kernel version) */ 19468349Sobrien 84, /* SunOS 5.x */ 19568349Sobrien}; 19668349Sobrien 19768349Sobriensize_t prpsoffsets64[] = { 198159764Sobrien 16, /* FreeBSD, 64-bit */ 199159764Sobrien 40, /* Linux (tested on core from 2.4.x) */ 20068349Sobrien 120, /* SunOS 5.x, 64-bit */ 20168349Sobrien}; 20268349Sobrien 20368349Sobrien#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) 20468349Sobrien#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) 20568349Sobrien 20668349Sobrien#define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) 20768349Sobrien 20868349Sobrien/* 20968349Sobrien * Look through the program headers of an executable image, searching 21068349Sobrien * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or 21168349Sobrien * "FreeBSD"; if one is found, try looking in various places in its 21268349Sobrien * contents for a 16-character string containing only printable 21368349Sobrien * characters - if found, that string should be the name of the program 21468349Sobrien * that dropped core. Note: right after that 16-character string is, 21568349Sobrien * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and 21668349Sobrien * Linux, a longer string (80 characters, in 5.x, probably other 21768349Sobrien * SVR4-flavored systems, and Linux) containing the start of the 21868349Sobrien * command line for that program. 21968349Sobrien * 22068349Sobrien * The signal number probably appears in a section of type NT_PRSTATUS, 22168349Sobrien * but that's also rather OS-dependent, in ways that are harder to 22268349Sobrien * dissect with heuristics, so I'm not bothering with the signal number. 22368349Sobrien * (I suppose the signal number could be of interest in situations where 22468349Sobrien * you don't have the binary of the program that dropped core; if you 22568349Sobrien * *do* have that binary, the debugger will probably tell you what 22668349Sobrien * signal it was.) 22768349Sobrien */ 228103373Sobrien 229103373Sobrien#define OS_STYLE_SVR4 0 230103373Sobrien#define OS_STYLE_FREEBSD 1 231103373Sobrien#define OS_STYLE_NETBSD 2 232103373Sobrien 233133359Sobrienprivate const char *os_style_names[] = { 234103373Sobrien "SVR4", 235103373Sobrien "FreeBSD", 236103373Sobrien "NetBSD", 237103373Sobrien}; 238103373Sobrien 239159764Sobrien#define FLAGS_DID_CORE 1 240159764Sobrien 241133359Sobrienprivate int 242133359Sobriendophn_core(struct magic_set *ms, int class, int swap, int fd, off_t off, 243133359Sobrien int num, size_t size) 24468349Sobrien{ 24568349Sobrien Elf32_Phdr ph32; 24668349Sobrien Elf64_Phdr ph64; 247133359Sobrien size_t offset; 248133359Sobrien unsigned char nbuf[BUFSIZ]; 249133359Sobrien ssize_t bufsize; 250159764Sobrien int flags = 0; 25168349Sobrien 252159764Sobrien if (size != xph_sizeof) { 253133359Sobrien if (file_printf(ms, ", corrupted program header size") == -1) 254133359Sobrien return -1; 255133359Sobrien return 0; 256133359Sobrien } 257159764Sobrien 25868349Sobrien /* 25968349Sobrien * Loop through all the program headers. 26068349Sobrien */ 26168349Sobrien for ( ; num; num--) { 262133359Sobrien if (lseek(fd, off, SEEK_SET) == (off_t)-1) { 263133359Sobrien file_badseek(ms); 264133359Sobrien return -1; 265133359Sobrien } 266159764Sobrien if (read(fd, xph_addr, xph_sizeof) == -1) { 267133359Sobrien file_badread(ms); 268133359Sobrien return -1; 269133359Sobrien } 27068349Sobrien off += size; 271159764Sobrien if (xph_type != PT_NOTE) 27268349Sobrien continue; 27368349Sobrien 27468349Sobrien /* 27568349Sobrien * This is a PT_NOTE section; loop through all the notes 27668349Sobrien * in the section. 27768349Sobrien */ 278159764Sobrien if (lseek(fd, (off_t)xph_offset, SEEK_SET) == (off_t)-1) { 279133359Sobrien file_badseek(ms); 280133359Sobrien return -1; 281133359Sobrien } 282139368Sobrien bufsize = read(fd, nbuf, 283159764Sobrien ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf))); 284133359Sobrien if (bufsize == -1) { 285133359Sobrien file_badread(ms); 286133359Sobrien return -1; 287133359Sobrien } 28868349Sobrien offset = 0; 28968349Sobrien for (;;) { 290133359Sobrien if (offset >= (size_t)bufsize) 29168349Sobrien break; 292133359Sobrien offset = donote(ms, nbuf, offset, (size_t)bufsize, 293159764Sobrien class, swap, 4, &flags); 294133359Sobrien if (offset == 0) 295133359Sobrien break; 29668349Sobrien 297133359Sobrien } 298133359Sobrien } 299133359Sobrien return 0; 300133359Sobrien} 301133359Sobrien#endif 302133359Sobrien 303133359Sobrienprivate size_t 304133359Sobriendonote(struct magic_set *ms, unsigned char *nbuf, size_t offset, size_t size, 305159764Sobrien int class, int swap, size_t align, int *flags) 306133359Sobrien{ 307133359Sobrien Elf32_Nhdr nh32; 308133359Sobrien Elf64_Nhdr nh64; 309133359Sobrien size_t noff, doff; 310133359Sobrien#ifdef ELFCORE 311133359Sobrien int os_style = -1; 312133359Sobrien#endif 313133359Sobrien uint32_t namesz, descsz; 314133359Sobrien 315159764Sobrien (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof); 316159764Sobrien offset += xnh_sizeof; 317133359Sobrien 318159764Sobrien namesz = xnh_namesz; 319159764Sobrien descsz = xnh_descsz; 320133359Sobrien if ((namesz == 0) && (descsz == 0)) { 321133359Sobrien /* 322133359Sobrien * We're out of note headers. 323133359Sobrien */ 324133359Sobrien return offset; 325133359Sobrien } 326133359Sobrien 327133359Sobrien if (namesz & 0x80000000) { 328133359Sobrien (void)file_printf(ms, ", bad note name size 0x%lx", 329133359Sobrien (unsigned long)namesz); 330133359Sobrien return offset; 331133359Sobrien } 332133359Sobrien 333133359Sobrien if (descsz & 0x80000000) { 334133359Sobrien (void)file_printf(ms, ", bad note description size 0x%lx", 335133359Sobrien (unsigned long)descsz); 336133359Sobrien return offset; 337133359Sobrien } 338133359Sobrien 339133359Sobrien 340133359Sobrien noff = offset; 341133359Sobrien doff = ELF_ALIGN(offset + namesz); 342133359Sobrien 343133359Sobrien if (offset + namesz > size) { 344133359Sobrien /* 345133359Sobrien * We're past the end of the buffer. 346133359Sobrien */ 347133359Sobrien return doff; 348133359Sobrien } 349133359Sobrien 350133359Sobrien offset = ELF_ALIGN(doff + descsz); 351139368Sobrien if (doff + descsz > size) { 352133359Sobrien return offset; 353133359Sobrien } 354133359Sobrien 355133359Sobrien if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && 356159764Sobrien xnh_type == NT_GNU_VERSION && descsz == 16) { 357133359Sobrien uint32_t desc[4]; 358133359Sobrien (void)memcpy(desc, &nbuf[doff], sizeof(desc)); 359133359Sobrien 360133359Sobrien if (file_printf(ms, ", for GNU/") == -1) 361133359Sobrien return size; 362133359Sobrien switch (getu32(swap, desc[0])) { 363133359Sobrien case GNU_OS_LINUX: 364133359Sobrien if (file_printf(ms, "Linux") == -1) 365133359Sobrien return size; 366133359Sobrien break; 367133359Sobrien case GNU_OS_HURD: 368133359Sobrien if (file_printf(ms, "Hurd") == -1) 369133359Sobrien return size; 370133359Sobrien break; 371133359Sobrien case GNU_OS_SOLARIS: 372133359Sobrien if (file_printf(ms, "Solaris") == -1) 373133359Sobrien return size; 374133359Sobrien break; 375133359Sobrien default: 376133359Sobrien if (file_printf(ms, "<unknown>") == -1) 377133359Sobrien return size; 378133359Sobrien } 379133359Sobrien if (file_printf(ms, " %d.%d.%d", getu32(swap, desc[1]), 380133359Sobrien getu32(swap, desc[2]), getu32(swap, desc[3])) == -1) 381133359Sobrien return size; 382133359Sobrien return size; 383133359Sobrien } 384133359Sobrien 385133359Sobrien if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0 && 386159764Sobrien xnh_type == NT_NETBSD_VERSION && descsz == 4) { 387133359Sobrien uint32_t desc; 388133359Sobrien (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); 389133359Sobrien desc = getu32(swap, desc); 390133359Sobrien 391133359Sobrien if (file_printf(ms, ", for NetBSD") == -1) 392133359Sobrien return size; 393133359Sobrien /* 394133359Sobrien * The version number used to be stuck as 199905, and was thus 395133359Sobrien * basically content-free. Newer versions of NetBSD have fixed 396133359Sobrien * this and now use the encoding of __NetBSD_Version__: 397133359Sobrien * 398133359Sobrien * MMmmrrpp00 399133359Sobrien * 400133359Sobrien * M = major version 401133359Sobrien * m = minor version 402133359Sobrien * r = release ["",A-Z,Z[A-Z] but numeric] 403133359Sobrien * p = patchlevel 404133359Sobrien */ 405133359Sobrien if (desc > 100000000U) { 406133359Sobrien u_int ver_patch = (desc / 100) % 100; 407133359Sobrien u_int ver_rel = (desc / 10000) % 100; 408133359Sobrien u_int ver_min = (desc / 1000000) % 100; 409133359Sobrien u_int ver_maj = desc / 100000000; 410133359Sobrien 411133359Sobrien if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1) 412133359Sobrien return size; 413133359Sobrien if (ver_rel == 0 && ver_patch != 0) { 414133359Sobrien if (file_printf(ms, ".%u", ver_patch) == -1) 415133359Sobrien return size; 416133359Sobrien } else if (ver_rel != 0) { 417133359Sobrien while (ver_rel > 26) { 418133359Sobrien file_printf(ms, "Z"); 419133359Sobrien ver_rel -= 26; 420133359Sobrien } 421133359Sobrien file_printf(ms, "%c", 'A' + ver_rel - 1); 42268349Sobrien } 423133359Sobrien } 424133359Sobrien return size; 425133359Sobrien } 42668349Sobrien 427133359Sobrien if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0 && 428159764Sobrien xnh_type == NT_FREEBSD_VERSION && descsz == 4) { 429133359Sobrien uint32_t desc; 430133359Sobrien (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); 431133359Sobrien desc = getu32(swap, desc); 432133359Sobrien if (file_printf(ms, ", for FreeBSD") == -1) 433133359Sobrien return size; 43468349Sobrien 435133359Sobrien /* 436133359Sobrien * Contents is __FreeBSD_version, whose relation to OS 437139368Sobrien * versions is defined by a huge table in the Porter's 438139368Sobrien * Handbook. This is the general scheme: 439139368Sobrien * 440139368Sobrien * Releases: 441139368Sobrien * Mmp000 (before 4.10) 442139368Sobrien * Mmi0p0 (before 5.0) 443139368Sobrien * Mmm0p0 444139368Sobrien * 445139368Sobrien * Development branches: 446139368Sobrien * Mmpxxx (before 4.6) 447139368Sobrien * Mmp1xx (before 4.10) 448139368Sobrien * Mmi1xx (before 5.0) 449139368Sobrien * M000xx (pre-M.0) 450139368Sobrien * Mmm1xx 451139368Sobrien * 452139368Sobrien * M = major version 453139368Sobrien * m = minor version 454139368Sobrien * i = minor version increment (491000 -> 4.10) 455139368Sobrien * p = patchlevel 456139368Sobrien * x = revision 457139368Sobrien * 458139368Sobrien * The first release of FreeBSD to use ELF by default 459139368Sobrien * was version 3.0. 460133359Sobrien */ 461139368Sobrien if (desc == 460002) { 462139368Sobrien if (file_printf(ms, " 4.6.2") == -1) 463139368Sobrien return size; 464139368Sobrien } else if (desc < 460100) { 465139368Sobrien if (file_printf(ms, " %d.%d", desc / 100000, 466139368Sobrien desc / 10000 % 10) == -1) 467139368Sobrien return size; 468139368Sobrien if (desc / 1000 % 10 > 0) 469139368Sobrien if (file_printf(ms, ".%d", desc / 1000 % 10) 470139368Sobrien == -1) 471133359Sobrien return size; 472139368Sobrien if ((desc % 1000 > 0) || (desc % 100000 == 0)) 473139368Sobrien if (file_printf(ms, " (%d)", desc) == -1) 474133359Sobrien return size; 475139368Sobrien } else if (desc < 500000) { 476139368Sobrien if (file_printf(ms, " %d.%d", desc / 100000, 477139368Sobrien desc / 10000 % 10 + desc / 1000 % 10) == -1) 478139368Sobrien return size; 479139368Sobrien if (desc / 100 % 10 > 0) { 480139368Sobrien if (file_printf(ms, " (%d)", desc) == -1) 481139368Sobrien return size; 482139368Sobrien } else if (desc / 10 % 10 > 0) { 483139368Sobrien if (file_printf(ms, ".%d", desc / 10 % 10) 484139368Sobrien == -1) 485139368Sobrien return size; 486103373Sobrien } 487133359Sobrien } else { 488133359Sobrien if (file_printf(ms, " %d.%d", desc / 100000, 489133359Sobrien desc / 1000 % 100) == -1) 490133359Sobrien return size; 491139368Sobrien if ((desc / 100 % 10 > 0) || 492139368Sobrien (desc % 100000 / 100 == 0)) { 493139368Sobrien if (file_printf(ms, " (%d)", desc) == -1) 494133359Sobrien return size; 495139368Sobrien } else if (desc / 10 % 10 > 0) { 496139368Sobrien if (file_printf(ms, ".%d", desc / 10 % 10) 497139368Sobrien == -1) 498133359Sobrien return size; 499133359Sobrien } 500133359Sobrien } 501133359Sobrien return size; 502133359Sobrien } 503103373Sobrien 504133359Sobrien if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 && 505159764Sobrien xnh_type == NT_OPENBSD_VERSION && descsz == 4) { 506133359Sobrien if (file_printf(ms, ", for OpenBSD") == -1) 507133359Sobrien return size; 508133359Sobrien /* Content of note is always 0 */ 509133359Sobrien return size; 510133359Sobrien } 511103373Sobrien 512159764Sobrien if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 && 513159764Sobrien xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) { 514159764Sobrien uint32_t desc; 515159764Sobrien if (file_printf(ms, ", for DragonFly") == -1) 516159764Sobrien return size; 517159764Sobrien (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); 518159764Sobrien desc = getu32(swap, desc); 519159764Sobrien if (file_printf(ms, " %d.%d.%d", desc / 100000, 520159764Sobrien desc / 10000 % 10, desc % 10000) == -1) 521159764Sobrien return size; 522159764Sobrien return size; 523159764Sobrien } 524159764Sobrien 525133359Sobrien /* 526133359Sobrien * Sigh. The 2.0.36 kernel in Debian 2.1, at 527133359Sobrien * least, doesn't correctly implement name 528133359Sobrien * sections, in core dumps, as specified by 529133359Sobrien * the "Program Linking" section of "UNIX(R) System 530133359Sobrien * V Release 4 Programmer's Guide: ANSI C and 531133359Sobrien * Programming Support Tools", because my copy 532133359Sobrien * clearly says "The first 'namesz' bytes in 'name' 533133359Sobrien * contain a *null-terminated* [emphasis mine] 534133359Sobrien * character representation of the entry's owner 535133359Sobrien * or originator", but the 2.0.36 kernel code 536133359Sobrien * doesn't include the terminating null in the 537133359Sobrien * name.... 538133359Sobrien */ 539133359Sobrien if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) || 540133359Sobrien (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) { 541133359Sobrien os_style = OS_STYLE_SVR4; 542133359Sobrien } 543133359Sobrien 544133359Sobrien if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) { 545133359Sobrien os_style = OS_STYLE_FREEBSD; 546133359Sobrien } 547133359Sobrien 548133359Sobrien if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11) 549133359Sobrien == 0)) { 550133359Sobrien os_style = OS_STYLE_NETBSD; 551133359Sobrien } 552133359Sobrien 553133359Sobrien#ifdef ELFCORE 554159764Sobrien if (os_style != -1) { 555159764Sobrien if ((*flags & FLAGS_DID_CORE) == 0) { 556159764Sobrien if (file_printf(ms, ", %s-style", 557159764Sobrien os_style_names[os_style]) == -1) 558159764Sobrien return size; 559159764Sobrien *flags |= FLAGS_DID_CORE; 560159764Sobrien } 561159764Sobrien } 562133359Sobrien 563159764Sobrien switch (os_style) { 564159764Sobrien case OS_STYLE_NETBSD: 565159764Sobrien if (xnh_type == NT_NETBSD_CORE_PROCINFO) { 566159764Sobrien uint32_t signo; 567159764Sobrien /* 568159764Sobrien * Extract the program name. It is at 569159764Sobrien * offset 0x7c, and is up to 32-bytes, 570159764Sobrien * including the terminating NUL. 571159764Sobrien */ 572159764Sobrien if (file_printf(ms, ", from '%.31s'", 573159764Sobrien &nbuf[doff + 0x7c]) == -1) 574159764Sobrien return size; 575159764Sobrien 576159764Sobrien /* 577159764Sobrien * Extract the signal number. It is at 578159764Sobrien * offset 0x08. 579159764Sobrien */ 580159764Sobrien (void)memcpy(&signo, &nbuf[doff + 0x08], 581159764Sobrien sizeof(signo)); 582159764Sobrien if (file_printf(ms, " (signal %u)", 583159764Sobrien getu32(swap, signo)) == -1) 584159764Sobrien return size; 585133359Sobrien return size; 586159764Sobrien } 587159764Sobrien break; 588133359Sobrien 589159764Sobrien default: 590159764Sobrien if (xnh_type == NT_PRPSINFO) { 591159764Sobrien size_t i, j; 592159764Sobrien unsigned char c; 593159764Sobrien /* 594159764Sobrien * Extract the program name. We assume 595159764Sobrien * it to be 16 characters (that's what it 596159764Sobrien * is in SunOS 5.x and Linux). 597159764Sobrien * 598159764Sobrien * Unfortunately, it's at a different offset 599159764Sobrien * in varous OSes, so try multiple offsets. 600159764Sobrien * If the characters aren't all printable, 601159764Sobrien * reject it. 602159764Sobrien */ 603159764Sobrien for (i = 0; i < NOFFSETS; i++) { 604159764Sobrien size_t reloffset = prpsoffsets(i); 605159764Sobrien size_t noffset = doff + reloffset; 606159764Sobrien for (j = 0; j < 16; j++, noffset++, 607159764Sobrien reloffset++) { 60868349Sobrien /* 609159764Sobrien * Make sure we're not past 610159764Sobrien * the end of the buffer; if 611159764Sobrien * we are, just give up. 61268349Sobrien */ 613159764Sobrien if (noffset >= size) 614133359Sobrien goto tryanother; 615159764Sobrien 616133359Sobrien /* 617159764Sobrien * Make sure we're not past 618159764Sobrien * the end of the contents; 619159764Sobrien * if we are, this obviously 620159764Sobrien * isn't the right offset. 621133359Sobrien */ 622159764Sobrien if (reloffset >= descsz) 623133359Sobrien goto tryanother; 624159764Sobrien 625159764Sobrien c = nbuf[noffset]; 626159764Sobrien if (c == '\0') { 627159764Sobrien /* 628159764Sobrien * A '\0' at the 629159764Sobrien * beginning is 630159764Sobrien * obviously wrong. 631159764Sobrien * Any other '\0' 632159764Sobrien * means we're done. 633159764Sobrien */ 634159764Sobrien if (j == 0) 635159764Sobrien goto tryanother; 636159764Sobrien else 637159764Sobrien break; 638159764Sobrien } else { 639159764Sobrien /* 640159764Sobrien * A nonprintable 641159764Sobrien * character is also 642159764Sobrien * wrong. 643159764Sobrien */ 644159764Sobrien if (!isprint(c) || isquote(c)) 645159764Sobrien goto tryanother; 646159764Sobrien } 64768349Sobrien } 648159764Sobrien /* 649159764Sobrien * Well, that worked. 650159764Sobrien */ 651159764Sobrien if (file_printf(ms, ", from '%.16s'", 652159764Sobrien &nbuf[doff + prpsoffsets(i)]) == -1) 653159764Sobrien return size; 654133359Sobrien return size; 655133359Sobrien 656159764Sobrien tryanother: 657159764Sobrien ; 658159764Sobrien } 65968349Sobrien } 660159764Sobrien break; 66168349Sobrien } 662133359Sobrien#endif 663133359Sobrien return offset; 66468349Sobrien} 66568349Sobrien 666133359Sobrienprivate int 667133359Sobriendoshn(struct magic_set *ms, int class, int swap, int fd, off_t off, int num, 668133359Sobrien size_t size) 66968349Sobrien{ 670133359Sobrien Elf32_Shdr sh32; 671133359Sobrien Elf64_Shdr sh64; 672159764Sobrien int stripped = 1; 673159764Sobrien int flags = 0; 674159764Sobrien void *nbuf; 675159764Sobrien off_t noff; 676133359Sobrien 677159764Sobrien if (size != xsh_sizeof) { 678133359Sobrien if (file_printf(ms, ", corrupted section header size") == -1) 679133359Sobrien return -1; 680133359Sobrien return 0; 681133359Sobrien } 682133359Sobrien 683133359Sobrien if (lseek(fd, off, SEEK_SET) == (off_t)-1) { 684133359Sobrien file_badseek(ms); 685133359Sobrien return -1; 686133359Sobrien } 687133359Sobrien 688133359Sobrien for ( ; num; num--) { 689159764Sobrien if (read(fd, xsh_addr, xsh_sizeof) == -1) { 690133359Sobrien file_badread(ms); 691133359Sobrien return -1; 692133359Sobrien } 693159764Sobrien switch (xsh_type) { 694159764Sobrien case SHT_SYMTAB: 695159764Sobrien#if 0 696159764Sobrien case SHT_DYNSYM: 697159764Sobrien#endif 698159764Sobrien stripped = 0; 699159764Sobrien break; 700159764Sobrien case SHT_NOTE: 701159764Sobrien if ((off = lseek(fd, (off_t)0, SEEK_CUR)) == 702159764Sobrien (off_t)-1) { 703159764Sobrien file_badread(ms); 704133359Sobrien return -1; 705159764Sobrien } 706159764Sobrien if ((nbuf = malloc((size_t)xsh_size)) == NULL) { 707159764Sobrien file_error(ms, errno, "Cannot allocate memory" 708159764Sobrien " for note"); 709159764Sobrien return -1; 710159764Sobrien } 711159764Sobrien if ((noff = lseek(fd, (off_t)xsh_offset, SEEK_SET)) == 712159764Sobrien (off_t)-1) { 713159764Sobrien file_badread(ms); 714159764Sobrien free(nbuf); 715159764Sobrien return -1; 716159764Sobrien } 717159764Sobrien if (read(fd, nbuf, (size_t)xsh_size) != 718159764Sobrien (ssize_t)xsh_size) { 719159764Sobrien free(nbuf); 720159764Sobrien file_badread(ms); 721159764Sobrien return -1; 722159764Sobrien } 723159764Sobrien 724159764Sobrien noff = 0; 725159764Sobrien for (;;) { 726159764Sobrien if (noff >= (size_t)xsh_size) 727159764Sobrien break; 728159764Sobrien noff = donote(ms, nbuf, (size_t)noff, 729159764Sobrien (size_t)xsh_size, class, swap, 4, 730159764Sobrien &flags); 731159764Sobrien if (noff == 0) 732159764Sobrien break; 733159764Sobrien } 734159764Sobrien if ((lseek(fd, off, SEEK_SET)) == (off_t)-1) { 735159764Sobrien free(nbuf); 736159764Sobrien file_badread(ms); 737159764Sobrien return -1; 738159764Sobrien } 739159764Sobrien free(nbuf); 740159764Sobrien break; 741133359Sobrien } 742133359Sobrien } 743159764Sobrien if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) 744133359Sobrien return -1; 745133359Sobrien return 0; 746133359Sobrien} 747133359Sobrien 748133359Sobrien/* 749133359Sobrien * Look through the program headers of an executable image, searching 750133359Sobrien * for a PT_INTERP section; if one is found, it's dynamically linked, 751133359Sobrien * otherwise it's statically linked. 752133359Sobrien */ 753133359Sobrienprivate int 754133359Sobriendophn_exec(struct magic_set *ms, int class, int swap, int fd, off_t off, 755133359Sobrien int num, size_t size) 756133359Sobrien{ 757133359Sobrien Elf32_Phdr ph32; 758133359Sobrien Elf64_Phdr ph64; 759133359Sobrien const char *linking_style = "statically"; 760133359Sobrien const char *shared_libraries = ""; 761133359Sobrien unsigned char nbuf[BUFSIZ]; 762133359Sobrien int bufsize; 763133359Sobrien size_t offset, align; 764133359Sobrien off_t savedoffset; 765159764Sobrien int flags = 0; 766133359Sobrien 767159764Sobrien if (size != xph_sizeof) { 768133359Sobrien if (file_printf(ms, ", corrupted program header size") == -1) 769133359Sobrien return -1; 770133359Sobrien return 0; 771133359Sobrien } 772133359Sobrien if (lseek(fd, off, SEEK_SET) == (off_t)-1) { 773133359Sobrien file_badseek(ms); 774133359Sobrien return -1; 775133359Sobrien } 776133359Sobrien 777133359Sobrien for ( ; num; num--) { 778159764Sobrien if (read(fd, xph_addr, xph_sizeof) == -1) { 779133359Sobrien file_badread(ms); 780133359Sobrien return -1; 781133359Sobrien } 782133359Sobrien if ((savedoffset = lseek(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) { 783133359Sobrien file_badseek(ms); 784133359Sobrien return -1; 785133359Sobrien } 786133359Sobrien 787159764Sobrien switch (xph_type) { 788133359Sobrien case PT_DYNAMIC: 789133359Sobrien linking_style = "dynamically"; 790133359Sobrien break; 791133359Sobrien case PT_INTERP: 792133359Sobrien shared_libraries = " (uses shared libs)"; 793133359Sobrien break; 794133359Sobrien case PT_NOTE: 795159764Sobrien if ((align = xph_align) & 0x80000000) { 796133359Sobrien if (file_printf(ms, 797133359Sobrien ", invalid note alignment 0x%lx", 798133359Sobrien (unsigned long)align) == -1) 799133359Sobrien return -1; 800133359Sobrien align = 4; 801133359Sobrien } 802133359Sobrien /* 803133359Sobrien * This is a PT_NOTE section; loop through all the notes 804133359Sobrien * in the section. 805133359Sobrien */ 806159764Sobrien if (lseek(fd, (off_t)xph_offset, SEEK_SET) 807133359Sobrien == (off_t)-1) { 808133359Sobrien file_badseek(ms); 809133359Sobrien return -1; 810133359Sobrien } 811159764Sobrien bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ? 812159764Sobrien xph_filesz : sizeof(nbuf))); 813133359Sobrien if (bufsize == -1) { 814133359Sobrien file_badread(ms); 815133359Sobrien return -1; 816133359Sobrien } 817133359Sobrien offset = 0; 818133359Sobrien for (;;) { 819133359Sobrien if (offset >= (size_t)bufsize) 820133359Sobrien break; 821133359Sobrien offset = donote(ms, nbuf, offset, 822159764Sobrien (size_t)bufsize, class, swap, align, 823159764Sobrien &flags); 824133359Sobrien if (offset == 0) 825133359Sobrien break; 826133359Sobrien } 827133359Sobrien if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) { 828133359Sobrien file_badseek(ms); 829133359Sobrien return -1; 830133359Sobrien } 831133359Sobrien break; 832133359Sobrien } 833133359Sobrien } 834133359Sobrien if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries) 835133359Sobrien == -1) 836133359Sobrien return -1; 837133359Sobrien return 0; 838133359Sobrien} 839133359Sobrien 840133359Sobrien 841133359Sobrienprotected int 842133359Sobrienfile_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, 843133359Sobrien size_t nbytes) 844133359Sobrien{ 84568349Sobrien union { 846103373Sobrien int32_t l; 847103373Sobrien char c[sizeof (int32_t)]; 84868349Sobrien } u; 84968349Sobrien int class; 85068349Sobrien int swap; 85168349Sobrien 85268349Sobrien /* 853133359Sobrien * If we cannot seek, it must be a pipe, socket or fifo. 854103373Sobrien */ 855103373Sobrien if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) 856133359Sobrien fd = file_pipe2file(ms, fd, buf, nbytes); 857103373Sobrien 858103373Sobrien /* 85968349Sobrien * ELF executables have multiple section headers in arbitrary 86068349Sobrien * file locations and thus file(1) cannot determine it from easily. 86168349Sobrien * Instead we traverse thru all section headers until a symbol table 86268349Sobrien * one is found or else the binary is stripped. 86368349Sobrien */ 86468349Sobrien if (buf[EI_MAG0] != ELFMAG0 86568349Sobrien || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) 86668349Sobrien || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) 867133359Sobrien return 0; 86868349Sobrien 86968349Sobrien 87068349Sobrien class = buf[4]; 87168349Sobrien 87268349Sobrien if (class == ELFCLASS32) { 87368349Sobrien Elf32_Ehdr elfhdr; 87468349Sobrien if (nbytes <= sizeof (Elf32_Ehdr)) 875133359Sobrien return 0; 87668349Sobrien 87768349Sobrien 87868349Sobrien u.l = 1; 87968349Sobrien (void) memcpy(&elfhdr, buf, sizeof elfhdr); 880103373Sobrien swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5]; 88168349Sobrien 882133359Sobrien if (getu16(swap, elfhdr.e_type) == ET_CORE) { 88368349Sobrien#ifdef ELFCORE 884133359Sobrien if (dophn_core(ms, class, swap, fd, 885133359Sobrien (off_t)getu32(swap, elfhdr.e_phoff), 886133359Sobrien getu16(swap, elfhdr.e_phnum), 887133359Sobrien (size_t)getu16(swap, elfhdr.e_phentsize)) == -1) 888133359Sobrien return -1; 88968349Sobrien#else 89068349Sobrien ; 89168349Sobrien#endif 892133359Sobrien } else { 89368349Sobrien if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 894133359Sobrien if (dophn_exec(ms, class, swap, 895133359Sobrien fd, (off_t)getu32(swap, elfhdr.e_phoff), 896133359Sobrien getu16(swap, elfhdr.e_phnum), 897133359Sobrien (size_t)getu16(swap, elfhdr.e_phentsize)) 898133359Sobrien == -1) 899133359Sobrien return -1; 90068349Sobrien } 901133359Sobrien if (doshn(ms, class, swap, fd, 902133359Sobrien (off_t)getu32(swap, elfhdr.e_shoff), 903133359Sobrien getu16(swap, elfhdr.e_shnum), 904133359Sobrien (size_t)getu16(swap, elfhdr.e_shentsize)) == -1) 905133359Sobrien return -1; 90668349Sobrien } 907133359Sobrien return 1; 90868349Sobrien } 90968349Sobrien 91068349Sobrien if (class == ELFCLASS64) { 91168349Sobrien Elf64_Ehdr elfhdr; 91268349Sobrien if (nbytes <= sizeof (Elf64_Ehdr)) 913133359Sobrien return 0; 91468349Sobrien 91568349Sobrien 91668349Sobrien u.l = 1; 91768349Sobrien (void) memcpy(&elfhdr, buf, sizeof elfhdr); 918103373Sobrien swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[5]; 91968349Sobrien 920133359Sobrien if (getu16(swap, elfhdr.e_type) == ET_CORE) { 92168349Sobrien#ifdef ELFCORE 922133359Sobrien if (dophn_core(ms, class, swap, fd, 923159764Sobrien (off_t)elf_getu64(swap, elfhdr.e_phoff), 924133359Sobrien getu16(swap, elfhdr.e_phnum), 925133359Sobrien (size_t)getu16(swap, elfhdr.e_phentsize)) == -1) 926133359Sobrien return -1; 92768349Sobrien#else 92868349Sobrien ; 92968349Sobrien#endif 930133359Sobrien } else { 93168349Sobrien if (getu16(swap, elfhdr.e_type) == ET_EXEC) { 932133359Sobrien if (dophn_exec(ms, class, swap, fd, 933159764Sobrien (off_t)elf_getu64(swap, elfhdr.e_phoff), 934133359Sobrien getu16(swap, elfhdr.e_phnum), 935133359Sobrien (size_t)getu16(swap, elfhdr.e_phentsize)) 936133359Sobrien == -1) 937133359Sobrien return -1; 93868349Sobrien } 939133359Sobrien if (doshn(ms, class, swap, fd, 940159764Sobrien (off_t)elf_getu64(swap, elfhdr.e_shoff), 941133359Sobrien getu16(swap, elfhdr.e_shnum), 942133359Sobrien (size_t)getu16(swap, elfhdr.e_shentsize)) == -1) 943133359Sobrien return -1; 94468349Sobrien } 945133359Sobrien return 1; 94668349Sobrien } 947133359Sobrien return 0; 94868349Sobrien} 94968349Sobrien#endif 950