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