etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / file / dist / src / readelf.c
blobed57d2d2a4d065e90680de6fbd41864caa8dcda9
1 /* $NetBSD: readelf.c,v 1.11 2015/01/02 21:15:32 christos Exp $ */
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.
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.
29 #include "file.h"
31 #ifndef lint
32 #if 0
33 FILE_RCSID("@(#)$File: readelf.c,v 1.117 2014/12/16 23:29:42 christos Exp $")
34 #else
35 __RCSID("$NetBSD: readelf.c,v 1.11 2015/01/02 21:15:32 christos Exp $");
36 #endif
37 #endif
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
47 #include "readelf.h"
48 #include "magic.h"
50 #ifdef ELFCORE
51 private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t,
52 off_t, int *, uint16_t *);
53 #endif
54 private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t,
55 off_t, int, int *, uint16_t *);
56 private int doshn(struct magic_set *, int, int, int, off_t, int, size_t,
57 off_t, int, int, int *, uint16_t *);
58 private size_t donote(struct magic_set *, void *, size_t, size_t, int,
59 int, size_t, int *, uint16_t *);
61 #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align)
63 #define isquote(c) (strchr("'\"`", (c)) != NULL)
65 private uint16_t getu16(int, uint16_t);
66 private uint32_t getu32(int, uint32_t);
67 private uint64_t getu64(int, uint64_t);
69 #define MAX_PHNUM 128
70 #define MAX_SHNUM 32768
71 #define SIZE_UNKNOWN ((off_t)-1)
73 private int
74 toomany(struct magic_set *ms, const char *name, uint16_t num)
76 if (file_printf(ms, ", too many %s (%u)", name, num
77 ) == -1)
78 return -1;
79 return 0;
82 private uint16_t
83 getu16(int swap, uint16_t value)
85 union {
86 uint16_t ui;
87 char c[2];
88 } retval, tmpval;
90 if (swap) {
91 tmpval.ui = value;
93 retval.c[0] = tmpval.c[1];
94 retval.c[1] = tmpval.c[0];
96 return retval.ui;
97 } else
98 return value;
101 private uint32_t
102 getu32(int swap, uint32_t value)
104 union {
105 uint32_t ui;
106 char c[4];
107 } retval, tmpval;
109 if (swap) {
110 tmpval.ui = value;
112 retval.c[0] = tmpval.c[3];
113 retval.c[1] = tmpval.c[2];
114 retval.c[2] = tmpval.c[1];
115 retval.c[3] = tmpval.c[0];
117 return retval.ui;
118 } else
119 return value;
122 private uint64_t
123 getu64(int swap, uint64_t value)
125 union {
126 uint64_t ui;
127 char c[8];
128 } retval, tmpval;
130 if (swap) {
131 tmpval.ui = value;
133 retval.c[0] = tmpval.c[7];
134 retval.c[1] = tmpval.c[6];
135 retval.c[2] = tmpval.c[5];
136 retval.c[3] = tmpval.c[4];
137 retval.c[4] = tmpval.c[3];
138 retval.c[5] = tmpval.c[2];
139 retval.c[6] = tmpval.c[1];
140 retval.c[7] = tmpval.c[0];
142 return retval.ui;
143 } else
144 return value;
147 #define elf_getu16(swap, value) getu16(swap, value)
148 #define elf_getu32(swap, value) getu32(swap, value)
149 #define elf_getu64(swap, value) getu64(swap, value)
151 #define xsh_addr (clazz == ELFCLASS32 \
152 ? (void *)&sh32 \
153 : (void *)&sh64)
154 #define xsh_sizeof (clazz == ELFCLASS32 \
155 ? sizeof(sh32) \
156 : sizeof(sh64))
157 #define xsh_size (size_t)(clazz == ELFCLASS32 \
158 ? elf_getu32(swap, sh32.sh_size) \
159 : elf_getu64(swap, sh64.sh_size))
160 #define xsh_offset (off_t)(clazz == ELFCLASS32 \
161 ? elf_getu32(swap, sh32.sh_offset) \
162 : elf_getu64(swap, sh64.sh_offset))
163 #define xsh_type (clazz == ELFCLASS32 \
164 ? elf_getu32(swap, sh32.sh_type) \
165 : elf_getu32(swap, sh64.sh_type))
166 #define xsh_name (clazz == ELFCLASS32 \
167 ? elf_getu32(swap, sh32.sh_name) \
168 : elf_getu32(swap, sh64.sh_name))
169 #define xph_addr (clazz == ELFCLASS32 \
170 ? (void *) &ph32 \
171 : (void *) &ph64)
172 #define xph_sizeof (clazz == ELFCLASS32 \
173 ? sizeof(ph32) \
174 : sizeof(ph64))
175 #define xph_type (clazz == ELFCLASS32 \
176 ? elf_getu32(swap, ph32.p_type) \
177 : elf_getu32(swap, ph64.p_type))
178 #define xph_offset (off_t)(clazz == ELFCLASS32 \
179 ? elf_getu32(swap, ph32.p_offset) \
180 : elf_getu64(swap, ph64.p_offset))
181 #define xph_align (size_t)((clazz == ELFCLASS32 \
182 ? (off_t) (ph32.p_align ? \
183 elf_getu32(swap, ph32.p_align) : 4) \
184 : (off_t) (ph64.p_align ? \
185 elf_getu64(swap, ph64.p_align) : 4)))
186 #define xph_filesz (size_t)((clazz == ELFCLASS32 \
187 ? elf_getu32(swap, ph32.p_filesz) \
188 : elf_getu64(swap, ph64.p_filesz)))
189 #define xnh_addr (clazz == ELFCLASS32 \
190 ? (void *)&nh32 \
191 : (void *)&nh64)
192 #define xph_memsz (size_t)((clazz == ELFCLASS32 \
193 ? elf_getu32(swap, ph32.p_memsz) \
194 : elf_getu64(swap, ph64.p_memsz)))
195 #define xnh_sizeof (clazz == ELFCLASS32 \
196 ? sizeof nh32 \
197 : sizeof nh64)
198 #define xnh_type (clazz == ELFCLASS32 \
199 ? elf_getu32(swap, nh32.n_type) \
200 : elf_getu32(swap, nh64.n_type))
201 #define xnh_namesz (clazz == ELFCLASS32 \
202 ? elf_getu32(swap, nh32.n_namesz) \
203 : elf_getu32(swap, nh64.n_namesz))
204 #define xnh_descsz (clazz == ELFCLASS32 \
205 ? elf_getu32(swap, nh32.n_descsz) \
206 : elf_getu32(swap, nh64.n_descsz))
207 #define prpsoffsets(i) (clazz == ELFCLASS32 \
208 ? prpsoffsets32[i] \
209 : prpsoffsets64[i])
210 #define xcap_addr (clazz == ELFCLASS32 \
211 ? (void *)&cap32 \
212 : (void *)&cap64)
213 #define xcap_sizeof (clazz == ELFCLASS32 \
214 ? sizeof cap32 \
215 : sizeof cap64)
216 #define xcap_tag (clazz == ELFCLASS32 \
217 ? elf_getu32(swap, cap32.c_tag) \
218 : elf_getu64(swap, cap64.c_tag))
219 #define xcap_val (clazz == ELFCLASS32 \
220 ? elf_getu32(swap, cap32.c_un.c_val) \
221 : elf_getu64(swap, cap64.c_un.c_val))
223 #ifdef ELFCORE
225 * Try larger offsets first to avoid false matches
226 * from earlier data that happen to look like strings.
228 static const size_t prpsoffsets32[] = {
229 #ifdef USE_NT_PSINFO
230 104, /* SunOS 5.x (command line) */
231 88, /* SunOS 5.x (short name) */
232 #endif /* USE_NT_PSINFO */
234 100, /* SunOS 5.x (command line) */
235 84, /* SunOS 5.x (short name) */
237 44, /* Linux (command line) */
238 28, /* Linux 2.0.36 (short name) */
240 8, /* FreeBSD */
243 static const size_t prpsoffsets64[] = {
244 #ifdef USE_NT_PSINFO
245 152, /* SunOS 5.x (command line) */
246 136, /* SunOS 5.x (short name) */
247 #endif /* USE_NT_PSINFO */
249 136, /* SunOS 5.x, 64-bit (command line) */
250 120, /* SunOS 5.x, 64-bit (short name) */
252 56, /* Linux (command line) */
253 40, /* Linux (tested on core from 2.4.x, short name) */
255 16, /* FreeBSD, 64-bit */
258 #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
259 #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
261 #define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
264 * Look through the program headers of an executable image, searching
265 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
266 * "FreeBSD"; if one is found, try looking in various places in its
267 * contents for a 16-character string containing only printable
268 * characters - if found, that string should be the name of the program
269 * that dropped core. Note: right after that 16-character string is,
270 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
271 * Linux, a longer string (80 characters, in 5.x, probably other
272 * SVR4-flavored systems, and Linux) containing the start of the
273 * command line for that program.
275 * SunOS 5.x core files contain two PT_NOTE sections, with the types
276 * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the
277 * same info about the command name and command line, so it probably
278 * isn't worthwhile to look for NT_PSINFO, but the offsets are provided
279 * above (see USE_NT_PSINFO), in case we ever decide to do so. The
280 * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent;
281 * the SunOS 5.x file command relies on this (and prefers the latter).
283 * The signal number probably appears in a section of type NT_PRSTATUS,
284 * but that's also rather OS-dependent, in ways that are harder to
285 * dissect with heuristics, so I'm not bothering with the signal number.
286 * (I suppose the signal number could be of interest in situations where
287 * you don't have the binary of the program that dropped core; if you
288 * *do* have that binary, the debugger will probably tell you what
289 * signal it was.)
292 #define OS_STYLE_SVR4 0
293 #define OS_STYLE_FREEBSD 1
294 #define OS_STYLE_NETBSD 2
296 private const char os_style_names[][8] = {
297 "SVR4",
298 "FreeBSD",
299 "NetBSD",
302 #define FLAGS_DID_CORE 0x001
303 #define FLAGS_DID_OS_NOTE 0x002
304 #define FLAGS_DID_BUILD_ID 0x004
305 #define FLAGS_DID_CORE_STYLE 0x008
306 #define FLAGS_DID_NETBSD_PAX 0x010
307 #define FLAGS_DID_NETBSD_MARCH 0x020
308 #define FLAGS_DID_NETBSD_CMODEL 0x040
309 #define FLAGS_DID_NETBSD_UNKNOWN 0x080
310 #define FLAGS_IS_CORE 0x100
312 private int
313 dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
314 int num, size_t size, off_t fsize, int *flags, uint16_t *notecount)
316 Elf32_Phdr ph32;
317 Elf64_Phdr ph64;
318 size_t offset, len;
319 unsigned char nbuf[BUFSIZ];
320 ssize_t bufsize;
322 if (size != xph_sizeof) {
323 if (file_printf(ms, ", corrupted program header size") == -1)
324 return -1;
325 return 0;
329 * Loop through all the program headers.
331 for ( ; num; num--) {
332 if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
333 file_badread(ms);
334 return -1;
336 off += size;
338 if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
339 /* Perhaps warn here */
340 continue;
343 if (xph_type != PT_NOTE)
344 continue;
347 * This is a PT_NOTE section; loop through all the notes
348 * in the section.
350 len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf);
351 if ((bufsize = pread(fd, nbuf, len, xph_offset)) == -1) {
352 file_badread(ms);
353 return -1;
355 offset = 0;
356 for (;;) {
357 if (offset >= (size_t)bufsize)
358 break;
359 offset = donote(ms, nbuf, offset, (size_t)bufsize,
360 clazz, swap, 4, flags, notecount);
361 if (offset == 0)
362 break;
366 return 0;
368 #endif
370 static void
371 do_note_netbsd_version(struct magic_set *ms, int swap, void *v)
373 uint32_t desc;
374 (void)memcpy(&desc, v, sizeof(desc));
375 desc = elf_getu32(swap, desc);
377 if (file_printf(ms, ", for NetBSD") == -1)
378 return;
380 * The version number used to be stuck as 199905, and was thus
381 * basically content-free. Newer versions of NetBSD have fixed
382 * this and now use the encoding of __NetBSD_Version__:
384 * MMmmrrpp00
386 * M = major version
387 * m = minor version
388 * r = release ["",A-Z,Z[A-Z] but numeric]
389 * p = patchlevel
391 if (desc > 100000000U) {
392 uint32_t ver_patch = (desc / 100) % 100;
393 uint32_t ver_rel = (desc / 10000) % 100;
394 uint32_t ver_min = (desc / 1000000) % 100;
395 uint32_t ver_maj = desc / 100000000;
397 if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1)
398 return;
399 if (ver_rel == 0 && ver_patch != 0) {
400 if (file_printf(ms, ".%u", ver_patch) == -1)
401 return;
402 } else if (ver_rel != 0) {
403 while (ver_rel > 26) {
404 if (file_printf(ms, "Z") == -1)
405 return;
406 ver_rel -= 26;
408 if (file_printf(ms, "%c", 'A' + ver_rel - 1)
409 == -1)
410 return;
415 static void
416 do_note_freebsd_version(struct magic_set *ms, int swap, void *v)
418 uint32_t desc;
420 (void)memcpy(&desc, v, sizeof(desc));
421 desc = elf_getu32(swap, desc);
422 if (file_printf(ms, ", for FreeBSD") == -1)
423 return;
426 * Contents is __FreeBSD_version, whose relation to OS
427 * versions is defined by a huge table in the Porter's
428 * Handbook. This is the general scheme:
430 * Releases:
431 * Mmp000 (before 4.10)
432 * Mmi0p0 (before 5.0)
433 * Mmm0p0
435 * Development branches:
436 * Mmpxxx (before 4.6)
437 * Mmp1xx (before 4.10)
438 * Mmi1xx (before 5.0)
439 * M000xx (pre-M.0)
440 * Mmm1xx
442 * M = major version
443 * m = minor version
444 * i = minor version increment (491000 -> 4.10)
445 * p = patchlevel
446 * x = revision
448 * The first release of FreeBSD to use ELF by default
449 * was version 3.0.
451 if (desc == 460002) {
452 if (file_printf(ms, " 4.6.2") == -1)
453 return;
454 } else if (desc < 460100) {
455 if (file_printf(ms, " %d.%d", desc / 100000,
456 desc / 10000 % 10) == -1)
457 return;
458 if (desc / 1000 % 10 > 0)
459 if (file_printf(ms, ".%d", desc / 1000 % 10) == -1)
460 return;
461 if ((desc % 1000 > 0) || (desc % 100000 == 0))
462 if (file_printf(ms, " (%d)", desc) == -1)
463 return;
464 } else if (desc < 500000) {
465 if (file_printf(ms, " %d.%d", desc / 100000,
466 desc / 10000 % 10 + desc / 1000 % 10) == -1)
467 return;
468 if (desc / 100 % 10 > 0) {
469 if (file_printf(ms, " (%d)", desc) == -1)
470 return;
471 } else if (desc / 10 % 10 > 0) {
472 if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
473 return;
475 } else {
476 if (file_printf(ms, " %d.%d", desc / 100000,
477 desc / 1000 % 100) == -1)
478 return;
479 if ((desc / 100 % 10 > 0) ||
480 (desc % 100000 / 100 == 0)) {
481 if (file_printf(ms, " (%d)", desc) == -1)
482 return;
483 } else if (desc / 10 % 10 > 0) {
484 if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
485 return;
490 private int
491 /*ARGSUSED*/
492 do_bid_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
493 int swap __attribute__((__unused__)), uint32_t namesz, uint32_t descsz,
494 size_t noff, size_t doff, int *flags)
496 if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
497 type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) {
498 uint8_t desc[20];
499 uint32_t i;
500 *flags |= FLAGS_DID_BUILD_ID;
501 if (file_printf(ms, ", BuildID[%s]=", descsz == 16 ? "md5/uuid" :
502 "sha1") == -1)
503 return 1;
504 (void)memcpy(desc, &nbuf[doff], descsz);
505 for (i = 0; i < descsz; i++)
506 if (file_printf(ms, "%02x", desc[i]) == -1)
507 return 1;
508 return 1;
510 return 0;
513 private int
514 do_os_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
515 int swap, uint32_t namesz, uint32_t descsz,
516 size_t noff, size_t doff, int *flags)
518 if (namesz == 5 && strcmp((char *)&nbuf[noff], "SuSE") == 0 &&
519 type == NT_GNU_VERSION && descsz == 2) {
520 *flags |= FLAGS_DID_OS_NOTE;
521 file_printf(ms, ", for SuSE %d.%d", nbuf[doff], nbuf[doff + 1]);
522 return 1;
525 if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
526 type == NT_GNU_VERSION && descsz == 16) {
527 uint32_t desc[4];
528 (void)memcpy(desc, &nbuf[doff], sizeof(desc));
530 *flags |= FLAGS_DID_OS_NOTE;
531 if (file_printf(ms, ", for GNU/") == -1)
532 return 1;
533 switch (elf_getu32(swap, desc[0])) {
534 case GNU_OS_LINUX:
535 if (file_printf(ms, "Linux") == -1)
536 return 1;
537 break;
538 case GNU_OS_HURD:
539 if (file_printf(ms, "Hurd") == -1)
540 return 1;
541 break;
542 case GNU_OS_SOLARIS:
543 if (file_printf(ms, "Solaris") == -1)
544 return 1;
545 break;
546 case GNU_OS_KFREEBSD:
547 if (file_printf(ms, "kFreeBSD") == -1)
548 return 1;
549 break;
550 case GNU_OS_KNETBSD:
551 if (file_printf(ms, "kNetBSD") == -1)
552 return 1;
553 break;
554 default:
555 if (file_printf(ms, "<unknown>") == -1)
556 return 1;
558 if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
559 elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
560 return 1;
561 return 1;
564 if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) {
565 if (type == NT_NETBSD_VERSION && descsz == 4) {
566 *flags |= FLAGS_DID_OS_NOTE;
567 do_note_netbsd_version(ms, swap, &nbuf[doff]);
568 return 1;
572 if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0) {
573 if (type == NT_FREEBSD_VERSION && descsz == 4) {
574 *flags |= FLAGS_DID_OS_NOTE;
575 do_note_freebsd_version(ms, swap, &nbuf[doff]);
576 return 1;
580 if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 &&
581 type == NT_OPENBSD_VERSION && descsz == 4) {
582 *flags |= FLAGS_DID_OS_NOTE;
583 if (file_printf(ms, ", for OpenBSD") == -1)
584 return 1;
585 /* Content of note is always 0 */
586 return 1;
589 if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 &&
590 type == NT_DRAGONFLY_VERSION && descsz == 4) {
591 uint32_t desc;
592 *flags |= FLAGS_DID_OS_NOTE;
593 if (file_printf(ms, ", for DragonFly") == -1)
594 return 1;
595 (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
596 desc = elf_getu32(swap, desc);
597 if (file_printf(ms, " %d.%d.%d", desc / 100000,
598 desc / 10000 % 10, desc % 10000) == -1)
599 return 1;
600 return 1;
602 return 0;
605 private int
606 do_pax_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
607 int swap, uint32_t namesz, uint32_t descsz,
608 size_t noff, size_t doff, int *flags)
610 if (namesz == 4 && strcmp((char *)&nbuf[noff], "PaX") == 0 &&
611 type == NT_NETBSD_PAX && descsz == 4) {
612 static const char *pax[] = {
613 "+mprotect",
614 "-mprotect",
615 "+segvguard",
616 "-segvguard",
617 "+ASLR",
618 "-ASLR",
620 uint32_t desc;
621 size_t i;
622 int did = 0;
624 *flags |= FLAGS_DID_NETBSD_PAX;
625 (void)memcpy(&desc, &nbuf[doff], sizeof(desc));
626 desc = elf_getu32(swap, desc);
628 if (desc && file_printf(ms, ", PaX: ") == -1)
629 return 1;
631 for (i = 0; i < __arraycount(pax); i++) {
632 if (((1 << (int)i) & desc) == 0)
633 continue;
634 if (file_printf(ms, "%s%s", did++ ? "," : "",
635 pax[i]) == -1)
636 return 1;
638 return 1;
640 return 0;
643 private int
644 do_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
645 int swap, uint32_t namesz, uint32_t descsz,
646 size_t noff, size_t doff, int *flags, size_t size, int clazz)
648 #ifdef ELFCORE
649 int os_style = -1;
651 * Sigh. The 2.0.36 kernel in Debian 2.1, at
652 * least, doesn't correctly implement name
653 * sections, in core dumps, as specified by
654 * the "Program Linking" section of "UNIX(R) System
655 * V Release 4 Programmer's Guide: ANSI C and
656 * Programming Support Tools", because my copy
657 * clearly says "The first 'namesz' bytes in 'name'
658 * contain a *null-terminated* [emphasis mine]
659 * character representation of the entry's owner
660 * or originator", but the 2.0.36 kernel code
661 * doesn't include the terminating null in the
662 * name....
664 if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) ||
665 (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) {
666 os_style = OS_STYLE_SVR4;
669 if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
670 os_style = OS_STYLE_FREEBSD;
673 if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
674 == 0)) {
675 os_style = OS_STYLE_NETBSD;
678 if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
679 if (file_printf(ms, ", %s-style", os_style_names[os_style])
680 == -1)
681 return 1;
682 *flags |= FLAGS_DID_CORE_STYLE;
685 switch (os_style) {
686 case OS_STYLE_NETBSD:
687 if (type == NT_NETBSD_CORE_PROCINFO) {
688 char sbuf[512];
689 uint32_t signo;
691 * Extract the program name. It is at
692 * offset 0x7c, and is up to 32-bytes,
693 * including the terminating NUL.
695 if (file_printf(ms, ", from '%.31s'",
696 file_printable(sbuf, sizeof(sbuf),
697 (const char *)&nbuf[doff + 0x7c])) == -1)
698 return 1;
701 * Extract the signal number. It is at
702 * offset 0x08.
704 (void)memcpy(&signo, &nbuf[doff + 0x08],
705 sizeof(signo));
706 if (file_printf(ms, " (signal %u)",
707 elf_getu32(swap, signo)) == -1)
708 return 1;
709 *flags |= FLAGS_DID_CORE;
710 return 1;
712 break;
714 default:
715 if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
716 size_t i, j;
717 unsigned char c;
719 * Extract the program name. We assume
720 * it to be 16 characters (that's what it
721 * is in SunOS 5.x and Linux).
723 * Unfortunately, it's at a different offset
724 * in various OSes, so try multiple offsets.
725 * If the characters aren't all printable,
726 * reject it.
728 for (i = 0; i < NOFFSETS; i++) {
729 unsigned char *cname, *cp;
730 size_t reloffset = prpsoffsets(i);
731 size_t noffset = doff + reloffset;
732 size_t k;
733 for (j = 0; j < 16; j++, noffset++,
734 reloffset++) {
736 * Make sure we're not past
737 * the end of the buffer; if
738 * we are, just give up.
740 if (noffset >= size)
741 goto tryanother;
744 * Make sure we're not past
745 * the end of the contents;
746 * if we are, this obviously
747 * isn't the right offset.
749 if (reloffset >= descsz)
750 goto tryanother;
752 c = nbuf[noffset];
753 if (c == '\0') {
755 * A '\0' at the
756 * beginning is
757 * obviously wrong.
758 * Any other '\0'
759 * means we're done.
761 if (j == 0)
762 goto tryanother;
763 else
764 break;
765 } else {
767 * A nonprintable
768 * character is also
769 * wrong.
771 if (!isprint(c) || isquote(c))
772 goto tryanother;
776 * Well, that worked.
780 * Try next offsets, in case this match is
781 * in the middle of a string.
783 for (k = i + 1 ; k < NOFFSETS; k++) {
784 size_t no;
785 int adjust = 1;
786 if (prpsoffsets(k) >= prpsoffsets(i))
787 continue;
788 for (no = doff + prpsoffsets(k);
789 no < doff + prpsoffsets(i); no++)
790 adjust = adjust
791 && isprint(nbuf[no]);
792 if (adjust)
793 i = k;
796 cname = (unsigned char *)
797 &nbuf[doff + prpsoffsets(i)];
798 for (cp = cname; *cp && isprint(*cp); cp++)
799 continue;
801 * Linux apparently appends a space at the end
802 * of the command line: remove it.
804 while (cp > cname && isspace(cp[-1]))
805 cp--;
806 if (file_printf(ms, ", from '%.*s'",
807 (int)(cp - cname), cname) == -1)
808 return 1;
809 *flags |= FLAGS_DID_CORE;
810 return 1;
812 tryanother:
816 break;
818 #endif
819 return 0;
822 private size_t
823 donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
824 int clazz, int swap, size_t align, int *flags, uint16_t *notecount)
826 Elf32_Nhdr nh32;
827 Elf64_Nhdr nh64;
828 size_t noff, doff;
829 uint32_t namesz, descsz;
830 unsigned char *nbuf = CAST(unsigned char *, vbuf);
832 if (*notecount == 0)
833 return 0;
834 --*notecount;
836 if (xnh_sizeof + offset > size) {
838 * We're out of note headers.
840 return xnh_sizeof + offset;
843 (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
844 offset += xnh_sizeof;
846 namesz = xnh_namesz;
847 descsz = xnh_descsz;
848 if ((namesz == 0) && (descsz == 0)) {
850 * We're out of note headers.
852 return (offset >= size) ? offset : size;
855 if (namesz & 0x80000000) {
856 (void)file_printf(ms, ", bad note name size 0x%lx",
857 (unsigned long)namesz);
858 return 0;
861 if (descsz & 0x80000000) {
862 (void)file_printf(ms, ", bad note description size 0x%lx",
863 (unsigned long)descsz);
864 return 0;
867 noff = offset;
868 doff = ELF_ALIGN(offset + namesz);
870 if (offset + namesz > size) {
872 * We're past the end of the buffer.
874 return doff;
877 offset = ELF_ALIGN(doff + descsz);
878 if (doff + descsz > size) {
880 * We're past the end of the buffer.
882 return (offset >= size) ? offset : size;
885 if ((*flags & FLAGS_DID_OS_NOTE) == 0) {
886 if (do_os_note(ms, nbuf, xnh_type, swap,
887 namesz, descsz, noff, doff, flags))
888 return size;
891 if ((*flags & FLAGS_DID_BUILD_ID) == 0) {
892 if (do_bid_note(ms, nbuf, xnh_type, swap,
893 namesz, descsz, noff, doff, flags))
894 return size;
897 if ((*flags & FLAGS_DID_NETBSD_PAX) == 0) {
898 if (do_pax_note(ms, nbuf, xnh_type, swap,
899 namesz, descsz, noff, doff, flags))
900 return size;
903 if ((*flags & FLAGS_DID_CORE) == 0) {
904 if (do_core_note(ms, nbuf, xnh_type, swap,
905 namesz, descsz, noff, doff, flags, size, clazz))
906 return size;
909 if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) {
910 if (descsz > 100)
911 descsz = 100;
912 switch (xnh_type) {
913 case NT_NETBSD_VERSION:
914 return size;
915 case NT_NETBSD_MARCH:
916 if (*flags & FLAGS_DID_NETBSD_MARCH)
917 return size;
918 *flags |= FLAGS_DID_NETBSD_MARCH;
919 if (file_printf(ms, ", compiled for: %.*s",
920 (int)descsz, (const char *)&nbuf[doff]) == -1)
921 return size;
922 break;
923 case NT_NETBSD_CMODEL:
924 if (*flags & FLAGS_DID_NETBSD_CMODEL)
925 return size;
926 *flags |= FLAGS_DID_NETBSD_CMODEL;
927 if (file_printf(ms, ", compiler model: %.*s",
928 (int)descsz, (const char *)&nbuf[doff]) == -1)
929 return size;
930 break;
931 default:
932 if (*flags & FLAGS_DID_NETBSD_UNKNOWN)
933 return size;
934 *flags |= FLAGS_DID_NETBSD_UNKNOWN;
935 if (file_printf(ms, ", note=%u", xnh_type) == -1)
936 return size;
937 break;
939 return size;
942 return offset;
945 /* SunOS 5.x hardware capability descriptions */
946 typedef struct cap_desc {
947 uint64_t cd_mask;
948 const char *cd_name;
949 } cap_desc_t;
951 static const cap_desc_t cap_desc_sparc[] = {
952 { AV_SPARC_MUL32, "MUL32" },
953 { AV_SPARC_DIV32, "DIV32" },
954 { AV_SPARC_FSMULD, "FSMULD" },
955 { AV_SPARC_V8PLUS, "V8PLUS" },
956 { AV_SPARC_POPC, "POPC" },
957 { AV_SPARC_VIS, "VIS" },
958 { AV_SPARC_VIS2, "VIS2" },
959 { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" },
960 { AV_SPARC_FMAF, "FMAF" },
961 { AV_SPARC_FJFMAU, "FJFMAU" },
962 { AV_SPARC_IMA, "IMA" },
963 { 0, NULL }
966 static const cap_desc_t cap_desc_386[] = {
967 { AV_386_FPU, "FPU" },
968 { AV_386_TSC, "TSC" },
969 { AV_386_CX8, "CX8" },
970 { AV_386_SEP, "SEP" },
971 { AV_386_AMD_SYSC, "AMD_SYSC" },
972 { AV_386_CMOV, "CMOV" },
973 { AV_386_MMX, "MMX" },
974 { AV_386_AMD_MMX, "AMD_MMX" },
975 { AV_386_AMD_3DNow, "AMD_3DNow" },
976 { AV_386_AMD_3DNowx, "AMD_3DNowx" },
977 { AV_386_FXSR, "FXSR" },
978 { AV_386_SSE, "SSE" },
979 { AV_386_SSE2, "SSE2" },
980 { AV_386_PAUSE, "PAUSE" },
981 { AV_386_SSE3, "SSE3" },
982 { AV_386_MON, "MON" },
983 { AV_386_CX16, "CX16" },
984 { AV_386_AHF, "AHF" },
985 { AV_386_TSCP, "TSCP" },
986 { AV_386_AMD_SSE4A, "AMD_SSE4A" },
987 { AV_386_POPCNT, "POPCNT" },
988 { AV_386_AMD_LZCNT, "AMD_LZCNT" },
989 { AV_386_SSSE3, "SSSE3" },
990 { AV_386_SSE4_1, "SSE4.1" },
991 { AV_386_SSE4_2, "SSE4.2" },
992 { 0, NULL }
995 private int
996 doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
997 size_t size, off_t fsize, int mach, int strtab, int *flags,
998 uint16_t *notecount)
1000 Elf32_Shdr sh32;
1001 Elf64_Shdr sh64;
1002 int stripped = 1;
1003 size_t nbadcap = 0;
1004 void *nbuf;
1005 off_t noff, coff, name_off;
1006 uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */
1007 uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */
1008 char name[50];
1009 ssize_t namesize;
1011 if (size != xsh_sizeof) {
1012 if (file_printf(ms, ", corrupted section header size") == -1)
1013 return -1;
1014 return 0;
1017 /* Read offset of name section to be able to read section names later */
1018 if (pread(fd, xsh_addr, xsh_sizeof, CAST(off_t, (off + size * strtab)))
1019 < (ssize_t)xsh_sizeof) {
1020 file_badread(ms);
1021 return -1;
1023 name_off = xsh_offset;
1025 for ( ; num; num--) {
1026 /* Read the name of this section. */
1027 if ((namesize = pread(fd, name, sizeof(name) - 1, name_off + xsh_name)) == -1) {
1028 file_badread(ms);
1029 return -1;
1031 name[namesize] = '\0';
1032 if (strcmp(name, ".debug_info") == 0)
1033 stripped = 0;
1035 if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) {
1036 file_badread(ms);
1037 return -1;
1039 off += size;
1041 /* Things we can determine before we seek */
1042 switch (xsh_type) {
1043 case SHT_SYMTAB:
1044 #if 0
1045 case SHT_DYNSYM:
1046 #endif
1047 stripped = 0;
1048 break;
1049 default:
1050 if (fsize != SIZE_UNKNOWN && xsh_offset > fsize) {
1051 /* Perhaps warn here */
1052 continue;
1054 break;
1057 /* Things we can determine when we seek */
1058 switch (xsh_type) {
1059 case SHT_NOTE:
1060 if ((nbuf = malloc(xsh_size)) == NULL) {
1061 file_error(ms, errno, "Cannot allocate memory"
1062 " for note");
1063 return -1;
1065 if (pread(fd, nbuf, xsh_size, xsh_offset) < (ssize_t)xsh_size) {
1066 file_badread(ms);
1067 free(nbuf);
1068 return -1;
1071 noff = 0;
1072 for (;;) {
1073 if (noff >= (off_t)xsh_size)
1074 break;
1075 noff = donote(ms, nbuf, (size_t)noff,
1076 xsh_size, clazz, swap, 4, flags, notecount);
1077 if (noff == 0)
1078 break;
1080 free(nbuf);
1081 break;
1082 case SHT_SUNW_cap:
1083 switch (mach) {
1084 case EM_SPARC:
1085 case EM_SPARCV9:
1086 case EM_IA_64:
1087 case EM_386:
1088 case EM_AMD64:
1089 break;
1090 default:
1091 goto skip;
1094 if (nbadcap > 5)
1095 break;
1096 if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) {
1097 file_badseek(ms);
1098 return -1;
1100 coff = 0;
1101 for (;;) {
1102 Elf32_Cap cap32;
1103 Elf64_Cap cap64;
1104 char cbuf[/*CONSTCOND*/
1105 MAX(sizeof cap32, sizeof cap64)];
1106 if ((coff += xcap_sizeof) > (off_t)xsh_size)
1107 break;
1108 if (read(fd, cbuf, (size_t)xcap_sizeof) !=
1109 (ssize_t)xcap_sizeof) {
1110 file_badread(ms);
1111 return -1;
1113 if (cbuf[0] == 'A') {
1114 #ifdef notyet
1115 char *p = cbuf + 1;
1116 uint32_t len, tag;
1117 memcpy(&len, p, sizeof(len));
1118 p += 4;
1119 len = getu32(swap, len);
1120 if (memcmp("gnu", p, 3) != 0) {
1121 if (file_printf(ms,
1122 ", unknown capability %.3s", p)
1123 == -1)
1124 return -1;
1125 break;
1127 p += strlen(p) + 1;
1128 tag = *p++;
1129 memcpy(&len, p, sizeof(len));
1130 p += 4;
1131 len = getu32(swap, len);
1132 if (tag != 1) {
1133 if (file_printf(ms, ", unknown gnu"
1134 " capability tag %d", tag)
1135 == -1)
1136 return -1;
1137 break;
1139 // gnu attributes
1140 #endif
1141 break;
1143 (void)memcpy(xcap_addr, cbuf, xcap_sizeof);
1144 switch (xcap_tag) {
1145 case CA_SUNW_NULL:
1146 break;
1147 case CA_SUNW_HW_1:
1148 cap_hw1 |= xcap_val;
1149 break;
1150 case CA_SUNW_SF_1:
1151 cap_sf1 |= xcap_val;
1152 break;
1153 default:
1154 if (file_printf(ms,
1155 ", with unknown capability "
1156 "0x%" INT64_T_FORMAT "x = 0x%"
1157 INT64_T_FORMAT "x",
1158 (unsigned long long)xcap_tag,
1159 (unsigned long long)xcap_val) == -1)
1160 return -1;
1161 if (nbadcap++ > 2)
1162 coff = xsh_size;
1163 break;
1166 /*FALLTHROUGH*/
1167 skip:
1168 default:
1169 break;
1173 if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
1174 return -1;
1175 if (cap_hw1) {
1176 const cap_desc_t *cdp;
1177 switch (mach) {
1178 case EM_SPARC:
1179 case EM_SPARC32PLUS:
1180 case EM_SPARCV9:
1181 cdp = cap_desc_sparc;
1182 break;
1183 case EM_386:
1184 case EM_IA_64:
1185 case EM_AMD64:
1186 cdp = cap_desc_386;
1187 break;
1188 default:
1189 cdp = NULL;
1190 break;
1192 if (file_printf(ms, ", uses") == -1)
1193 return -1;
1194 if (cdp) {
1195 while (cdp->cd_name) {
1196 if (cap_hw1 & cdp->cd_mask) {
1197 if (file_printf(ms,
1198 " %s", cdp->cd_name) == -1)
1199 return -1;
1200 cap_hw1 &= ~cdp->cd_mask;
1202 ++cdp;
1204 if (cap_hw1)
1205 if (file_printf(ms,
1206 " unknown hardware capability 0x%"
1207 INT64_T_FORMAT "x",
1208 (unsigned long long)cap_hw1) == -1)
1209 return -1;
1210 } else {
1211 if (file_printf(ms,
1212 " hardware capability 0x%" INT64_T_FORMAT "x",
1213 (unsigned long long)cap_hw1) == -1)
1214 return -1;
1217 if (cap_sf1) {
1218 if (cap_sf1 & SF1_SUNW_FPUSED) {
1219 if (file_printf(ms,
1220 (cap_sf1 & SF1_SUNW_FPKNWN)
1221 ? ", uses frame pointer"
1222 : ", not known to use frame pointer") == -1)
1223 return -1;
1225 cap_sf1 &= ~SF1_SUNW_MASK;
1226 if (cap_sf1)
1227 if (file_printf(ms,
1228 ", with unknown software capability 0x%"
1229 INT64_T_FORMAT "x",
1230 (unsigned long long)cap_sf1) == -1)
1231 return -1;
1233 return 0;
1237 * Look through the program headers of an executable image, searching
1238 * for a PT_INTERP section; if one is found, it's dynamically linked,
1239 * otherwise it's statically linked.
1241 private int
1242 dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
1243 int num, size_t size, off_t fsize, int sh_num, int *flags,
1244 uint16_t *notecount)
1246 Elf32_Phdr ph32;
1247 Elf64_Phdr ph64;
1248 const char *linking_style = "statically";
1249 const char *interp = "";
1250 unsigned char nbuf[BUFSIZ];
1251 char ibuf[BUFSIZ];
1252 ssize_t bufsize;
1253 size_t offset, align, len;
1255 if (size != xph_sizeof) {
1256 if (file_printf(ms, ", corrupted program header size") == -1)
1257 return -1;
1258 return 0;
1261 for ( ; num; num--) {
1262 if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
1263 file_badread(ms);
1264 return -1;
1267 off += size;
1268 bufsize = 0;
1269 align = 4;
1271 /* Things we can determine before we seek */
1272 switch (xph_type) {
1273 case PT_DYNAMIC:
1274 linking_style = "dynamically";
1275 break;
1276 case PT_NOTE:
1277 if (sh_num) /* Did this through section headers */
1278 continue;
1279 if (((align = xph_align) & 0x80000000UL) != 0 ||
1280 align < 4) {
1281 if (file_printf(ms,
1282 ", invalid note alignment 0x%lx",
1283 (unsigned long)align) == -1)
1284 return -1;
1285 align = 4;
1287 /*FALLTHROUGH*/
1288 case PT_INTERP:
1289 len = xph_filesz < sizeof(nbuf) ? xph_filesz
1290 : sizeof(nbuf);
1291 bufsize = pread(fd, nbuf, len, xph_offset);
1292 if (bufsize == -1) {
1293 file_badread(ms);
1294 return -1;
1296 break;
1297 default:
1298 if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
1299 /* Maybe warn here? */
1300 continue;
1302 break;
1305 /* Things we can determine when we seek */
1306 switch (xph_type) {
1307 case PT_INTERP:
1308 if (bufsize && nbuf[0]) {
1309 nbuf[bufsize - 1] = '\0';
1310 interp = (const char *)nbuf;
1311 } else
1312 interp = "*empty*";
1313 break;
1314 case PT_NOTE:
1316 * This is a PT_NOTE section; loop through all the notes
1317 * in the section.
1319 offset = 0;
1320 for (;;) {
1321 if (offset >= (size_t)bufsize)
1322 break;
1323 offset = donote(ms, nbuf, offset,
1324 (size_t)bufsize, clazz, swap, align,
1325 flags, notecount);
1326 if (offset == 0)
1327 break;
1329 break;
1330 default:
1331 break;
1334 if (file_printf(ms, ", %s linked", linking_style)
1335 == -1)
1336 return -1;
1337 if (interp[0])
1338 if (file_printf(ms, ", interpreter %s",
1339 file_printable(ibuf, sizeof(ibuf), interp)) == -1)
1340 return -1;
1341 return 0;
1345 protected int
1346 file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
1347 size_t nbytes)
1349 union {
1350 int32_t l;
1351 char c[sizeof (int32_t)];
1352 } u;
1353 int clazz;
1354 int swap;
1355 struct stat st;
1356 off_t fsize;
1357 int flags = 0;
1358 Elf32_Ehdr elf32hdr;
1359 Elf64_Ehdr elf64hdr;
1360 uint16_t type, phnum, shnum, notecount;
1362 if (ms->flags & (MAGIC_MIME|MAGIC_APPLE))
1363 return 0;
1365 * ELF executables have multiple section headers in arbitrary
1366 * file locations and thus file(1) cannot determine it from easily.
1367 * Instead we traverse thru all section headers until a symbol table
1368 * one is found or else the binary is stripped.
1369 * Return immediately if it's not ELF (so we avoid pipe2file unless needed).
1371 if (buf[EI_MAG0] != ELFMAG0
1372 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
1373 || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
1374 return 0;
1377 * If we cannot seek, it must be a pipe, socket or fifo.
1379 if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
1380 fd = file_pipe2file(ms, fd, buf, nbytes);
1382 if (fstat(fd, &st) == -1) {
1383 file_badread(ms);
1384 return -1;
1386 if (S_ISREG(st.st_mode) || st.st_size != 0)
1387 fsize = st.st_size;
1388 else
1389 fsize = SIZE_UNKNOWN;
1391 clazz = buf[EI_CLASS];
1393 switch (clazz) {
1394 case ELFCLASS32:
1395 #undef elf_getu
1396 #define elf_getu(a, b) elf_getu32(a, b)
1397 #undef elfhdr
1398 #define elfhdr elf32hdr
1399 #include "elfclass.h"
1400 case ELFCLASS64:
1401 #undef elf_getu
1402 #define elf_getu(a, b) elf_getu64(a, b)
1403 #undef elfhdr
1404 #define elfhdr elf64hdr
1405 #include "elfclass.h"
1406 default:
1407 if (file_printf(ms, ", unknown class %d", clazz) == -1)
1408 return -1;
1409 break;
1411 return 0;
1413 #endif