Annotation of src/usr.bin/file/readelf.c, Revision 1.10
1.10 ! chl 1: /* $OpenBSD: readelf.c,v 1.9 2008/05/08 01:40:56 chl Exp $ */
1.5 ian 2: /*
1.8 tedu 3: * Copyright (c) Christos Zoulas 2003.
4: * All Rights Reserved.
1.5 ian 5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice immediately at the beginning of the file, without modification,
11: * this list of conditions, and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26: * SUCH DAMAGE.
27: */
1.4 ian 28: #include "file.h"
1.1 millert 29:
30: #ifdef BUILTIN_ELF
31: #include <string.h>
32: #include <ctype.h>
33: #include <stdlib.h>
1.4 ian 34: #ifdef HAVE_UNISTD_H
1.1 millert 35: #include <unistd.h>
1.4 ian 36: #endif
1.1 millert 37:
38: #include "readelf.h"
1.10 ! chl 39: #include "magic.h"
1.4 ian 40:
41: #ifndef lint
1.10 ! chl 42: FILE_RCSID("@(#)$Id: readelf.c,v 1.9 2008/05/08 01:40:56 chl Exp $")
1.4 ian 43: #endif
44:
45: #ifdef ELFCORE
1.9 chl 46: private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t,
47: off_t, int *);
1.4 ian 48: #endif
1.9 chl 49: private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t,
50: off_t, int *);
51: private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, int *);
1.8 tedu 52: private size_t donote(struct magic_set *, unsigned char *, size_t, size_t, int,
1.9 chl 53: int, size_t, int *);
1.8 tedu 54:
55: #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align)
56:
1.9 chl 57: #define isquote(c) (strchr("'\"`", (c)) != NULL)
58:
1.8 tedu 59: private uint16_t getu16(int, uint16_t);
60: private uint32_t getu32(int, uint32_t);
61: private uint64_t getu64(int, uint64_t);
1.4 ian 62:
1.8 tedu 63: private uint16_t
1.4 ian 64: getu16(int swap, uint16_t value)
65: {
66: union {
67: uint16_t ui;
68: char c[2];
69: } retval, tmpval;
70:
71: if (swap) {
72: tmpval.ui = value;
73:
74: retval.c[0] = tmpval.c[1];
75: retval.c[1] = tmpval.c[0];
76:
77: return retval.ui;
78: } else
79: return value;
80: }
81:
1.8 tedu 82: private uint32_t
1.4 ian 83: getu32(int swap, uint32_t value)
84: {
85: union {
86: uint32_t ui;
87: char c[4];
88: } retval, tmpval;
89:
90: if (swap) {
91: tmpval.ui = value;
92:
93: retval.c[0] = tmpval.c[3];
94: retval.c[1] = tmpval.c[2];
95: retval.c[2] = tmpval.c[1];
96: retval.c[3] = tmpval.c[0];
97:
98: return retval.ui;
99: } else
100: return value;
101: }
102:
1.8 tedu 103: private uint64_t
1.4 ian 104: getu64(int swap, uint64_t value)
105: {
106: union {
107: uint64_t ui;
108: char c[8];
109: } retval, tmpval;
110:
111: if (swap) {
112: tmpval.ui = value;
113:
114: retval.c[0] = tmpval.c[7];
115: retval.c[1] = tmpval.c[6];
116: retval.c[2] = tmpval.c[5];
117: retval.c[3] = tmpval.c[4];
118: retval.c[4] = tmpval.c[3];
119: retval.c[5] = tmpval.c[2];
120: retval.c[6] = tmpval.c[1];
121: retval.c[7] = tmpval.c[0];
122:
123: return retval.ui;
124: } else
125: return value;
126: }
127:
1.10 ! chl 128: #define elf_getu16(swap, value) getu16(swap, value)
! 129: #define elf_getu32(swap, value) getu32(swap, value)
1.9 chl 130: #ifdef USE_ARRAY_FOR_64BIT_TYPES
131: # define elf_getu64(swap, array) \
1.10 ! chl 132: ((swap ? ((uint64_t)elf_getu32(swap, array[0])) << 32 : elf_getu32(swap, array[0])) + \
! 133: (swap ? elf_getu32(swap, array[1]) : ((uint64_t)elf_getu32(swap, array[1]) << 32)))
1.9 chl 134: #else
135: # define elf_getu64(swap, value) getu64(swap, value)
136: #endif
137:
1.10 ! chl 138: #define xsh_addr (class == ELFCLASS32 \
! 139: ? (void *) &sh32 \
1.4 ian 140: : (void *) &sh64)
1.10 ! chl 141: #define xsh_sizeof (class == ELFCLASS32 \
! 142: ? sizeof sh32 \
1.4 ian 143: : sizeof sh64)
1.10 ! chl 144: #define xsh_size (class == ELFCLASS32 \
! 145: ? elf_getu32(swap, sh32.sh_size) \
! 146: : elf_getu64(swap, sh64.sh_size))
! 147: #define xsh_offset (class == ELFCLASS32 \
! 148: ? elf_getu32(swap, sh32.sh_offset) \
! 149: : elf_getu64(swap, sh64.sh_offset))
! 150: #define xsh_type (class == ELFCLASS32 \
! 151: ? elf_getu32(swap, sh32.sh_type) \
! 152: : elf_getu32(swap, sh64.sh_type))
! 153: #define xph_addr (class == ELFCLASS32 \
! 154: ? (void *) &ph32 \
1.4 ian 155: : (void *) &ph64)
1.10 ! chl 156: #define xph_sizeof (class == ELFCLASS32 \
! 157: ? sizeof ph32 \
1.4 ian 158: : sizeof ph64)
1.10 ! chl 159: #define xph_type (class == ELFCLASS32 \
! 160: ? elf_getu32(swap, ph32.p_type) \
! 161: : elf_getu32(swap, ph64.p_type))
! 162: #define xph_offset (off_t)(class == ELFCLASS32 \
! 163: ? elf_getu32(swap, ph32.p_offset) \
! 164: : elf_getu64(swap, ph64.p_offset))
! 165: #define xph_align (size_t)((class == ELFCLASS32 \
! 166: ? (off_t) (ph32.p_align ? \
! 167: elf_getu32(swap, ph32.p_align) : 4) \
! 168: : (off_t) (ph64.p_align ? \
! 169: elf_getu64(swap, ph64.p_align) : 4)))
! 170: #define xph_filesz (size_t)((class == ELFCLASS32 \
! 171: ? elf_getu32(swap, ph32.p_filesz) \
! 172: : elf_getu64(swap, ph64.p_filesz)))
! 173: #define xnh_addr (class == ELFCLASS32 \
! 174: ? (void *) &nh32 \
1.9 chl 175: : (void *) &nh64)
1.10 ! chl 176: #define xph_memsz (size_t)((class == ELFCLASS32 \
! 177: ? elf_getu32(swap, ph32.p_memsz) \
! 178: : elf_getu64(swap, ph64.p_memsz)))
! 179: #define xnh_sizeof (class == ELFCLASS32 \
! 180: ? sizeof nh32 \
1.8 tedu 181: : sizeof nh64)
1.10 ! chl 182: #define xnh_type (class == ELFCLASS32 \
! 183: ? elf_getu32(swap, nh32.n_type) \
! 184: : elf_getu32(swap, nh64.n_type))
! 185: #define xnh_namesz (class == ELFCLASS32 \
! 186: ? elf_getu32(swap, nh32.n_namesz) \
! 187: : elf_getu32(swap, nh64.n_namesz))
! 188: #define xnh_descsz (class == ELFCLASS32 \
! 189: ? elf_getu32(swap, nh32.n_descsz) \
! 190: : elf_getu32(swap, nh64.n_descsz))
! 191: #define prpsoffsets(i) (class == ELFCLASS32 \
! 192: ? prpsoffsets32[i] \
1.4 ian 193: : prpsoffsets64[i])
1.1 millert 194:
1.4 ian 195: #ifdef ELFCORE
1.10 ! chl 196: /*
! 197: * Try larger offsets first to avoid false matches
! 198: * from earlier data that happen to look like strings.
! 199: */
! 200: static const size_t prpsoffsets32[] = {
! 201: #ifdef USE_NT_PSINFO
! 202: 104, /* SunOS 5.x (command line) */
! 203: 88, /* SunOS 5.x (short name) */
! 204: #endif /* USE_NT_PSINFO */
! 205:
! 206: 100, /* SunOS 5.x (command line) */
! 207: 84, /* SunOS 5.x (short name) */
! 208:
! 209: 44, /* Linux (command line) */
! 210: 28, /* Linux 2.0.36 (short name) */
! 211:
1.4 ian 212: 8, /* FreeBSD */
213: };
214:
1.10 ! chl 215: static const size_t prpsoffsets64[] = {
! 216: #ifdef USE_NT_PSINFO
! 217: 152, /* SunOS 5.x (command line) */
! 218: 136, /* SunOS 5.x (short name) */
! 219: #endif /* USE_NT_PSINFO */
! 220:
! 221: 136, /* SunOS 5.x, 64-bit (command line) */
! 222: 120, /* SunOS 5.x, 64-bit (short name) */
! 223:
! 224: 56, /* Linux (command line) */
! 225: 40, /* Linux (tested on core from 2.4.x, short name) */
! 226:
1.9 chl 227: 16, /* FreeBSD, 64-bit */
1.1 millert 228: };
229:
1.4 ian 230: #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
231: #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
232:
233: #define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
1.1 millert 234:
235: /*
236: * Look through the program headers of an executable image, searching
1.4 ian 237: * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
238: * "FreeBSD"; if one is found, try looking in various places in its
239: * contents for a 16-character string containing only printable
240: * characters - if found, that string should be the name of the program
241: * that dropped core. Note: right after that 16-character string is,
242: * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
243: * Linux, a longer string (80 characters, in 5.x, probably other
244: * SVR4-flavored systems, and Linux) containing the start of the
245: * command line for that program.
246: *
1.10 ! chl 247: * SunOS 5.x core files contain two PT_NOTE sections, with the types
! 248: * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the
! 249: * same info about the command name and command line, so it probably
! 250: * isn't worthwhile to look for NT_PSINFO, but the offsets are provided
! 251: * above (see USE_NT_PSINFO), in case we ever decide to do so. The
! 252: * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent;
! 253: * the SunOS 5.x file command relies on this (and prefers the latter).
! 254: *
1.4 ian 255: * The signal number probably appears in a section of type NT_PRSTATUS,
256: * but that's also rather OS-dependent, in ways that are harder to
257: * dissect with heuristics, so I'm not bothering with the signal number.
258: * (I suppose the signal number could be of interest in situations where
259: * you don't have the binary of the program that dropped core; if you
260: * *do* have that binary, the debugger will probably tell you what
261: * signal it was.)
1.1 millert 262: */
1.4 ian 263:
264: #define OS_STYLE_SVR4 0
265: #define OS_STYLE_FREEBSD 1
266: #define OS_STYLE_NETBSD 2
267:
1.10 ! chl 268: private const char os_style_names[][8] = {
1.4 ian 269: "SVR4",
270: "FreeBSD",
271: "NetBSD",
272: };
273:
1.9 chl 274: #define FLAGS_DID_CORE 1
275: #define FLAGS_DID_NOTE 2
1.10 ! chl 276: #define FLAGS_DID_CORE_STYLE 4
1.9 chl 277:
1.8 tedu 278: private int
279: dophn_core(struct magic_set *ms, int class, int swap, int fd, off_t off,
1.9 chl 280: int num, size_t size, off_t fsize, int *flags)
1.1 millert 281: {
1.4 ian 282: Elf32_Phdr ph32;
283: Elf64_Phdr ph64;
1.8 tedu 284: size_t offset;
285: unsigned char nbuf[BUFSIZ];
286: ssize_t bufsize;
1.9 chl 287: off_t savedoffset;
288: struct stat st;
1.8 tedu 289:
1.9 chl 290: if (fstat(fd, &st) < 0) {
291: file_badread(ms);
292: return -1;
293: }
294:
295: if (size != xph_sizeof) {
1.8 tedu 296: if (file_printf(ms, ", corrupted program header size") == -1)
297: return -1;
298: return 0;
299: }
1.9 chl 300:
1.4 ian 301: /*
302: * Loop through all the program headers.
303: */
1.1 millert 304: for ( ; num; num--) {
1.9 chl 305: if ((savedoffset = lseek(fd, off, SEEK_SET)) == (off_t)-1) {
1.8 tedu 306: file_badseek(ms);
307: return -1;
308: }
1.9 chl 309: if (read(fd, xph_addr, xph_sizeof) == -1) {
1.8 tedu 310: file_badread(ms);
311: return -1;
312: }
1.9 chl 313: if (xph_offset > fsize) {
314: if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) {
315: file_badseek(ms);
316: return -1;
317: }
318: continue;
319: }
320:
1.1 millert 321: off += size;
1.9 chl 322: if (xph_type != PT_NOTE)
1.1 millert 323: continue;
1.4 ian 324:
325: /*
326: * This is a PT_NOTE section; loop through all the notes
327: * in the section.
328: */
1.9 chl 329: if (lseek(fd, xph_offset, SEEK_SET) == (off_t)-1) {
1.8 tedu 330: file_badseek(ms);
331: return -1;
332: }
1.9 chl 333: bufsize = read(fd, nbuf,
334: ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf)));
1.8 tedu 335: if (bufsize == -1) {
336: file_badread(ms);
337: return -1;
338: }
1.1 millert 339: offset = 0;
340: for (;;) {
1.8 tedu 341: if (offset >= (size_t)bufsize)
342: break;
343: offset = donote(ms, nbuf, offset, (size_t)bufsize,
1.9 chl 344: class, swap, 4, flags);
1.8 tedu 345: if (offset == 0)
1.1 millert 346: break;
347:
1.8 tedu 348: }
349: }
350: return 0;
351: }
352: #endif
353:
354: private size_t
355: donote(struct magic_set *ms, unsigned char *nbuf, size_t offset, size_t size,
1.9 chl 356: int class, int swap, size_t align, int *flags)
1.8 tedu 357: {
358: Elf32_Nhdr nh32;
359: Elf64_Nhdr nh64;
360: size_t noff, doff;
361: #ifdef ELFCORE
362: int os_style = -1;
363: #endif
364: uint32_t namesz, descsz;
365:
1.9 chl 366: (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
367: offset += xnh_sizeof;
1.8 tedu 368:
1.9 chl 369: namesz = xnh_namesz;
370: descsz = xnh_descsz;
1.8 tedu 371: if ((namesz == 0) && (descsz == 0)) {
372: /*
373: * We're out of note headers.
374: */
1.9 chl 375: return (offset >= size) ? offset : size;
1.8 tedu 376: }
377:
378: if (namesz & 0x80000000) {
379: (void)file_printf(ms, ", bad note name size 0x%lx",
380: (unsigned long)namesz);
381: return offset;
382: }
383:
384: if (descsz & 0x80000000) {
385: (void)file_printf(ms, ", bad note description size 0x%lx",
386: (unsigned long)descsz);
387: return offset;
388: }
389:
390:
391: noff = offset;
392: doff = ELF_ALIGN(offset + namesz);
393:
1.9 chl 394: if (offset + namesz > size) {
1.8 tedu 395: /*
396: * We're past the end of the buffer.
397: */
398: return doff;
399: }
400:
401: offset = ELF_ALIGN(doff + descsz);
1.9 chl 402: if (doff + descsz > size) {
403: /*
404: * We're past the end of the buffer.
405: */
406: return (offset >= size) ? offset : size;
1.8 tedu 407: }
408:
1.9 chl 409: if (*flags & FLAGS_DID_NOTE)
410: goto core;
411:
1.8 tedu 412: if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
1.9 chl 413: xnh_type == NT_GNU_VERSION && descsz == 16) {
1.8 tedu 414: uint32_t desc[4];
415: (void)memcpy(desc, &nbuf[doff], sizeof(desc));
416:
417: if (file_printf(ms, ", for GNU/") == -1)
418: return size;
1.10 ! chl 419: switch (elf_getu32(swap, desc[0])) {
1.8 tedu 420: case GNU_OS_LINUX:
421: if (file_printf(ms, "Linux") == -1)
422: return size;
423: break;
424: case GNU_OS_HURD:
425: if (file_printf(ms, "Hurd") == -1)
426: return size;
427: break;
428: case GNU_OS_SOLARIS:
429: if (file_printf(ms, "Solaris") == -1)
430: return size;
431: break;
1.10 ! chl 432: case GNU_OS_KFREEBSD:
! 433: if (file_printf(ms, "kFreeBSD") == -1)
! 434: return size;
! 435: break;
! 436: case GNU_OS_KNETBSD:
! 437: if (file_printf(ms, "kNetBSD") == -1)
! 438: return size;
! 439: break;
1.8 tedu 440: default:
441: if (file_printf(ms, "<unknown>") == -1)
442: return size;
443: }
1.10 ! chl 444: if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
! 445: elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
1.8 tedu 446: return size;
1.9 chl 447: *flags |= FLAGS_DID_NOTE;
1.8 tedu 448: return size;
449: }
450:
451: if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0 &&
1.9 chl 452: xnh_type == NT_NETBSD_VERSION && descsz == 4) {
1.8 tedu 453: uint32_t desc;
454: (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
1.10 ! chl 455: desc = elf_getu32(swap, desc);
1.8 tedu 456:
457: if (file_printf(ms, ", for NetBSD") == -1)
458: return size;
459: /*
460: * The version number used to be stuck as 199905, and was thus
461: * basically content-free. Newer versions of NetBSD have fixed
462: * this and now use the encoding of __NetBSD_Version__:
463: *
464: * MMmmrrpp00
465: *
466: * M = major version
467: * m = minor version
468: * r = release ["",A-Z,Z[A-Z] but numeric]
469: * p = patchlevel
470: */
471: if (desc > 100000000U) {
1.9 chl 472: uint32_t ver_patch = (desc / 100) % 100;
473: uint32_t ver_rel = (desc / 10000) % 100;
474: uint32_t ver_min = (desc / 1000000) % 100;
475: uint32_t ver_maj = desc / 100000000;
1.8 tedu 476:
477: if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1)
478: return size;
479: if (ver_rel == 0 && ver_patch != 0) {
480: if (file_printf(ms, ".%u", ver_patch) == -1)
481: return size;
482: } else if (ver_rel != 0) {
483: while (ver_rel > 26) {
1.9 chl 484: if (file_printf(ms, "Z") == -1)
485: return size;
1.8 tedu 486: ver_rel -= 26;
487: }
1.9 chl 488: if (file_printf(ms, "%c", 'A' + ver_rel - 1)
489: == -1)
490: return size;
1.1 millert 491: }
1.8 tedu 492: }
1.9 chl 493: *flags |= FLAGS_DID_NOTE;
1.8 tedu 494: return size;
495: }
1.4 ian 496:
1.8 tedu 497: if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0 &&
1.9 chl 498: xnh_type == NT_FREEBSD_VERSION && descsz == 4) {
1.8 tedu 499: uint32_t desc;
500: (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
1.10 ! chl 501: desc = elf_getu32(swap, desc);
1.8 tedu 502: if (file_printf(ms, ", for FreeBSD") == -1)
503: return size;
1.1 millert 504:
1.8 tedu 505: /*
506: * Contents is __FreeBSD_version, whose relation to OS
1.9 chl 507: * versions is defined by a huge table in the Porter's
508: * Handbook. This is the general scheme:
509: *
510: * Releases:
511: * Mmp000 (before 4.10)
512: * Mmi0p0 (before 5.0)
513: * Mmm0p0
514: *
515: * Development branches:
516: * Mmpxxx (before 4.6)
517: * Mmp1xx (before 4.10)
518: * Mmi1xx (before 5.0)
519: * M000xx (pre-M.0)
520: * Mmm1xx
521: *
522: * M = major version
523: * m = minor version
524: * i = minor version increment (491000 -> 4.10)
525: * p = patchlevel
526: * x = revision
527: *
528: * The first release of FreeBSD to use ELF by default
529: * was version 3.0.
1.8 tedu 530: */
1.9 chl 531: if (desc == 460002) {
532: if (file_printf(ms, " 4.6.2") == -1)
533: return size;
534: } else if (desc < 460100) {
1.8 tedu 535: if (file_printf(ms, " %d.%d", desc / 100000,
536: desc / 10000 % 10) == -1)
537: return size;
538: if (desc / 1000 % 10 > 0)
539: if (file_printf(ms, ".%d", desc / 1000 % 10)
540: == -1)
541: return size;
1.9 chl 542: if ((desc % 1000 > 0) || (desc % 100000 == 0))
543: if (file_printf(ms, " (%d)", desc) == -1)
544: return size;
545: } else if (desc < 500000) {
546: if (file_printf(ms, " %d.%d", desc / 100000,
547: desc / 10000 % 10 + desc / 1000 % 10) == -1)
548: return size;
549: if (desc / 100 % 10 > 0) {
550: if (file_printf(ms, " (%d)", desc) == -1)
551: return size;
552: } else if (desc / 10 % 10 > 0) {
553: if (file_printf(ms, ".%d", desc / 10 % 10)
554: == -1)
555: return size;
556: }
1.8 tedu 557: } else {
558: if (file_printf(ms, " %d.%d", desc / 100000,
559: desc / 1000 % 100) == -1)
560: return size;
1.9 chl 561: if ((desc / 100 % 10 > 0) ||
562: (desc % 100000 / 100 == 0)) {
563: if (file_printf(ms, " (%d)", desc) == -1)
1.8 tedu 564: return size;
1.9 chl 565: } else if (desc / 10 % 10 > 0) {
566: if (file_printf(ms, ".%d", desc / 10 % 10)
567: == -1)
1.8 tedu 568: return size;
1.4 ian 569: }
1.8 tedu 570: }
1.9 chl 571: *flags |= FLAGS_DID_NOTE;
1.8 tedu 572: return size;
573: }
574:
575: if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 &&
1.9 chl 576: xnh_type == NT_OPENBSD_VERSION && descsz == 4) {
1.8 tedu 577: if (file_printf(ms, ", for OpenBSD") == -1)
578: return size;
579: /* Content of note is always 0 */
1.9 chl 580: *flags |= FLAGS_DID_NOTE;
1.8 tedu 581: return size;
582: }
583:
1.9 chl 584: if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 &&
585: xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) {
586: uint32_t desc;
587: if (file_printf(ms, ", for DragonFly") == -1)
588: return size;
589: (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
1.10 ! chl 590: desc = elf_getu32(swap, desc);
1.9 chl 591: if (file_printf(ms, " %d.%d.%d", desc / 100000,
592: desc / 10000 % 10, desc % 10000) == -1)
593: return size;
594: *flags |= FLAGS_DID_NOTE;
595: return size;
596: }
597:
598: core:
1.8 tedu 599: /*
600: * Sigh. The 2.0.36 kernel in Debian 2.1, at
601: * least, doesn't correctly implement name
602: * sections, in core dumps, as specified by
603: * the "Program Linking" section of "UNIX(R) System
604: * V Release 4 Programmer's Guide: ANSI C and
605: * Programming Support Tools", because my copy
606: * clearly says "The first 'namesz' bytes in 'name'
607: * contain a *null-terminated* [emphasis mine]
608: * character representation of the entry's owner
609: * or originator", but the 2.0.36 kernel code
610: * doesn't include the terminating null in the
611: * name....
612: */
613: if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) ||
614: (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) {
615: os_style = OS_STYLE_SVR4;
616: }
617:
618: if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
619: os_style = OS_STYLE_FREEBSD;
620: }
621:
622: if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
623: == 0)) {
624: os_style = OS_STYLE_NETBSD;
625: }
1.4 ian 626:
1.8 tedu 627: #ifdef ELFCORE
1.9 chl 628: if ((*flags & FLAGS_DID_CORE) != 0)
629: return size;
630:
1.10 ! chl 631: if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
1.9 chl 632: if (file_printf(ms, ", %s-style", os_style_names[os_style])
633: == -1)
1.8 tedu 634: return size;
1.10 ! chl 635: *flags |= FLAGS_DID_CORE_STYLE;
1.9 chl 636: }
1.4 ian 637:
1.9 chl 638: switch (os_style) {
639: case OS_STYLE_NETBSD:
640: if (xnh_type == NT_NETBSD_CORE_PROCINFO) {
641: uint32_t signo;
642: /*
643: * Extract the program name. It is at
644: * offset 0x7c, and is up to 32-bytes,
645: * including the terminating NUL.
646: */
647: if (file_printf(ms, ", from '%.31s'",
648: &nbuf[doff + 0x7c]) == -1)
649: return size;
650:
651: /*
652: * Extract the signal number. It is at
653: * offset 0x08.
654: */
655: (void)memcpy(&signo, &nbuf[doff + 0x08],
656: sizeof(signo));
657: if (file_printf(ms, " (signal %u)",
1.10 ! chl 658: elf_getu32(swap, signo)) == -1)
1.9 chl 659: return size;
1.10 ! chl 660: *flags |= FLAGS_DID_CORE;
1.8 tedu 661: return size;
1.9 chl 662: }
663: break;
1.8 tedu 664:
1.9 chl 665: default:
666: if (xnh_type == NT_PRPSINFO) {
667: size_t i, j;
668: unsigned char c;
669: /*
670: * Extract the program name. We assume
671: * it to be 16 characters (that's what it
672: * is in SunOS 5.x and Linux).
673: *
674: * Unfortunately, it's at a different offset
675: * in various OSes, so try multiple offsets.
676: * If the characters aren't all printable,
677: * reject it.
678: */
679: for (i = 0; i < NOFFSETS; i++) {
1.10 ! chl 680: unsigned char *cname, *cp;
1.9 chl 681: size_t reloffset = prpsoffsets(i);
682: size_t noffset = doff + reloffset;
683: for (j = 0; j < 16; j++, noffset++,
684: reloffset++) {
1.8 tedu 685: /*
1.9 chl 686: * Make sure we're not past
687: * the end of the buffer; if
688: * we are, just give up.
1.8 tedu 689: */
1.9 chl 690: if (noffset >= size)
1.8 tedu 691: goto tryanother;
1.9 chl 692:
1.1 millert 693: /*
1.9 chl 694: * Make sure we're not past
695: * the end of the contents;
696: * if we are, this obviously
697: * isn't the right offset.
1.1 millert 698: */
1.9 chl 699: if (reloffset >= descsz)
1.8 tedu 700: goto tryanother;
1.9 chl 701:
702: c = nbuf[noffset];
703: if (c == '\0') {
704: /*
705: * A '\0' at the
706: * beginning is
707: * obviously wrong.
708: * Any other '\0'
709: * means we're done.
710: */
711: if (j == 0)
712: goto tryanother;
713: else
714: break;
715: } else {
716: /*
717: * A nonprintable
718: * character is also
719: * wrong.
720: */
721: if (!isprint(c) || isquote(c))
722: goto tryanother;
723: }
1.8 tedu 724: }
1.9 chl 725: /*
726: * Well, that worked.
727: */
1.10 ! chl 728: cname = (unsigned char *)
! 729: &nbuf[doff + prpsoffsets(i)];
! 730: for (cp = cname; *cp && isprint(*cp); cp++)
! 731: continue;
! 732: /*
! 733: * Linux apparently appends a space at the end
! 734: * of the command line: remove it.
! 735: */
! 736: while (cp > cname && isspace(cp[-1]))
! 737: cp--;
! 738: if (file_printf(ms, ", from '%.*s'",
! 739: (int)(cp - cname), cname) == -1)
1.9 chl 740: return size;
1.10 ! chl 741: *flags |= FLAGS_DID_CORE;
1.8 tedu 742: return size;
743:
1.9 chl 744: tryanother:
745: ;
746: }
1.8 tedu 747: }
1.9 chl 748: break;
1.8 tedu 749: }
750: #endif
751: return offset;
752: }
753:
754: private int
755: doshn(struct magic_set *ms, int class, int swap, int fd, off_t off, int num,
1.9 chl 756: size_t size, int *flags)
1.8 tedu 757: {
758: Elf32_Shdr sh32;
759: Elf64_Shdr sh64;
1.9 chl 760: int stripped = 1;
761: void *nbuf;
762: off_t noff;
1.8 tedu 763:
1.9 chl 764: if (size != xsh_sizeof) {
1.8 tedu 765: if (file_printf(ms, ", corrupted section header size") == -1)
766: return -1;
767: return 0;
768: }
769:
770: if (lseek(fd, off, SEEK_SET) == (off_t)-1) {
771: file_badseek(ms);
772: return -1;
773: }
774:
775: for ( ; num; num--) {
1.9 chl 776: if (read(fd, xsh_addr, xsh_sizeof) == -1) {
1.8 tedu 777: file_badread(ms);
778: return -1;
779: }
1.9 chl 780: switch (xsh_type) {
781: case SHT_SYMTAB:
782: #if 0
783: case SHT_DYNSYM:
784: #endif
785: stripped = 0;
786: break;
787: case SHT_NOTE:
788: if ((off = lseek(fd, (off_t)0, SEEK_CUR)) ==
789: (off_t)-1) {
790: file_badread(ms);
791: return -1;
792: }
793: if ((nbuf = malloc((size_t)xsh_size)) == NULL) {
794: file_error(ms, errno, "Cannot allocate memory"
795: " for note");
796: return -1;
797: }
798: if ((noff = lseek(fd, (off_t)xsh_offset, SEEK_SET)) ==
799: (off_t)-1) {
800: file_badread(ms);
801: free(nbuf);
802: return -1;
803: }
804: if (read(fd, nbuf, (size_t)xsh_size) !=
805: (ssize_t)xsh_size) {
806: free(nbuf);
807: file_badread(ms);
808: return -1;
809: }
810:
811: noff = 0;
812: for (;;) {
813: if (noff >= (size_t)xsh_size)
814: break;
815: noff = donote(ms, nbuf, (size_t)noff,
816: (size_t)xsh_size, class, swap, 4,
817: flags);
818: if (noff == 0)
819: break;
820: }
821: if ((lseek(fd, off, SEEK_SET)) == (off_t)-1) {
822: free(nbuf);
823: file_badread(ms);
1.8 tedu 824: return -1;
1.9 chl 825: }
826: free(nbuf);
827: break;
1.8 tedu 828: }
829: }
1.9 chl 830: if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
1.8 tedu 831: return -1;
832: return 0;
833: }
834:
835: /*
836: * Look through the program headers of an executable image, searching
837: * for a PT_INTERP section; if one is found, it's dynamically linked,
838: * otherwise it's statically linked.
839: */
840: private int
841: dophn_exec(struct magic_set *ms, int class, int swap, int fd, off_t off,
1.9 chl 842: int num, size_t size, off_t fsize, int *flags)
1.8 tedu 843: {
844: Elf32_Phdr ph32;
845: Elf64_Phdr ph64;
846: const char *linking_style = "statically";
847: const char *shared_libraries = "";
848: unsigned char nbuf[BUFSIZ];
849: int bufsize;
850: size_t offset, align;
1.9 chl 851: off_t savedoffset = (off_t)-1;
852: struct stat st;
1.8 tedu 853:
1.9 chl 854: if (fstat(fd, &st) < 0) {
855: file_badread(ms);
856: return -1;
857: }
858:
859: if (size != xph_sizeof) {
1.8 tedu 860: if (file_printf(ms, ", corrupted program header size") == -1)
1.10 ! chl 861: return -1;
1.8 tedu 862: return 0;
863: }
1.9 chl 864:
1.8 tedu 865: if (lseek(fd, off, SEEK_SET) == (off_t)-1) {
866: file_badseek(ms);
867: return -1;
868: }
869:
870: for ( ; num; num--) {
1.9 chl 871: if (read(fd, xph_addr, xph_sizeof) == -1) {
1.8 tedu 872: file_badread(ms);
873: return -1;
874: }
1.9 chl 875: if (xph_offset > st.st_size && savedoffset != (off_t)-1) {
876: if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) {
877: file_badseek(ms);
878: return -1;
879: }
880: continue;
881: }
882:
1.8 tedu 883: if ((savedoffset = lseek(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) {
884: file_badseek(ms);
885: return -1;
886: }
887:
1.9 chl 888: if (xph_offset > fsize) {
889: if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) {
890: file_badseek(ms);
891: return -1;
892: }
893: continue;
894: }
895:
896: switch (xph_type) {
1.8 tedu 897: case PT_DYNAMIC:
898: linking_style = "dynamically";
899: break;
900: case PT_INTERP:
901: shared_libraries = " (uses shared libs)";
902: break;
903: case PT_NOTE:
1.9 chl 904: if ((align = xph_align) & 0x80000000) {
1.8 tedu 905: if (file_printf(ms,
906: ", invalid note alignment 0x%lx",
907: (unsigned long)align) == -1)
908: return -1;
909: align = 4;
910: }
911: /*
912: * This is a PT_NOTE section; loop through all the notes
913: * in the section.
914: */
1.9 chl 915: if (lseek(fd, xph_offset, SEEK_SET)
1.8 tedu 916: == (off_t)-1) {
917: file_badseek(ms);
918: return -1;
919: }
1.9 chl 920: bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ?
921: xph_filesz : sizeof(nbuf)));
1.8 tedu 922: if (bufsize == -1) {
923: file_badread(ms);
924: return -1;
925: }
926: offset = 0;
927: for (;;) {
928: if (offset >= (size_t)bufsize)
929: break;
930: offset = donote(ms, nbuf, offset,
1.9 chl 931: (size_t)bufsize, class, swap, align,
932: flags);
1.8 tedu 933: if (offset == 0)
1.4 ian 934: break;
1.1 millert 935: }
1.9 chl 936: if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) {
1.8 tedu 937: file_badseek(ms);
938: return -1;
939: }
940: break;
1.10 ! chl 941: default:
! 942: break;
1.1 millert 943: }
944: }
1.8 tedu 945: if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries)
946: == -1)
947: return -1;
948: return 0;
1.1 millert 949: }
950:
1.8 tedu 951:
952: protected int
953: file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
954: size_t nbytes)
1.1 millert 955: {
956: union {
1.3 itojun 957: int32_t l;
958: char c[sizeof (int32_t)];
1.1 millert 959: } u;
1.4 ian 960: int class;
961: int swap;
1.9 chl 962: struct stat st;
963: off_t fsize;
964: int flags = 0;
1.10 ! chl 965: Elf32_Ehdr elf32hdr;
! 966: Elf64_Ehdr elf64hdr;
! 967: uint16_t type;
! 968:
! 969: if (ms->flags & MAGIC_MIME)
! 970: return 0;
! 971: /*
! 972: * ELF executables have multiple section headers in arbitrary
! 973: * file locations and thus file(1) cannot determine it from easily.
! 974: * Instead we traverse thru all section headers until a symbol table
! 975: * one is found or else the binary is stripped.
! 976: * Return immediately if it's not ELF (so we avoid pipe2file unless needed).
! 977: */
! 978: if (buf[EI_MAG0] != ELFMAG0
! 979: || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
! 980: || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
! 981: return 0;
1.4 ian 982:
983: /*
1.8 tedu 984: * If we cannot seek, it must be a pipe, socket or fifo.
1.4 ian 985: */
986: if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
1.8 tedu 987: fd = file_pipe2file(ms, fd, buf, nbytes);
1.1 millert 988:
1.9 chl 989: if (fstat(fd, &st) == -1) {
990: file_badread(ms);
991: return -1;
992: }
993: fsize = st.st_size;
994:
995: class = buf[EI_CLASS];
1.4 ian 996:
1.10 ! chl 997: switch (class) {
! 998: case ELFCLASS32:
! 999: #undef elf_getu
! 1000: #define elf_getu(a, b) elf_getu32(a, b)
! 1001: #undef elfhdr
! 1002: #define elfhdr elf32hdr
! 1003: #include "elfclass.h"
! 1004: case ELFCLASS64:
! 1005: #undef elf_getu
! 1006: #define elf_getu(a, b) elf_getu64(a, b)
! 1007: #undef elfhdr
! 1008: #define elfhdr elf64hdr
! 1009: #include "elfclass.h"
! 1010: default:
! 1011: if (file_printf(ms, ", unknown class %d", class) == -1)
! 1012: return -1;
! 1013: break;
1.1 millert 1014: }
1.8 tedu 1015: return 0;
1.1 millert 1016: }
1017: #endif