readelf.c revision 1.11
1/*	$NetBSD: readelf.c,v 1.11 2015/01/02 21:15:32 christos Exp $	*/
2
3/*
4 * Copyright (c) Christos Zoulas 2003.
5 * All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice immediately at the beginning of the file, without modification,
12 *    this list of conditions, and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29#include "file.h"
30
31#ifndef lint
32#if 0
33FILE_RCSID("@(#)$File: readelf.c,v 1.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
38
39#ifdef BUILTIN_ELF
40#include <string.h>
41#include <ctype.h>
42#include <stdlib.h>
43#ifdef HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46
47#include "readelf.h"
48#include "magic.h"
49
50#ifdef	ELFCORE
51private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t,
52    off_t, int *, uint16_t *);
53#endif
54private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t,
55    off_t, int, int *, uint16_t *);
56private int doshn(struct magic_set *, int, int, int, off_t, int, size_t,
57    off_t, int, int, int *, uint16_t *);
58private size_t donote(struct magic_set *, void *, size_t, size_t, int,
59    int, size_t, int *, uint16_t *);
60
61#define	ELF_ALIGN(a)	((((a) + align - 1) / align) * align)
62
63#define isquote(c) (strchr("'\"`", (c)) != NULL)
64
65private uint16_t getu16(int, uint16_t);
66private uint32_t getu32(int, uint32_t);
67private uint64_t getu64(int, uint64_t);
68
69#define MAX_PHNUM	128
70#define	MAX_SHNUM	32768
71#define SIZE_UNKNOWN	((off_t)-1)
72
73private int
74toomany(struct magic_set *ms, const char *name, uint16_t num)
75{
76	if (file_printf(ms, ", too many %s (%u)", name, num
77	    ) == -1)
78		return -1;
79	return 0;
80}
81
82private uint16_t
83getu16(int swap, uint16_t value)
84{
85	union {
86		uint16_t ui;
87		char c[2];
88	} retval, tmpval;
89
90	if (swap) {
91		tmpval.ui = value;
92
93		retval.c[0] = tmpval.c[1];
94		retval.c[1] = tmpval.c[0];
95
96		return retval.ui;
97	} else
98		return value;
99}
100
101private uint32_t
102getu32(int swap, uint32_t value)
103{
104	union {
105		uint32_t ui;
106		char c[4];
107	} retval, tmpval;
108
109	if (swap) {
110		tmpval.ui = value;
111
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];
116
117		return retval.ui;
118	} else
119		return value;
120}
121
122private uint64_t
123getu64(int swap, uint64_t value)
124{
125	union {
126		uint64_t ui;
127		char c[8];
128	} retval, tmpval;
129
130	if (swap) {
131		tmpval.ui = value;
132
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];
141
142		return retval.ui;
143	} else
144		return value;
145}
146
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)
150
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))
222
223#ifdef ELFCORE
224/*
225 * Try larger offsets first to avoid false matches
226 * from earlier data that happen to look like strings.
227 */
228static 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 */
233
234	100,		/* SunOS 5.x (command line) */
235	84,		/* SunOS 5.x (short name) */
236
237	44,		/* Linux (command line) */
238	28,		/* Linux 2.0.36 (short name) */
239
240	8,		/* FreeBSD */
241};
242
243static 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 */
248
249	136,		/* SunOS 5.x, 64-bit (command line) */
250	120,		/* SunOS 5.x, 64-bit (short name) */
251
252	56,		/* Linux (command line) */
253	40,             /* Linux (tested on core from 2.4.x, short name) */
254
255	16,		/* FreeBSD, 64-bit */
256};
257
258#define	NOFFSETS32	(sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
259#define NOFFSETS64	(sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
260
261#define NOFFSETS	(clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
262
263/*
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.
274 *
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).
282 *
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.)
290 */
291
292#define	OS_STYLE_SVR4		0
293#define	OS_STYLE_FREEBSD	1
294#define	OS_STYLE_NETBSD		2
295
296private const char os_style_names[][8] = {
297	"SVR4",
298	"FreeBSD",
299	"NetBSD",
300};
301
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
311
312private int
313dophn_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)
315{
316	Elf32_Phdr ph32;
317	Elf64_Phdr ph64;
318	size_t offset, len;
319	unsigned char nbuf[BUFSIZ];
320	ssize_t bufsize;
321
322	if (size != xph_sizeof) {
323		if (file_printf(ms, ", corrupted program header size") == -1)
324			return -1;
325		return 0;
326	}
327
328	/*
329	 * Loop through all the program headers.
330	 */
331	for ( ; num; num--) {
332		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
333			file_badread(ms);
334			return -1;
335		}
336		off += size;
337
338		if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
339			/* Perhaps warn here */
340			continue;
341		}
342
343		if (xph_type != PT_NOTE)
344			continue;
345
346		/*
347		 * This is a PT_NOTE section; loop through all the notes
348		 * in the section.
349		 */
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;
354		}
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;
363
364		}
365	}
366	return 0;
367}
368#endif
369
370static void
371do_note_netbsd_version(struct magic_set *ms, int swap, void *v)
372{
373	uint32_t desc;
374	(void)memcpy(&desc, v, sizeof(desc));
375	desc = elf_getu32(swap, desc);
376
377	if (file_printf(ms, ", for NetBSD") == -1)
378		return;
379	/*
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__:
383	 *
384	 *	MMmmrrpp00
385	 *
386	 * M = major version
387	 * m = minor version
388	 * r = release ["",A-Z,Z[A-Z] but numeric]
389	 * p = patchlevel
390	 */
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;
396
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;
407			}
408			if (file_printf(ms, "%c", 'A' + ver_rel - 1)
409			    == -1)
410				return;
411		}
412	}
413}
414
415static void
416do_note_freebsd_version(struct magic_set *ms, int swap, void *v)
417{
418	uint32_t desc;
419
420	(void)memcpy(&desc, v, sizeof(desc));
421	desc = elf_getu32(swap, desc);
422	if (file_printf(ms, ", for FreeBSD") == -1)
423		return;
424
425	/*
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:
429	 *
430	 * Releases:
431	 * 	Mmp000 (before 4.10)
432	 * 	Mmi0p0 (before 5.0)
433	 * 	Mmm0p0
434	 *
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
441	 *
442	 * M = major version
443	 * m = minor version
444	 * i = minor version increment (491000 -> 4.10)
445	 * p = patchlevel
446	 * x = revision
447	 *
448	 * The first release of FreeBSD to use ELF by default
449	 * was version 3.0.
450	 */
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;
474		}
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;
486		}
487	}
488}
489
490private int
491/*ARGSUSED*/
492do_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)
495{
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;
509	}
510	return 0;
511}
512
513private int
514do_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)
517{
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;
523	}
524
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));
529
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;
557		}
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;
562	}
563
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;
569		}
570	}
571
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;
577		}
578	}
579
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;
587	}
588
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;
601	}
602	return 0;
603}
604
605private int
606do_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)
609{
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",
619		};
620		uint32_t desc;
621		size_t i;
622		int did = 0;
623
624		*flags |= FLAGS_DID_NETBSD_PAX;
625		(void)memcpy(&desc, &nbuf[doff], sizeof(desc));
626		desc = elf_getu32(swap, desc);
627
628		if (desc && file_printf(ms, ", PaX: ") == -1)
629			return 1;
630
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;
637		}
638		return 1;
639	}
640	return 0;
641}
642
643private int
644do_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)
647{
648#ifdef ELFCORE
649	int os_style = -1;
650	/*
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....
663	 */
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;
667	}
668
669	if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
670		os_style = OS_STYLE_FREEBSD;
671	}
672
673	if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
674	    == 0)) {
675		os_style = OS_STYLE_NETBSD;
676	}
677
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;
683	}
684
685	switch (os_style) {
686	case OS_STYLE_NETBSD:
687		if (type == NT_NETBSD_CORE_PROCINFO) {
688			char sbuf[512];
689			uint32_t signo;
690			/*
691			 * Extract the program name.  It is at
692			 * offset 0x7c, and is up to 32-bytes,
693			 * including the terminating NUL.
694			 */
695			if (file_printf(ms, ", from '%.31s'",
696			    file_printable(sbuf, sizeof(sbuf),
697			    (const char *)&nbuf[doff + 0x7c])) == -1)
698				return 1;
699
700			/*
701			 * Extract the signal number.  It is at
702			 * offset 0x08.
703			 */
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;
711		}
712		break;
713
714	default:
715		if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
716			size_t i, j;
717			unsigned char c;
718			/*
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).
722			 *
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.
727			 */
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++) {
735					/*
736					 * Make sure we're not past
737					 * the end of the buffer; if
738					 * we are, just give up.
739					 */
740					if (noffset >= size)
741						goto tryanother;
742
743					/*
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.
748					 */
749					if (reloffset >= descsz)
750						goto tryanother;
751
752					c = nbuf[noffset];
753					if (c == '\0') {
754						/*
755						 * A '\0' at the
756						 * beginning is
757						 * obviously wrong.
758						 * Any other '\0'
759						 * means we're done.
760						 */
761						if (j == 0)
762							goto tryanother;
763						else
764							break;
765					} else {
766						/*
767						 * A nonprintable
768						 * character is also
769						 * wrong.
770						 */
771						if (!isprint(c) || isquote(c))
772							goto tryanother;
773					}
774				}
775				/*
776				 * Well, that worked.
777				 */
778
779				/*
780				 * Try next offsets, in case this match is
781				 * in the middle of a string.
782				 */
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;
794				}
795
796				cname = (unsigned char *)
797				    &nbuf[doff + prpsoffsets(i)];
798				for (cp = cname; *cp && isprint(*cp); cp++)
799					continue;
800				/*
801				 * Linux apparently appends a space at the end
802				 * of the command line: remove it.
803				 */
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;
811
812			tryanother:
813				;
814			}
815		}
816		break;
817	}
818#endif
819	return 0;
820}
821
822private size_t
823donote(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)
825{
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);
831
832	if (*notecount == 0)
833		return 0;
834	--*notecount;
835
836	if (xnh_sizeof + offset > size) {
837		/*
838		 * We're out of note headers.
839		 */
840		return xnh_sizeof + offset;
841	}
842
843	(void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
844	offset += xnh_sizeof;
845
846	namesz = xnh_namesz;
847	descsz = xnh_descsz;
848	if ((namesz == 0) && (descsz == 0)) {
849		/*
850		 * We're out of note headers.
851		 */
852		return (offset >= size) ? offset : size;
853	}
854
855	if (namesz & 0x80000000) {
856	    (void)file_printf(ms, ", bad note name size 0x%lx",
857		(unsigned long)namesz);
858	    return 0;
859	}
860
861	if (descsz & 0x80000000) {
862	    (void)file_printf(ms, ", bad note description size 0x%lx",
863		(unsigned long)descsz);
864	    return 0;
865	}
866
867	noff = offset;
868	doff = ELF_ALIGN(offset + namesz);
869
870	if (offset + namesz > size) {
871		/*
872		 * We're past the end of the buffer.
873		 */
874		return doff;
875	}
876
877	offset = ELF_ALIGN(doff + descsz);
878	if (doff + descsz > size) {
879		/*
880		 * We're past the end of the buffer.
881		 */
882		return (offset >= size) ? offset : size;
883	}
884
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;
889	}
890
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;
895	}
896
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;
901	}
902
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;
907	}
908
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;
938		}
939		return size;
940	}
941
942	return offset;
943}
944
945/* SunOS 5.x hardware capability descriptions */
946typedef struct cap_desc {
947	uint64_t cd_mask;
948	const char *cd_name;
949} cap_desc_t;
950
951static 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 }
964};
965
966static 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 }
993};
994
995private int
996doshn(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)
999{
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;
1010
1011	if (size != xsh_sizeof) {
1012		if (file_printf(ms, ", corrupted section header size") == -1)
1013			return -1;
1014		return 0;
1015	}
1016
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;
1022	}
1023	name_off = xsh_offset;
1024
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;
1030		}
1031		name[namesize] = '\0';
1032		if (strcmp(name, ".debug_info") == 0)
1033			stripped = 0;
1034
1035		if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) {
1036			file_badread(ms);
1037			return -1;
1038		}
1039		off += size;
1040
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;
1053			}
1054			break;
1055		}
1056
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;
1064			}
1065			if (pread(fd, nbuf, xsh_size, xsh_offset) < (ssize_t)xsh_size) {
1066				file_badread(ms);
1067				free(nbuf);
1068				return -1;
1069			}
1070
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;
1079			}
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;
1092			}
1093
1094			if (nbadcap > 5)
1095				break;
1096			if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) {
1097				file_badseek(ms);
1098				return -1;
1099			}
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;
1112				}
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;
1126					}
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;
1138					}
1139					// gnu attributes
1140#endif
1141					break;
1142				}
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;
1164				}
1165			}
1166			/*FALLTHROUGH*/
1167		skip:
1168		default:
1169			break;
1170		}
1171	}
1172
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;
1191		}
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;
1201				}
1202				++cdp;
1203			}
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;
1215		}
1216	}
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;
1224		}
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;
1232	}
1233	return 0;
1234}
1235
1236/*
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.
1240 */
1241private int
1242dophn_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)
1245{
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;
1254
1255	if (size != xph_sizeof) {
1256		if (file_printf(ms, ", corrupted program header size") == -1)
1257			return -1;
1258		return 0;
1259	}
1260
1261  	for ( ; num; num--) {
1262		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
1263			file_badread(ms);
1264			return -1;
1265		}
1266
1267		off += size;
1268		bufsize = 0;
1269		align = 4;
1270
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;
1286			}
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;
1295			}
1296			break;
1297		default:
1298			if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
1299				/* Maybe warn here? */
1300				continue;
1301			}
1302			break;
1303		}
1304
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:
1315			/*
1316			 * This is a PT_NOTE section; loop through all the notes
1317			 * in the section.
1318			 */
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;
1328			}
1329			break;
1330		default:
1331			break;
1332		}
1333	}
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;
1342}
1343
1344
1345protected int
1346file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
1347    size_t nbytes)
1348{
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;
1361
1362	if (ms->flags & (MAGIC_MIME|MAGIC_APPLE))
1363		return 0;
1364	/*
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).
1370	 */
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;
1375
1376	/*
1377	 * If we cannot seek, it must be a pipe, socket or fifo.
1378	 */
1379	if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
1380		fd = file_pipe2file(ms, fd, buf, nbytes);
1381
1382	if (fstat(fd, &st) == -1) {
1383  		file_badread(ms);
1384		return -1;
1385	}
1386	if (S_ISREG(st.st_mode) || st.st_size != 0)
1387		fsize = st.st_size;
1388	else
1389		fsize = SIZE_UNKNOWN;
1390
1391	clazz = buf[EI_CLASS];
1392
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;
1410	}
1411	return 0;
1412}
1413#endif
1414