1133359Sobrien/*
2133359Sobrien * Copyright (c) Christos Zoulas 2003.
3133359Sobrien * All Rights Reserved.
4133359Sobrien *
5133359Sobrien * Redistribution and use in source and binary forms, with or without
6133359Sobrien * modification, are permitted provided that the following conditions
7133359Sobrien * are met:
8133359Sobrien * 1. Redistributions of source code must retain the above copyright
9133359Sobrien *    notice immediately at the beginning of the file, without modification,
10133359Sobrien *    this list of conditions, and the following disclaimer.
11133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
12133359Sobrien *    notice, this list of conditions and the following disclaimer in the
13133359Sobrien *    documentation and/or other materials provided with the distribution.
14133359Sobrien *
15133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25133359Sobrien * SUCH DAMAGE.
26133359Sobrien */
2768349Sobrien#include "file.h"
2868349Sobrien
29191736Sobrien#ifndef lint
30328875SeadlerFILE_RCSID("@(#)$File: readelf.c,v 1.138 2017/08/27 07:55:02 christos Exp $")
31191736Sobrien#endif
32191736Sobrien
3368349Sobrien#ifdef BUILTIN_ELF
3468349Sobrien#include <string.h>
3568349Sobrien#include <ctype.h>
3668349Sobrien#include <stdlib.h>
3768349Sobrien#ifdef HAVE_UNISTD_H
3868349Sobrien#include <unistd.h>
3968349Sobrien#endif
4068349Sobrien
4168349Sobrien#include "readelf.h"
42186690Sobrien#include "magic.h"
4368349Sobrien
4468349Sobrien#ifdef	ELFCORE
45169942Sobrienprivate int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t,
46277592Sdelphij    off_t, int *, uint16_t *);
4768349Sobrien#endif
48169942Sobrienprivate int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t,
49277592Sdelphij    off_t, int, int *, uint16_t *);
50226048Sobrienprivate int doshn(struct magic_set *, int, int, int, off_t, int, size_t,
51277592Sdelphij    off_t, int, int, int *, uint16_t *);
52186690Sobrienprivate size_t donote(struct magic_set *, void *, size_t, size_t, int,
53300899Sdelphij    int, size_t, int *, uint16_t *, int, off_t, int, off_t);
5468349Sobrien
55133359Sobrien#define	ELF_ALIGN(a)	((((a) + align - 1) / align) * align)
5668349Sobrien
57159764Sobrien#define isquote(c) (strchr("'\"`", (c)) != NULL)
58159764Sobrien
59133359Sobrienprivate uint16_t getu16(int, uint16_t);
60133359Sobrienprivate uint32_t getu32(int, uint32_t);
61133359Sobrienprivate uint64_t getu64(int, uint64_t);
62133359Sobrien
63276415Sdelphij#define MAX_PHNUM	128
64276415Sdelphij#define	MAX_SHNUM	32768
65276415Sdelphij#define SIZE_UNKNOWN	((off_t)-1)
66275668Sdelphij
67275668Sdelphijprivate int
68275668Sdelphijtoomany(struct magic_set *ms, const char *name, uint16_t num)
69275668Sdelphij{
70277592Sdelphij	if (file_printf(ms, ", too many %s (%u)", name, num
71275668Sdelphij	    ) == -1)
72275668Sdelphij		return -1;
73275668Sdelphij	return 0;
74275668Sdelphij}
75275668Sdelphij
76133359Sobrienprivate uint16_t
77103373Sobriengetu16(int swap, uint16_t value)
7868349Sobrien{
7968349Sobrien	union {
8068349Sobrien		uint16_t ui;
8168349Sobrien		char c[2];
8268349Sobrien	} retval, tmpval;
8368349Sobrien
8468349Sobrien	if (swap) {
8568349Sobrien		tmpval.ui = value;
8668349Sobrien
8768349Sobrien		retval.c[0] = tmpval.c[1];
8868349Sobrien		retval.c[1] = tmpval.c[0];
8968349Sobrien
9068349Sobrien		return retval.ui;
9168349Sobrien	} else
9268349Sobrien		return value;
9368349Sobrien}
9468349Sobrien
95133359Sobrienprivate uint32_t
96103373Sobriengetu32(int swap, uint32_t value)
9768349Sobrien{
9868349Sobrien	union {
9968349Sobrien		uint32_t ui;
10068349Sobrien		char c[4];
10168349Sobrien	} retval, tmpval;
10268349Sobrien
10368349Sobrien	if (swap) {
10468349Sobrien		tmpval.ui = value;
10568349Sobrien
10668349Sobrien		retval.c[0] = tmpval.c[3];
10768349Sobrien		retval.c[1] = tmpval.c[2];
10868349Sobrien		retval.c[2] = tmpval.c[1];
10968349Sobrien		retval.c[3] = tmpval.c[0];
11068349Sobrien
11168349Sobrien		return retval.ui;
11268349Sobrien	} else
11368349Sobrien		return value;
11468349Sobrien}
11568349Sobrien
116133359Sobrienprivate uint64_t
117103373Sobriengetu64(int swap, uint64_t value)
11868349Sobrien{
11968349Sobrien	union {
12068349Sobrien		uint64_t ui;
12168349Sobrien		char c[8];
12268349Sobrien	} retval, tmpval;
12368349Sobrien
12468349Sobrien	if (swap) {
12568349Sobrien		tmpval.ui = value;
12668349Sobrien
12768349Sobrien		retval.c[0] = tmpval.c[7];
12868349Sobrien		retval.c[1] = tmpval.c[6];
12968349Sobrien		retval.c[2] = tmpval.c[5];
13068349Sobrien		retval.c[3] = tmpval.c[4];
13168349Sobrien		retval.c[4] = tmpval.c[3];
13268349Sobrien		retval.c[5] = tmpval.c[2];
13368349Sobrien		retval.c[6] = tmpval.c[1];
13468349Sobrien		retval.c[7] = tmpval.c[0];
13568349Sobrien
13668349Sobrien		return retval.ui;
13768349Sobrien	} else
13868349Sobrien		return value;
13968349Sobrien}
14068349Sobrien
141186690Sobrien#define elf_getu16(swap, value) getu16(swap, value)
142186690Sobrien#define elf_getu32(swap, value) getu32(swap, value)
143267843Sdelphij#define elf_getu64(swap, value) getu64(swap, value)
144159764Sobrien
145186690Sobrien#define xsh_addr	(clazz == ELFCLASS32			\
146267843Sdelphij			 ? (void *)&sh32			\
147267843Sdelphij			 : (void *)&sh64)
148186690Sobrien#define xsh_sizeof	(clazz == ELFCLASS32			\
149267843Sdelphij			 ? sizeof(sh32)				\
150267843Sdelphij			 : sizeof(sh64))
151267843Sdelphij#define xsh_size	(size_t)(clazz == ELFCLASS32		\
152186690Sobrien			 ? elf_getu32(swap, sh32.sh_size)	\
153186690Sobrien			 : elf_getu64(swap, sh64.sh_size))
154226048Sobrien#define xsh_offset	(off_t)(clazz == ELFCLASS32		\
155186690Sobrien			 ? elf_getu32(swap, sh32.sh_offset)	\
156186690Sobrien			 : elf_getu64(swap, sh64.sh_offset))
157186690Sobrien#define xsh_type	(clazz == ELFCLASS32			\
158186690Sobrien			 ? elf_getu32(swap, sh32.sh_type)	\
159186690Sobrien			 : elf_getu32(swap, sh64.sh_type))
160267843Sdelphij#define xsh_name    	(clazz == ELFCLASS32			\
161267843Sdelphij			 ? elf_getu32(swap, sh32.sh_name)	\
162267843Sdelphij			 : elf_getu32(swap, sh64.sh_name))
163186690Sobrien#define xph_addr	(clazz == ELFCLASS32			\
164186690Sobrien			 ? (void *) &ph32			\
16568349Sobrien			 : (void *) &ph64)
166186690Sobrien#define xph_sizeof	(clazz == ELFCLASS32			\
167267843Sdelphij			 ? sizeof(ph32)				\
168267843Sdelphij			 : sizeof(ph64))
169186690Sobrien#define xph_type	(clazz == ELFCLASS32			\
170186690Sobrien			 ? elf_getu32(swap, ph32.p_type)	\
171186690Sobrien			 : elf_getu32(swap, ph64.p_type))
172186690Sobrien#define xph_offset	(off_t)(clazz == ELFCLASS32		\
173186690Sobrien			 ? elf_getu32(swap, ph32.p_offset)	\
174186690Sobrien			 : elf_getu64(swap, ph64.p_offset))
175186690Sobrien#define xph_align	(size_t)((clazz == ELFCLASS32		\
176186690Sobrien			 ? (off_t) (ph32.p_align ? 		\
177186690Sobrien			    elf_getu32(swap, ph32.p_align) : 4) \
178186690Sobrien			 : (off_t) (ph64.p_align ?		\
179186690Sobrien			    elf_getu64(swap, ph64.p_align) : 4)))
180300899Sdelphij#define xph_vaddr	(size_t)((clazz == ELFCLASS32		\
181300899Sdelphij			 ? (off_t) (ph32.p_vaddr ? 		\
182300899Sdelphij			    elf_getu32(swap, ph32.p_vaddr) : 4) \
183300899Sdelphij			 : (off_t) (ph64.p_vaddr ?		\
184300899Sdelphij			    elf_getu64(swap, ph64.p_vaddr) : 4)))
185186690Sobrien#define xph_filesz	(size_t)((clazz == ELFCLASS32		\
186186690Sobrien			 ? elf_getu32(swap, ph32.p_filesz)	\
187186690Sobrien			 : elf_getu64(swap, ph64.p_filesz)))
188186690Sobrien#define xnh_addr	(clazz == ELFCLASS32			\
189267843Sdelphij			 ? (void *)&nh32			\
190267843Sdelphij			 : (void *)&nh64)
191186690Sobrien#define xph_memsz	(size_t)((clazz == ELFCLASS32		\
192186690Sobrien			 ? elf_getu32(swap, ph32.p_memsz)	\
193186690Sobrien			 : elf_getu64(swap, ph64.p_memsz)))
194186690Sobrien#define xnh_sizeof	(clazz == ELFCLASS32			\
195300899Sdelphij			 ? sizeof(nh32)				\
196300899Sdelphij			 : sizeof(nh64))
197186690Sobrien#define xnh_type	(clazz == ELFCLASS32			\
198186690Sobrien			 ? elf_getu32(swap, nh32.n_type)	\
199186690Sobrien			 : elf_getu32(swap, nh64.n_type))
200186690Sobrien#define xnh_namesz	(clazz == ELFCLASS32			\
201186690Sobrien			 ? elf_getu32(swap, nh32.n_namesz)	\
202186690Sobrien			 : elf_getu32(swap, nh64.n_namesz))
203186690Sobrien#define xnh_descsz	(clazz == ELFCLASS32			\
204186690Sobrien			 ? elf_getu32(swap, nh32.n_descsz)	\
205186690Sobrien			 : elf_getu32(swap, nh64.n_descsz))
206186690Sobrien#define prpsoffsets(i)	(clazz == ELFCLASS32			\
207186690Sobrien			 ? prpsoffsets32[i]			\
20868349Sobrien			 : prpsoffsets64[i])
209186690Sobrien#define xcap_addr	(clazz == ELFCLASS32			\
210267843Sdelphij			 ? (void *)&cap32			\
211267843Sdelphij			 : (void *)&cap64)
212186690Sobrien#define xcap_sizeof	(clazz == ELFCLASS32			\
213186690Sobrien			 ? sizeof cap32				\
214186690Sobrien			 : sizeof cap64)
215186690Sobrien#define xcap_tag	(clazz == ELFCLASS32			\
216186690Sobrien			 ? elf_getu32(swap, cap32.c_tag)	\
217186690Sobrien			 : elf_getu64(swap, cap64.c_tag))
218186690Sobrien#define xcap_val	(clazz == ELFCLASS32			\
219186690Sobrien			 ? elf_getu32(swap, cap32.c_un.c_val)	\
220186690Sobrien			 : elf_getu64(swap, cap64.c_un.c_val))
221300899Sdelphij#define xauxv_addr	(clazz == ELFCLASS32			\
222300899Sdelphij			 ? (void *)&auxv32			\
223300899Sdelphij			 : (void *)&auxv64)
224300899Sdelphij#define xauxv_sizeof	(clazz == ELFCLASS32			\
225300899Sdelphij			 ? sizeof(auxv32)			\
226300899Sdelphij			 : sizeof(auxv64))
227300899Sdelphij#define xauxv_type	(clazz == ELFCLASS32			\
228300899Sdelphij			 ? elf_getu32(swap, auxv32.a_type)	\
229300899Sdelphij			 : elf_getu64(swap, auxv64.a_type))
230300899Sdelphij#define xauxv_val	(clazz == ELFCLASS32			\
231300899Sdelphij			 ? elf_getu32(swap, auxv32.a_v)		\
232300899Sdelphij			 : elf_getu64(swap, auxv64.a_v))
23368349Sobrien
23468349Sobrien#ifdef ELFCORE
235186690Sobrien/*
236186690Sobrien * Try larger offsets first to avoid false matches
237186690Sobrien * from earlier data that happen to look like strings.
238186690Sobrien */
239186690Sobrienstatic const size_t	prpsoffsets32[] = {
240186690Sobrien#ifdef USE_NT_PSINFO
241186690Sobrien	104,		/* SunOS 5.x (command line) */
242186690Sobrien	88,		/* SunOS 5.x (short name) */
243186690Sobrien#endif /* USE_NT_PSINFO */
244186690Sobrien
245186690Sobrien	100,		/* SunOS 5.x (command line) */
246186690Sobrien	84,		/* SunOS 5.x (short name) */
247186690Sobrien
248186690Sobrien	44,		/* Linux (command line) */
249186690Sobrien	28,		/* Linux 2.0.36 (short name) */
250186690Sobrien
25168349Sobrien	8,		/* FreeBSD */
25268349Sobrien};
25368349Sobrien
254186690Sobrienstatic const size_t	prpsoffsets64[] = {
255186690Sobrien#ifdef USE_NT_PSINFO
256186690Sobrien	152,		/* SunOS 5.x (command line) */
257186690Sobrien	136,		/* SunOS 5.x (short name) */
258186690Sobrien#endif /* USE_NT_PSINFO */
259186690Sobrien
260186690Sobrien	136,		/* SunOS 5.x, 64-bit (command line) */
261186690Sobrien	120,		/* SunOS 5.x, 64-bit (short name) */
262186690Sobrien
263186690Sobrien	56,		/* Linux (command line) */
264186690Sobrien	40,             /* Linux (tested on core from 2.4.x, short name) */
265186690Sobrien
266159764Sobrien	16,		/* FreeBSD, 64-bit */
26768349Sobrien};
26868349Sobrien
26968349Sobrien#define	NOFFSETS32	(sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
27068349Sobrien#define NOFFSETS64	(sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
27168349Sobrien
272186690Sobrien#define NOFFSETS	(clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
27368349Sobrien
27468349Sobrien/*
27568349Sobrien * Look through the program headers of an executable image, searching
27668349Sobrien * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
27768349Sobrien * "FreeBSD"; if one is found, try looking in various places in its
27868349Sobrien * contents for a 16-character string containing only printable
27968349Sobrien * characters - if found, that string should be the name of the program
28068349Sobrien * that dropped core.  Note: right after that 16-character string is,
28168349Sobrien * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
28268349Sobrien * Linux, a longer string (80 characters, in 5.x, probably other
28368349Sobrien * SVR4-flavored systems, and Linux) containing the start of the
28468349Sobrien * command line for that program.
28568349Sobrien *
286186690Sobrien * SunOS 5.x core files contain two PT_NOTE sections, with the types
287186690Sobrien * NT_PRPSINFO (old) and NT_PSINFO (new).  These structs contain the
288186690Sobrien * same info about the command name and command line, so it probably
289186690Sobrien * isn't worthwhile to look for NT_PSINFO, but the offsets are provided
290186690Sobrien * above (see USE_NT_PSINFO), in case we ever decide to do so.  The
291186690Sobrien * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent;
292186690Sobrien * the SunOS 5.x file command relies on this (and prefers the latter).
293186690Sobrien *
29468349Sobrien * The signal number probably appears in a section of type NT_PRSTATUS,
29568349Sobrien * but that's also rather OS-dependent, in ways that are harder to
29668349Sobrien * dissect with heuristics, so I'm not bothering with the signal number.
29768349Sobrien * (I suppose the signal number could be of interest in situations where
29868349Sobrien * you don't have the binary of the program that dropped core; if you
29968349Sobrien * *do* have that binary, the debugger will probably tell you what
30068349Sobrien * signal it was.)
30168349Sobrien */
302103373Sobrien
303103373Sobrien#define	OS_STYLE_SVR4		0
304103373Sobrien#define	OS_STYLE_FREEBSD	1
305103373Sobrien#define	OS_STYLE_NETBSD		2
306103373Sobrien
307186690Sobrienprivate const char os_style_names[][8] = {
308103373Sobrien	"SVR4",
309103373Sobrien	"FreeBSD",
310103373Sobrien	"NetBSD",
311103373Sobrien};
312103373Sobrien
313328875Seadler#define FLAGS_CORE_STYLE		0x003
314159764Sobrien
315328875Seadler#define FLAGS_DID_CORE			0x004
316328875Seadler#define FLAGS_DID_OS_NOTE		0x008
317328875Seadler#define FLAGS_DID_BUILD_ID		0x010
318328875Seadler#define FLAGS_DID_CORE_STYLE		0x020
319328875Seadler#define FLAGS_DID_NETBSD_PAX		0x040
320328875Seadler#define FLAGS_DID_NETBSD_MARCH		0x080
321328875Seadler#define FLAGS_DID_NETBSD_CMODEL		0x100
322328875Seadler#define FLAGS_DID_NETBSD_UNKNOWN	0x200
323328875Seadler#define FLAGS_IS_CORE			0x400
324328875Seadler#define FLAGS_DID_AUXV			0x800
325328875Seadler
326133359Sobrienprivate int
327186690Sobriendophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
328277592Sdelphij    int num, size_t size, off_t fsize, int *flags, uint16_t *notecount)
32968349Sobrien{
33068349Sobrien	Elf32_Phdr ph32;
33168349Sobrien	Elf64_Phdr ph64;
332267843Sdelphij	size_t offset, len;
333133359Sobrien	unsigned char nbuf[BUFSIZ];
334133359Sobrien	ssize_t bufsize;
335300899Sdelphij	off_t ph_off = off;
336300899Sdelphij	int ph_num = num;
33768349Sobrien
338159764Sobrien	if (size != xph_sizeof) {
339133359Sobrien		if (file_printf(ms, ", corrupted program header size") == -1)
340133359Sobrien			return -1;
341133359Sobrien		return 0;
342133359Sobrien	}
343159764Sobrien
34468349Sobrien	/*
34568349Sobrien	 * Loop through all the program headers.
34668349Sobrien	 */
34768349Sobrien	for ( ; num; num--) {
348277592Sdelphij		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
349133359Sobrien			file_badread(ms);
350133359Sobrien			return -1;
351133359Sobrien		}
352226048Sobrien		off += size;
353226048Sobrien
354276415Sdelphij		if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
355226048Sobrien			/* Perhaps warn here */
356169942Sobrien			continue;
357169942Sobrien		}
358169942Sobrien
359159764Sobrien		if (xph_type != PT_NOTE)
36068349Sobrien			continue;
36168349Sobrien
36268349Sobrien		/*
36368349Sobrien		 * This is a PT_NOTE section; loop through all the notes
36468349Sobrien		 * in the section.
36568349Sobrien		 */
366267843Sdelphij		len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf);
367267843Sdelphij		if ((bufsize = pread(fd, nbuf, len, xph_offset)) == -1) {
368133359Sobrien			file_badread(ms);
369133359Sobrien			return -1;
370133359Sobrien		}
37168349Sobrien		offset = 0;
37268349Sobrien		for (;;) {
373133359Sobrien			if (offset >= (size_t)bufsize)
37468349Sobrien				break;
375133359Sobrien			offset = donote(ms, nbuf, offset, (size_t)bufsize,
376300899Sdelphij			    clazz, swap, 4, flags, notecount, fd, ph_off,
377300899Sdelphij			    ph_num, fsize);
378133359Sobrien			if (offset == 0)
379133359Sobrien				break;
38068349Sobrien
381133359Sobrien		}
382133359Sobrien	}
383133359Sobrien	return 0;
384133359Sobrien}
385133359Sobrien#endif
386133359Sobrien
387267843Sdelphijstatic void
388267843Sdelphijdo_note_netbsd_version(struct magic_set *ms, int swap, void *v)
389267843Sdelphij{
390267843Sdelphij	uint32_t desc;
391267843Sdelphij	(void)memcpy(&desc, v, sizeof(desc));
392267843Sdelphij	desc = elf_getu32(swap, desc);
393267843Sdelphij
394267843Sdelphij	if (file_printf(ms, ", for NetBSD") == -1)
395267843Sdelphij		return;
396267843Sdelphij	/*
397267843Sdelphij	 * The version number used to be stuck as 199905, and was thus
398267843Sdelphij	 * basically content-free.  Newer versions of NetBSD have fixed
399267843Sdelphij	 * this and now use the encoding of __NetBSD_Version__:
400267843Sdelphij	 *
401267843Sdelphij	 *	MMmmrrpp00
402267843Sdelphij	 *
403267843Sdelphij	 * M = major version
404267843Sdelphij	 * m = minor version
405267843Sdelphij	 * r = release ["",A-Z,Z[A-Z] but numeric]
406267843Sdelphij	 * p = patchlevel
407267843Sdelphij	 */
408267843Sdelphij	if (desc > 100000000U) {
409267843Sdelphij		uint32_t ver_patch = (desc / 100) % 100;
410267843Sdelphij		uint32_t ver_rel = (desc / 10000) % 100;
411267843Sdelphij		uint32_t ver_min = (desc / 1000000) % 100;
412267843Sdelphij		uint32_t ver_maj = desc / 100000000;
413267843Sdelphij
414267843Sdelphij		if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1)
415267843Sdelphij			return;
416267843Sdelphij		if (ver_rel == 0 && ver_patch != 0) {
417267843Sdelphij			if (file_printf(ms, ".%u", ver_patch) == -1)
418267843Sdelphij				return;
419267843Sdelphij		} else if (ver_rel != 0) {
420267843Sdelphij			while (ver_rel > 26) {
421267843Sdelphij				if (file_printf(ms, "Z") == -1)
422267843Sdelphij					return;
423267843Sdelphij				ver_rel -= 26;
424267843Sdelphij			}
425267843Sdelphij			if (file_printf(ms, "%c", 'A' + ver_rel - 1)
426267843Sdelphij			    == -1)
427267843Sdelphij				return;
428267843Sdelphij		}
429267843Sdelphij	}
430267843Sdelphij}
431267843Sdelphij
432267843Sdelphijstatic void
433267843Sdelphijdo_note_freebsd_version(struct magic_set *ms, int swap, void *v)
434267843Sdelphij{
435267843Sdelphij	uint32_t desc;
436267843Sdelphij
437267843Sdelphij	(void)memcpy(&desc, v, sizeof(desc));
438267843Sdelphij	desc = elf_getu32(swap, desc);
439267843Sdelphij	if (file_printf(ms, ", for FreeBSD") == -1)
440267843Sdelphij		return;
441267843Sdelphij
442267843Sdelphij	/*
443267843Sdelphij	 * Contents is __FreeBSD_version, whose relation to OS
444267843Sdelphij	 * versions is defined by a huge table in the Porter's
445267843Sdelphij	 * Handbook.  This is the general scheme:
446267843Sdelphij	 *
447267843Sdelphij	 * Releases:
448267843Sdelphij	 * 	Mmp000 (before 4.10)
449267843Sdelphij	 * 	Mmi0p0 (before 5.0)
450267843Sdelphij	 * 	Mmm0p0
451267843Sdelphij	 *
452267843Sdelphij	 * Development branches:
453267843Sdelphij	 * 	Mmpxxx (before 4.6)
454267843Sdelphij	 * 	Mmp1xx (before 4.10)
455267843Sdelphij	 * 	Mmi1xx (before 5.0)
456267843Sdelphij	 * 	M000xx (pre-M.0)
457267843Sdelphij	 * 	Mmm1xx
458267843Sdelphij	 *
459267843Sdelphij	 * M = major version
460267843Sdelphij	 * m = minor version
461267843Sdelphij	 * i = minor version increment (491000 -> 4.10)
462267843Sdelphij	 * p = patchlevel
463267843Sdelphij	 * x = revision
464267843Sdelphij	 *
465267843Sdelphij	 * The first release of FreeBSD to use ELF by default
466267843Sdelphij	 * was version 3.0.
467267843Sdelphij	 */
468267843Sdelphij	if (desc == 460002) {
469267843Sdelphij		if (file_printf(ms, " 4.6.2") == -1)
470267843Sdelphij			return;
471267843Sdelphij	} else if (desc < 460100) {
472267843Sdelphij		if (file_printf(ms, " %d.%d", desc / 100000,
473267843Sdelphij		    desc / 10000 % 10) == -1)
474267843Sdelphij			return;
475267843Sdelphij		if (desc / 1000 % 10 > 0)
476267843Sdelphij			if (file_printf(ms, ".%d", desc / 1000 % 10) == -1)
477267843Sdelphij				return;
478267843Sdelphij		if ((desc % 1000 > 0) || (desc % 100000 == 0))
479267843Sdelphij			if (file_printf(ms, " (%d)", desc) == -1)
480267843Sdelphij				return;
481267843Sdelphij	} else if (desc < 500000) {
482267843Sdelphij		if (file_printf(ms, " %d.%d", desc / 100000,
483267843Sdelphij		    desc / 10000 % 10 + desc / 1000 % 10) == -1)
484267843Sdelphij			return;
485267843Sdelphij		if (desc / 100 % 10 > 0) {
486267843Sdelphij			if (file_printf(ms, " (%d)", desc) == -1)
487267843Sdelphij				return;
488267843Sdelphij		} else if (desc / 10 % 10 > 0) {
489267843Sdelphij			if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
490267843Sdelphij				return;
491267843Sdelphij		}
492267843Sdelphij	} else {
493267843Sdelphij		if (file_printf(ms, " %d.%d", desc / 100000,
494267843Sdelphij		    desc / 1000 % 100) == -1)
495267843Sdelphij			return;
496267843Sdelphij		if ((desc / 100 % 10 > 0) ||
497267843Sdelphij		    (desc % 100000 / 100 == 0)) {
498267843Sdelphij			if (file_printf(ms, " (%d)", desc) == -1)
499267843Sdelphij				return;
500267843Sdelphij		} else if (desc / 10 % 10 > 0) {
501267843Sdelphij			if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
502267843Sdelphij				return;
503267843Sdelphij		}
504267843Sdelphij	}
505267843Sdelphij}
506267843Sdelphij
507277592Sdelphijprivate int
508284778Sdelphij/*ARGSUSED*/
509277592Sdelphijdo_bid_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
510277592Sdelphij    int swap __attribute__((__unused__)), uint32_t namesz, uint32_t descsz,
511277592Sdelphij    size_t noff, size_t doff, int *flags)
512133359Sobrien{
513277592Sdelphij	if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
514323281Sgordon	    type == NT_GNU_BUILD_ID && (descsz >= 4 && descsz <= 20)) {
515277592Sdelphij		uint8_t desc[20];
516309848Sdelphij		const char *btype;
517277592Sdelphij		uint32_t i;
518277592Sdelphij		*flags |= FLAGS_DID_BUILD_ID;
519309848Sdelphij		switch (descsz) {
520309848Sdelphij		case 8:
521309848Sdelphij		    btype = "xxHash";
522309848Sdelphij		    break;
523309848Sdelphij		case 16:
524309848Sdelphij		    btype = "md5/uuid";
525309848Sdelphij		    break;
526309848Sdelphij		case 20:
527309848Sdelphij		    btype = "sha1";
528309848Sdelphij		    break;
529309848Sdelphij		default:
530309848Sdelphij		    btype = "unknown";
531309848Sdelphij		    break;
532309848Sdelphij		}
533309848Sdelphij		if (file_printf(ms, ", BuildID[%s]=", btype) == -1)
534277592Sdelphij			return 1;
535277592Sdelphij		(void)memcpy(desc, &nbuf[doff], descsz);
536277592Sdelphij		for (i = 0; i < descsz; i++)
537277592Sdelphij		    if (file_printf(ms, "%02x", desc[i]) == -1)
538277592Sdelphij			return 1;
539277592Sdelphij		return 1;
540275668Sdelphij	}
541277592Sdelphij	return 0;
542277592Sdelphij}
543275668Sdelphij
544277592Sdelphijprivate int
545277592Sdelphijdo_os_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
546277592Sdelphij    int swap, uint32_t namesz, uint32_t descsz,
547277592Sdelphij    size_t noff, size_t doff, int *flags)
548277592Sdelphij{
549267843Sdelphij	if (namesz == 5 && strcmp((char *)&nbuf[noff], "SuSE") == 0 &&
550277592Sdelphij	    type == NT_GNU_VERSION && descsz == 2) {
551277592Sdelphij	    *flags |= FLAGS_DID_OS_NOTE;
552267843Sdelphij	    file_printf(ms, ", for SuSE %d.%d", nbuf[doff], nbuf[doff + 1]);
553277592Sdelphij	    return 1;
554267843Sdelphij	}
555277592Sdelphij
556133359Sobrien	if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
557277592Sdelphij	    type == NT_GNU_VERSION && descsz == 16) {
558133359Sobrien		uint32_t desc[4];
559133359Sobrien		(void)memcpy(desc, &nbuf[doff], sizeof(desc));
560133359Sobrien
561277592Sdelphij		*flags |= FLAGS_DID_OS_NOTE;
562133359Sobrien		if (file_printf(ms, ", for GNU/") == -1)
563277592Sdelphij			return 1;
564186690Sobrien		switch (elf_getu32(swap, desc[0])) {
565133359Sobrien		case GNU_OS_LINUX:
566133359Sobrien			if (file_printf(ms, "Linux") == -1)
567277592Sdelphij				return 1;
568133359Sobrien			break;
569133359Sobrien		case GNU_OS_HURD:
570133359Sobrien			if (file_printf(ms, "Hurd") == -1)
571277592Sdelphij				return 1;
572133359Sobrien			break;
573133359Sobrien		case GNU_OS_SOLARIS:
574133359Sobrien			if (file_printf(ms, "Solaris") == -1)
575277592Sdelphij				return 1;
576133359Sobrien			break;
577186690Sobrien		case GNU_OS_KFREEBSD:
578186690Sobrien			if (file_printf(ms, "kFreeBSD") == -1)
579277592Sdelphij				return 1;
580186690Sobrien			break;
581186690Sobrien		case GNU_OS_KNETBSD:
582186690Sobrien			if (file_printf(ms, "kNetBSD") == -1)
583277592Sdelphij				return 1;
584186690Sobrien			break;
585133359Sobrien		default:
586133359Sobrien			if (file_printf(ms, "<unknown>") == -1)
587277592Sdelphij				return 1;
588133359Sobrien		}
589186690Sobrien		if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
590186690Sobrien		    elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
591277592Sdelphij			return 1;
592277592Sdelphij		return 1;
593133359Sobrien	}
594133359Sobrien
595277592Sdelphij	if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) {
596277592Sdelphij	    	if (type == NT_NETBSD_VERSION && descsz == 4) {
597277592Sdelphij			*flags |= FLAGS_DID_OS_NOTE;
598277592Sdelphij			do_note_netbsd_version(ms, swap, &nbuf[doff]);
599277592Sdelphij			return 1;
600277592Sdelphij		}
601226048Sobrien	}
602226048Sobrien
603277592Sdelphij	if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0) {
604277592Sdelphij	    	if (type == NT_FREEBSD_VERSION && descsz == 4) {
605277592Sdelphij			*flags |= FLAGS_DID_OS_NOTE;
606277592Sdelphij			do_note_freebsd_version(ms, swap, &nbuf[doff]);
607277592Sdelphij			return 1;
608277592Sdelphij		}
609277592Sdelphij	}
610277592Sdelphij
611277592Sdelphij	if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 &&
612277592Sdelphij	    type == NT_OPENBSD_VERSION && descsz == 4) {
613277592Sdelphij		*flags |= FLAGS_DID_OS_NOTE;
614277592Sdelphij		if (file_printf(ms, ", for OpenBSD") == -1)
615277592Sdelphij			return 1;
616277592Sdelphij		/* Content of note is always 0 */
617277592Sdelphij		return 1;
618277592Sdelphij	}
619277592Sdelphij
620277592Sdelphij	if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 &&
621277592Sdelphij	    type == NT_DRAGONFLY_VERSION && descsz == 4) {
622277592Sdelphij		uint32_t desc;
623277592Sdelphij		*flags |= FLAGS_DID_OS_NOTE;
624277592Sdelphij		if (file_printf(ms, ", for DragonFly") == -1)
625277592Sdelphij			return 1;
626277592Sdelphij		(void)memcpy(&desc, &nbuf[doff], sizeof(desc));
627277592Sdelphij		desc = elf_getu32(swap, desc);
628277592Sdelphij		if (file_printf(ms, " %d.%d.%d", desc / 100000,
629277592Sdelphij		    desc / 10000 % 10, desc % 10000) == -1)
630277592Sdelphij			return 1;
631277592Sdelphij		return 1;
632277592Sdelphij	}
633277592Sdelphij	return 0;
634277592Sdelphij}
635277592Sdelphij
636277592Sdelphijprivate int
637277592Sdelphijdo_pax_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
638277592Sdelphij    int swap, uint32_t namesz, uint32_t descsz,
639277592Sdelphij    size_t noff, size_t doff, int *flags)
640277592Sdelphij{
641267843Sdelphij	if (namesz == 4 && strcmp((char *)&nbuf[noff], "PaX") == 0 &&
642277592Sdelphij	    type == NT_NETBSD_PAX && descsz == 4) {
643267843Sdelphij		static const char *pax[] = {
644267843Sdelphij		    "+mprotect",
645267843Sdelphij		    "-mprotect",
646267843Sdelphij		    "+segvguard",
647267843Sdelphij		    "-segvguard",
648267843Sdelphij		    "+ASLR",
649267843Sdelphij		    "-ASLR",
650267843Sdelphij		};
651133359Sobrien		uint32_t desc;
652267843Sdelphij		size_t i;
653267843Sdelphij		int did = 0;
654267843Sdelphij
655277592Sdelphij		*flags |= FLAGS_DID_NETBSD_PAX;
656133359Sobrien		(void)memcpy(&desc, &nbuf[doff], sizeof(desc));
657186690Sobrien		desc = elf_getu32(swap, desc);
658133359Sobrien
659267843Sdelphij		if (desc && file_printf(ms, ", PaX: ") == -1)
660277592Sdelphij			return 1;
661133359Sobrien
662267843Sdelphij		for (i = 0; i < __arraycount(pax); i++) {
663284778Sdelphij			if (((1 << (int)i) & desc) == 0)
664267843Sdelphij				continue;
665267843Sdelphij			if (file_printf(ms, "%s%s", did++ ? "," : "",
666267843Sdelphij			    pax[i]) == -1)
667277592Sdelphij				return 1;
668133359Sobrien		}
669277592Sdelphij		return 1;
670133359Sobrien	}
671277592Sdelphij	return 0;
672277592Sdelphij}
67368349Sobrien
674277592Sdelphijprivate int
675277592Sdelphijdo_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
676277592Sdelphij    int swap, uint32_t namesz, uint32_t descsz,
677277592Sdelphij    size_t noff, size_t doff, int *flags, size_t size, int clazz)
678277592Sdelphij{
679277592Sdelphij#ifdef ELFCORE
680277592Sdelphij	int os_style = -1;
681133359Sobrien	/*
682133359Sobrien	 * Sigh.  The 2.0.36 kernel in Debian 2.1, at
683133359Sobrien	 * least, doesn't correctly implement name
684133359Sobrien	 * sections, in core dumps, as specified by
685133359Sobrien	 * the "Program Linking" section of "UNIX(R) System
686133359Sobrien	 * V Release 4 Programmer's Guide: ANSI C and
687133359Sobrien	 * Programming Support Tools", because my copy
688133359Sobrien	 * clearly says "The first 'namesz' bytes in 'name'
689133359Sobrien	 * contain a *null-terminated* [emphasis mine]
690133359Sobrien	 * character representation of the entry's owner
691133359Sobrien	 * or originator", but the 2.0.36 kernel code
692133359Sobrien	 * doesn't include the terminating null in the
693133359Sobrien	 * name....
694133359Sobrien	 */
695133359Sobrien	if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) ||
696133359Sobrien	    (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) {
697133359Sobrien		os_style = OS_STYLE_SVR4;
698133359Sobrien	}
699133359Sobrien
700133359Sobrien	if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
701133359Sobrien		os_style = OS_STYLE_FREEBSD;
702133359Sobrien	}
703133359Sobrien
704133359Sobrien	if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
705133359Sobrien	    == 0)) {
706133359Sobrien		os_style = OS_STYLE_NETBSD;
707133359Sobrien	}
708133359Sobrien
709175296Sobrien	if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
710169942Sobrien		if (file_printf(ms, ", %s-style", os_style_names[os_style])
711169942Sobrien		    == -1)
712277592Sdelphij			return 1;
713175296Sobrien		*flags |= FLAGS_DID_CORE_STYLE;
714328875Seadler		*flags |= os_style;
715159764Sobrien	}
716133359Sobrien
717159764Sobrien	switch (os_style) {
718159764Sobrien	case OS_STYLE_NETBSD:
719277592Sdelphij		if (type == NT_NETBSD_CORE_PROCINFO) {
720277592Sdelphij			char sbuf[512];
721328875Seadler			struct NetBSD_elfcore_procinfo pi;
722328875Seadler			memset(&pi, 0, sizeof(pi));
723328875Seadler			memcpy(&pi, nbuf + doff, descsz);
724328875Seadler
725328875Seadler			if (file_printf(ms, ", from '%.31s', pid=%u, uid=%u, "
726328875Seadler			    "gid=%u, nlwps=%u, lwp=%u (signal %u/code %u)",
727277592Sdelphij			    file_printable(sbuf, sizeof(sbuf),
728328875Seadler			    CAST(char *, pi.cpi_name)),
729328875Seadler			    elf_getu32(swap, pi.cpi_pid),
730328875Seadler			    elf_getu32(swap, pi.cpi_euid),
731328875Seadler			    elf_getu32(swap, pi.cpi_egid),
732328875Seadler			    elf_getu32(swap, pi.cpi_nlwps),
733328875Seadler			    elf_getu32(swap, pi.cpi_siglwp),
734328875Seadler			    elf_getu32(swap, pi.cpi_signo),
735328875Seadler			    elf_getu32(swap, pi.cpi_sigcode)) == -1)
736277592Sdelphij				return 1;
737328875Seadler
738175296Sobrien			*flags |= FLAGS_DID_CORE;
739277592Sdelphij			return 1;
740159764Sobrien		}
741159764Sobrien		break;
742133359Sobrien
743159764Sobrien	default:
744277592Sdelphij		if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
745159764Sobrien			size_t i, j;
746159764Sobrien			unsigned char c;
747159764Sobrien			/*
748159764Sobrien			 * Extract the program name.  We assume
749159764Sobrien			 * it to be 16 characters (that's what it
750159764Sobrien			 * is in SunOS 5.x and Linux).
751159764Sobrien			 *
752159764Sobrien			 * Unfortunately, it's at a different offset
753175296Sobrien			 * in various OSes, so try multiple offsets.
754159764Sobrien			 * If the characters aren't all printable,
755159764Sobrien			 * reject it.
756159764Sobrien			 */
757159764Sobrien			for (i = 0; i < NOFFSETS; i++) {
758175296Sobrien				unsigned char *cname, *cp;
759159764Sobrien				size_t reloffset = prpsoffsets(i);
760159764Sobrien				size_t noffset = doff + reloffset;
761226048Sobrien				size_t k;
762159764Sobrien				for (j = 0; j < 16; j++, noffset++,
763159764Sobrien				    reloffset++) {
76468349Sobrien					/*
765159764Sobrien					 * Make sure we're not past
766159764Sobrien					 * the end of the buffer; if
767159764Sobrien					 * we are, just give up.
76868349Sobrien					 */
769159764Sobrien					if (noffset >= size)
770133359Sobrien						goto tryanother;
771159764Sobrien
772133359Sobrien					/*
773159764Sobrien					 * Make sure we're not past
774159764Sobrien					 * the end of the contents;
775159764Sobrien					 * if we are, this obviously
776159764Sobrien					 * isn't the right offset.
777133359Sobrien					 */
778159764Sobrien					if (reloffset >= descsz)
779133359Sobrien						goto tryanother;
780159764Sobrien
781159764Sobrien					c = nbuf[noffset];
782159764Sobrien					if (c == '\0') {
783159764Sobrien						/*
784159764Sobrien						 * A '\0' at the
785159764Sobrien						 * beginning is
786159764Sobrien						 * obviously wrong.
787159764Sobrien						 * Any other '\0'
788159764Sobrien						 * means we're done.
789159764Sobrien						 */
790159764Sobrien						if (j == 0)
791159764Sobrien							goto tryanother;
792159764Sobrien						else
793159764Sobrien							break;
794159764Sobrien					} else {
795159764Sobrien						/*
796159764Sobrien						 * A nonprintable
797159764Sobrien						 * character is also
798159764Sobrien						 * wrong.
799159764Sobrien						 */
800159764Sobrien						if (!isprint(c) || isquote(c))
801159764Sobrien							goto tryanother;
802159764Sobrien					}
80368349Sobrien				}
804159764Sobrien				/*
805159764Sobrien				 * Well, that worked.
806159764Sobrien				 */
807226048Sobrien
808226048Sobrien				/*
809226048Sobrien				 * Try next offsets, in case this match is
810226048Sobrien				 * in the middle of a string.
811226048Sobrien				 */
812277592Sdelphij				for (k = i + 1 ; k < NOFFSETS; k++) {
813226048Sobrien					size_t no;
814226048Sobrien					int adjust = 1;
815226048Sobrien					if (prpsoffsets(k) >= prpsoffsets(i))
816226048Sobrien						continue;
817226048Sobrien					for (no = doff + prpsoffsets(k);
818226048Sobrien					     no < doff + prpsoffsets(i); no++)
819226048Sobrien						adjust = adjust
820226048Sobrien						         && isprint(nbuf[no]);
821226048Sobrien					if (adjust)
822226048Sobrien						i = k;
823226048Sobrien				}
824226048Sobrien
825175296Sobrien				cname = (unsigned char *)
826175296Sobrien				    &nbuf[doff + prpsoffsets(i)];
827175296Sobrien				for (cp = cname; *cp && isprint(*cp); cp++)
828175296Sobrien					continue;
829186690Sobrien				/*
830186690Sobrien				 * Linux apparently appends a space at the end
831186690Sobrien				 * of the command line: remove it.
832186690Sobrien				 */
833186690Sobrien				while (cp > cname && isspace(cp[-1]))
834175296Sobrien					cp--;
835175296Sobrien				if (file_printf(ms, ", from '%.*s'",
836175296Sobrien				    (int)(cp - cname), cname) == -1)
837277592Sdelphij					return 1;
838175296Sobrien				*flags |= FLAGS_DID_CORE;
839277592Sdelphij				return 1;
840133359Sobrien
841159764Sobrien			tryanother:
842159764Sobrien				;
843159764Sobrien			}
84468349Sobrien		}
845159764Sobrien		break;
84668349Sobrien	}
847133359Sobrien#endif
848277592Sdelphij	return 0;
849277592Sdelphij}
850277592Sdelphij
851300899Sdelphijprivate off_t
852300899Sdelphijget_offset_from_virtaddr(struct magic_set *ms, int swap, int clazz, int fd,
853300899Sdelphij    off_t off, int num, off_t fsize, uint64_t virtaddr)
854300899Sdelphij{
855300899Sdelphij	Elf32_Phdr ph32;
856300899Sdelphij	Elf64_Phdr ph64;
857300899Sdelphij
858300899Sdelphij	/*
859300899Sdelphij	 * Loop through all the program headers and find the header with
860300899Sdelphij	 * virtual address in which the "virtaddr" belongs to.
861300899Sdelphij	 */
862300899Sdelphij	for ( ; num; num--) {
863300899Sdelphij		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
864300899Sdelphij			file_badread(ms);
865300899Sdelphij			return -1;
866300899Sdelphij		}
867300899Sdelphij		off += xph_sizeof;
868300899Sdelphij
869300899Sdelphij		if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
870300899Sdelphij			/* Perhaps warn here */
871300899Sdelphij			continue;
872300899Sdelphij		}
873300899Sdelphij
874300899Sdelphij		if (virtaddr >= xph_vaddr && virtaddr < xph_vaddr + xph_filesz)
875300899Sdelphij			return xph_offset + (virtaddr - xph_vaddr);
876300899Sdelphij	}
877300899Sdelphij	return 0;
878300899Sdelphij}
879300899Sdelphij
880277592Sdelphijprivate size_t
881300899Sdelphijget_string_on_virtaddr(struct magic_set *ms,
882300899Sdelphij    int swap, int clazz, int fd, off_t ph_off, int ph_num,
883300899Sdelphij    off_t fsize, uint64_t virtaddr, char *buf, ssize_t buflen)
884300899Sdelphij{
885300899Sdelphij	char *bptr;
886300899Sdelphij	off_t offset;
887300899Sdelphij
888300899Sdelphij	if (buflen == 0)
889300899Sdelphij		return 0;
890300899Sdelphij
891300899Sdelphij	offset = get_offset_from_virtaddr(ms, swap, clazz, fd, ph_off, ph_num,
892300899Sdelphij	    fsize, virtaddr);
893328875Seadler	if ((buflen = pread(fd, buf, CAST(size_t, buflen), offset)) <= 0) {
894300899Sdelphij		file_badread(ms);
895300899Sdelphij		return 0;
896300899Sdelphij	}
897300899Sdelphij
898300899Sdelphij	buf[buflen - 1] = '\0';
899300899Sdelphij
900300899Sdelphij	/* We expect only printable characters, so return if buffer contains
901300899Sdelphij	 * non-printable character before the '\0' or just '\0'. */
902300899Sdelphij	for (bptr = buf; *bptr && isprint((unsigned char)*bptr); bptr++)
903300899Sdelphij		continue;
904300899Sdelphij	if (*bptr != '\0')
905300899Sdelphij		return 0;
906300899Sdelphij
907300899Sdelphij	return bptr - buf;
908300899Sdelphij}
909300899Sdelphij
910300899Sdelphij
911300899Sdelphijprivate int
912300899Sdelphijdo_auxv_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
913300899Sdelphij    int swap, uint32_t namesz __attribute__((__unused__)),
914300899Sdelphij    uint32_t descsz __attribute__((__unused__)),
915300899Sdelphij    size_t noff __attribute__((__unused__)), size_t doff,
916300899Sdelphij    int *flags, size_t size __attribute__((__unused__)), int clazz,
917300899Sdelphij    int fd, off_t ph_off, int ph_num, off_t fsize)
918300899Sdelphij{
919300899Sdelphij#ifdef ELFCORE
920300899Sdelphij	Aux32Info auxv32;
921300899Sdelphij	Aux64Info auxv64;
922300899Sdelphij	size_t elsize = xauxv_sizeof;
923300899Sdelphij	const char *tag;
924300899Sdelphij	int is_string;
925300899Sdelphij	size_t nval;
926300899Sdelphij
927328875Seadler	if ((*flags & (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE)) !=
928328875Seadler	    (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE))
929300899Sdelphij		return 0;
930300899Sdelphij
931328875Seadler	switch (*flags & FLAGS_CORE_STYLE) {
932328875Seadler	case OS_STYLE_SVR4:
933328875Seadler		if (type != NT_AUXV)
934328875Seadler			return 0;
935328875Seadler		break;
936328875Seadler#ifdef notyet
937328875Seadler	case OS_STYLE_NETBSD:
938328875Seadler		if (type != NT_NETBSD_CORE_AUXV)
939328875Seadler			return 0;
940328875Seadler		break;
941328875Seadler	case OS_STYLE_FREEBSD:
942328875Seadler		if (type != NT_FREEBSD_PROCSTAT_AUXV)
943328875Seadler			return 0;
944328875Seadler		break;
945328875Seadler#endif
946328875Seadler	default:
947328875Seadler		return 0;
948328875Seadler	}
949328875Seadler
950300899Sdelphij	*flags |= FLAGS_DID_AUXV;
951300899Sdelphij
952300899Sdelphij	nval = 0;
953300899Sdelphij	for (size_t off = 0; off + elsize <= descsz; off += elsize) {
954300899Sdelphij		(void)memcpy(xauxv_addr, &nbuf[doff + off], xauxv_sizeof);
955300899Sdelphij		/* Limit processing to 50 vector entries to prevent DoS */
956300899Sdelphij		if (nval++ >= 50) {
957300899Sdelphij			file_error(ms, 0, "Too many ELF Auxv elements");
958300899Sdelphij			return 1;
959300899Sdelphij		}
960300899Sdelphij
961300899Sdelphij		switch(xauxv_type) {
962300899Sdelphij		case AT_LINUX_EXECFN:
963300899Sdelphij			is_string = 1;
964300899Sdelphij			tag = "execfn";
965300899Sdelphij			break;
966300899Sdelphij		case AT_LINUX_PLATFORM:
967300899Sdelphij			is_string = 1;
968300899Sdelphij			tag = "platform";
969300899Sdelphij			break;
970300899Sdelphij		case AT_LINUX_UID:
971300899Sdelphij			is_string = 0;
972300899Sdelphij			tag = "real uid";
973300899Sdelphij			break;
974300899Sdelphij		case AT_LINUX_GID:
975300899Sdelphij			is_string = 0;
976300899Sdelphij			tag = "real gid";
977300899Sdelphij			break;
978300899Sdelphij		case AT_LINUX_EUID:
979300899Sdelphij			is_string = 0;
980300899Sdelphij			tag = "effective uid";
981300899Sdelphij			break;
982300899Sdelphij		case AT_LINUX_EGID:
983300899Sdelphij			is_string = 0;
984300899Sdelphij			tag = "effective gid";
985300899Sdelphij			break;
986300899Sdelphij		default:
987300899Sdelphij			is_string = 0;
988300899Sdelphij			tag = NULL;
989300899Sdelphij			break;
990300899Sdelphij		}
991300899Sdelphij
992300899Sdelphij		if (tag == NULL)
993300899Sdelphij			continue;
994300899Sdelphij
995300899Sdelphij		if (is_string) {
996300899Sdelphij			char buf[256];
997300899Sdelphij			ssize_t buflen;
998300899Sdelphij			buflen = get_string_on_virtaddr(ms, swap, clazz, fd,
999300899Sdelphij			    ph_off, ph_num, fsize, xauxv_val, buf, sizeof(buf));
1000300899Sdelphij
1001300899Sdelphij			if (buflen == 0)
1002300899Sdelphij				continue;
1003300899Sdelphij
1004300899Sdelphij			if (file_printf(ms, ", %s: '%s'", tag, buf) == -1)
1005300899Sdelphij				return 0;
1006300899Sdelphij		} else {
1007300899Sdelphij			if (file_printf(ms, ", %s: %d", tag, (int) xauxv_val)
1008300899Sdelphij			    == -1)
1009300899Sdelphij				return 0;
1010300899Sdelphij		}
1011300899Sdelphij	}
1012300899Sdelphij	return 1;
1013300899Sdelphij#else
1014300899Sdelphij	return 0;
1015300899Sdelphij#endif
1016300899Sdelphij}
1017300899Sdelphij
1018300899Sdelphijprivate size_t
1019277592Sdelphijdonote(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
1020300899Sdelphij    int clazz, int swap, size_t align, int *flags, uint16_t *notecount,
1021300899Sdelphij    int fd, off_t ph_off, int ph_num, off_t fsize)
1022277592Sdelphij{
1023277592Sdelphij	Elf32_Nhdr nh32;
1024277592Sdelphij	Elf64_Nhdr nh64;
1025277592Sdelphij	size_t noff, doff;
1026277592Sdelphij	uint32_t namesz, descsz;
1027277592Sdelphij	unsigned char *nbuf = CAST(unsigned char *, vbuf);
1028277592Sdelphij
1029277592Sdelphij	if (*notecount == 0)
1030277592Sdelphij		return 0;
1031277592Sdelphij	--*notecount;
1032277592Sdelphij
1033277592Sdelphij	if (xnh_sizeof + offset > size) {
1034277592Sdelphij		/*
1035277592Sdelphij		 * We're out of note headers.
1036277592Sdelphij		 */
1037277592Sdelphij		return xnh_sizeof + offset;
1038277592Sdelphij	}
1039277592Sdelphij
1040277592Sdelphij	(void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
1041277592Sdelphij	offset += xnh_sizeof;
1042277592Sdelphij
1043277592Sdelphij	namesz = xnh_namesz;
1044277592Sdelphij	descsz = xnh_descsz;
1045300899Sdelphij
1046277592Sdelphij	if ((namesz == 0) && (descsz == 0)) {
1047277592Sdelphij		/*
1048277592Sdelphij		 * We're out of note headers.
1049277592Sdelphij		 */
1050277592Sdelphij		return (offset >= size) ? offset : size;
1051277592Sdelphij	}
1052277592Sdelphij
1053277592Sdelphij	if (namesz & 0x80000000) {
1054328875Seadler	    (void)file_printf(ms, ", bad note name size %#lx",
1055277592Sdelphij		(unsigned long)namesz);
1056277592Sdelphij	    return 0;
1057277592Sdelphij	}
1058277592Sdelphij
1059277592Sdelphij	if (descsz & 0x80000000) {
1060328875Seadler	    (void)file_printf(ms, ", bad note description size %#lx",
1061277592Sdelphij		(unsigned long)descsz);
1062277592Sdelphij	    return 0;
1063277592Sdelphij	}
1064277592Sdelphij
1065277592Sdelphij	noff = offset;
1066277592Sdelphij	doff = ELF_ALIGN(offset + namesz);
1067277592Sdelphij
1068277592Sdelphij	if (offset + namesz > size) {
1069277592Sdelphij		/*
1070277592Sdelphij		 * We're past the end of the buffer.
1071277592Sdelphij		 */
1072277592Sdelphij		return doff;
1073277592Sdelphij	}
1074277592Sdelphij
1075277592Sdelphij	offset = ELF_ALIGN(doff + descsz);
1076277592Sdelphij	if (doff + descsz > size) {
1077277592Sdelphij		/*
1078277592Sdelphij		 * We're past the end of the buffer.
1079277592Sdelphij		 */
1080277592Sdelphij		return (offset >= size) ? offset : size;
1081277592Sdelphij	}
1082277592Sdelphij
1083300899Sdelphij
1084277592Sdelphij	if ((*flags & FLAGS_DID_OS_NOTE) == 0) {
1085277592Sdelphij		if (do_os_note(ms, nbuf, xnh_type, swap,
1086277592Sdelphij		    namesz, descsz, noff, doff, flags))
1087300899Sdelphij			return offset;
1088277592Sdelphij	}
1089277592Sdelphij
1090277592Sdelphij	if ((*flags & FLAGS_DID_BUILD_ID) == 0) {
1091277592Sdelphij		if (do_bid_note(ms, nbuf, xnh_type, swap,
1092277592Sdelphij		    namesz, descsz, noff, doff, flags))
1093300899Sdelphij			return offset;
1094277592Sdelphij	}
1095277592Sdelphij
1096277592Sdelphij	if ((*flags & FLAGS_DID_NETBSD_PAX) == 0) {
1097277592Sdelphij		if (do_pax_note(ms, nbuf, xnh_type, swap,
1098277592Sdelphij		    namesz, descsz, noff, doff, flags))
1099300899Sdelphij			return offset;
1100277592Sdelphij	}
1101277592Sdelphij
1102277592Sdelphij	if ((*flags & FLAGS_DID_CORE) == 0) {
1103277592Sdelphij		if (do_core_note(ms, nbuf, xnh_type, swap,
1104277592Sdelphij		    namesz, descsz, noff, doff, flags, size, clazz))
1105300899Sdelphij			return offset;
1106277592Sdelphij	}
1107277592Sdelphij
1108300899Sdelphij	if ((*flags & FLAGS_DID_AUXV) == 0) {
1109300899Sdelphij		if (do_auxv_note(ms, nbuf, xnh_type, swap,
1110300899Sdelphij			namesz, descsz, noff, doff, flags, size, clazz,
1111300899Sdelphij			fd, ph_off, ph_num, fsize))
1112300899Sdelphij			return offset;
1113300899Sdelphij	}
1114300899Sdelphij
1115277592Sdelphij	if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) {
1116277592Sdelphij		if (descsz > 100)
1117277592Sdelphij			descsz = 100;
1118277592Sdelphij		switch (xnh_type) {
1119277592Sdelphij	    	case NT_NETBSD_VERSION:
1120300899Sdelphij			return offset;
1121277592Sdelphij		case NT_NETBSD_MARCH:
1122277592Sdelphij			if (*flags & FLAGS_DID_NETBSD_MARCH)
1123300899Sdelphij				return offset;
1124277592Sdelphij			*flags |= FLAGS_DID_NETBSD_MARCH;
1125277592Sdelphij			if (file_printf(ms, ", compiled for: %.*s",
1126277592Sdelphij			    (int)descsz, (const char *)&nbuf[doff]) == -1)
1127300899Sdelphij				return offset;
1128277592Sdelphij			break;
1129277592Sdelphij		case NT_NETBSD_CMODEL:
1130277592Sdelphij			if (*flags & FLAGS_DID_NETBSD_CMODEL)
1131300899Sdelphij				return offset;
1132277592Sdelphij			*flags |= FLAGS_DID_NETBSD_CMODEL;
1133277592Sdelphij			if (file_printf(ms, ", compiler model: %.*s",
1134277592Sdelphij			    (int)descsz, (const char *)&nbuf[doff]) == -1)
1135300899Sdelphij				return offset;
1136277592Sdelphij			break;
1137277592Sdelphij		default:
1138277592Sdelphij			if (*flags & FLAGS_DID_NETBSD_UNKNOWN)
1139300899Sdelphij				return offset;
1140277592Sdelphij			*flags |= FLAGS_DID_NETBSD_UNKNOWN;
1141277592Sdelphij			if (file_printf(ms, ", note=%u", xnh_type) == -1)
1142300899Sdelphij				return offset;
1143277592Sdelphij			break;
1144277592Sdelphij		}
1145300899Sdelphij		return offset;
1146277592Sdelphij	}
1147277592Sdelphij
1148133359Sobrien	return offset;
114968349Sobrien}
115068349Sobrien
1151186690Sobrien/* SunOS 5.x hardware capability descriptions */
1152186690Sobrientypedef struct cap_desc {
1153186690Sobrien	uint64_t cd_mask;
1154186690Sobrien	const char *cd_name;
1155186690Sobrien} cap_desc_t;
1156186690Sobrien
1157186690Sobrienstatic const cap_desc_t cap_desc_sparc[] = {
1158186690Sobrien	{ AV_SPARC_MUL32,		"MUL32" },
1159186690Sobrien	{ AV_SPARC_DIV32,		"DIV32" },
1160186690Sobrien	{ AV_SPARC_FSMULD,		"FSMULD" },
1161186690Sobrien	{ AV_SPARC_V8PLUS,		"V8PLUS" },
1162186690Sobrien	{ AV_SPARC_POPC,		"POPC" },
1163186690Sobrien	{ AV_SPARC_VIS,			"VIS" },
1164186690Sobrien	{ AV_SPARC_VIS2,		"VIS2" },
1165186690Sobrien	{ AV_SPARC_ASI_BLK_INIT,	"ASI_BLK_INIT" },
1166186690Sobrien	{ AV_SPARC_FMAF,		"FMAF" },
1167186690Sobrien	{ AV_SPARC_FJFMAU,		"FJFMAU" },
1168186690Sobrien	{ AV_SPARC_IMA,			"IMA" },
1169186690Sobrien	{ 0, NULL }
1170186690Sobrien};
1171186690Sobrien
1172186690Sobrienstatic const cap_desc_t cap_desc_386[] = {
1173186690Sobrien	{ AV_386_FPU,			"FPU" },
1174186690Sobrien	{ AV_386_TSC,			"TSC" },
1175186690Sobrien	{ AV_386_CX8,			"CX8" },
1176186690Sobrien	{ AV_386_SEP,			"SEP" },
1177186690Sobrien	{ AV_386_AMD_SYSC,		"AMD_SYSC" },
1178186690Sobrien	{ AV_386_CMOV,			"CMOV" },
1179186690Sobrien	{ AV_386_MMX,			"MMX" },
1180186690Sobrien	{ AV_386_AMD_MMX,		"AMD_MMX" },
1181186690Sobrien	{ AV_386_AMD_3DNow,		"AMD_3DNow" },
1182186690Sobrien	{ AV_386_AMD_3DNowx,		"AMD_3DNowx" },
1183186690Sobrien	{ AV_386_FXSR,			"FXSR" },
1184186690Sobrien	{ AV_386_SSE,			"SSE" },
1185186690Sobrien	{ AV_386_SSE2,			"SSE2" },
1186186690Sobrien	{ AV_386_PAUSE,			"PAUSE" },
1187186690Sobrien	{ AV_386_SSE3,			"SSE3" },
1188186690Sobrien	{ AV_386_MON,			"MON" },
1189186690Sobrien	{ AV_386_CX16,			"CX16" },
1190186690Sobrien	{ AV_386_AHF,			"AHF" },
1191186690Sobrien	{ AV_386_TSCP,			"TSCP" },
1192186690Sobrien	{ AV_386_AMD_SSE4A,		"AMD_SSE4A" },
1193186690Sobrien	{ AV_386_POPCNT,		"POPCNT" },
1194186690Sobrien	{ AV_386_AMD_LZCNT,		"AMD_LZCNT" },
1195186690Sobrien	{ AV_386_SSSE3,			"SSSE3" },
1196186690Sobrien	{ AV_386_SSE4_1,		"SSE4.1" },
1197186690Sobrien	{ AV_386_SSE4_2,		"SSE4.2" },
1198186690Sobrien	{ 0, NULL }
1199186690Sobrien};
1200186690Sobrien
1201133359Sobrienprivate int
1202186690Sobriendoshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
1203277592Sdelphij    size_t size, off_t fsize, int mach, int strtab, int *flags,
1204277592Sdelphij    uint16_t *notecount)
120568349Sobrien{
1206133359Sobrien	Elf32_Shdr sh32;
1207133359Sobrien	Elf64_Shdr sh64;
1208328875Seadler	int stripped = 1, has_debug_info = 0;
1209275668Sdelphij	size_t nbadcap = 0;
1210159764Sobrien	void *nbuf;
1211267843Sdelphij	off_t noff, coff, name_off;
1212328875Seadler	uint64_t cap_hw1 = 0;	/* SunOS 5.x hardware capabilities */
1213328875Seadler	uint64_t cap_sf1 = 0;	/* SunOS 5.x software capabilities */
1214267843Sdelphij	char name[50];
1215277592Sdelphij	ssize_t namesize;
1216133359Sobrien
1217159764Sobrien	if (size != xsh_sizeof) {
1218133359Sobrien		if (file_printf(ms, ", corrupted section header size") == -1)
1219133359Sobrien			return -1;
1220133359Sobrien		return 0;
1221133359Sobrien	}
1222133359Sobrien
1223267843Sdelphij	/* Read offset of name section to be able to read section names later */
1224284778Sdelphij	if (pread(fd, xsh_addr, xsh_sizeof, CAST(off_t, (off + size * strtab)))
1225284778Sdelphij	    < (ssize_t)xsh_sizeof) {
1226328875Seadler		if (file_printf(ms, ", missing section headers") == -1)
1227328875Seadler			return -1;
1228328875Seadler		return 0;
1229267843Sdelphij	}
1230267843Sdelphij	name_off = xsh_offset;
1231267843Sdelphij
1232133359Sobrien	for ( ; num; num--) {
1233267843Sdelphij		/* Read the name of this section. */
1234277592Sdelphij		if ((namesize = pread(fd, name, sizeof(name) - 1, name_off + xsh_name)) == -1) {
1235267843Sdelphij			file_badread(ms);
1236226048Sobrien			return -1;
1237226048Sobrien		}
1238277592Sdelphij		name[namesize] = '\0';
1239328875Seadler		if (strcmp(name, ".debug_info") == 0) {
1240328875Seadler			has_debug_info = 1;
1241267843Sdelphij			stripped = 0;
1242328875Seadler		}
1243267843Sdelphij
1244277592Sdelphij		if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) {
1245133359Sobrien			file_badread(ms);
1246133359Sobrien			return -1;
1247133359Sobrien		}
1248226048Sobrien		off += size;
1249226048Sobrien
1250226048Sobrien		/* Things we can determine before we seek */
1251159764Sobrien		switch (xsh_type) {
1252159764Sobrien		case SHT_SYMTAB:
1253159764Sobrien#if 0
1254159764Sobrien		case SHT_DYNSYM:
1255159764Sobrien#endif
1256159764Sobrien			stripped = 0;
1257159764Sobrien			break;
1258226048Sobrien		default:
1259276415Sdelphij			if (fsize != SIZE_UNKNOWN && xsh_offset > fsize) {
1260226048Sobrien				/* Perhaps warn here */
1261226048Sobrien				continue;
1262226048Sobrien			}
1263226048Sobrien			break;
1264226048Sobrien		}
1265226048Sobrien
1266290152Sdelphij
1267226048Sobrien		/* Things we can determine when we seek */
1268226048Sobrien		switch (xsh_type) {
1269159764Sobrien		case SHT_NOTE:
1270290152Sdelphij			if ((uintmax_t)(xsh_size + xsh_offset) >
1271290152Sdelphij			    (uintmax_t)fsize) {
1272290152Sdelphij				if (file_printf(ms,
1273328875Seadler				    ", note offset/size %#" INTMAX_T_FORMAT
1274328875Seadler				    "x+%#" INTMAX_T_FORMAT "x exceeds"
1275328875Seadler				    " file size %#" INTMAX_T_FORMAT "x",
1276290152Sdelphij				    (uintmax_t)xsh_offset, (uintmax_t)xsh_size,
1277290152Sdelphij				    (uintmax_t)fsize) == -1)
1278290152Sdelphij					return -1;
1279290152Sdelphij				return 0;
1280290152Sdelphij			}
1281267843Sdelphij			if ((nbuf = malloc(xsh_size)) == NULL) {
1282159764Sobrien				file_error(ms, errno, "Cannot allocate memory"
1283159764Sobrien				    " for note");
1284159764Sobrien				return -1;
1285159764Sobrien			}
1286290152Sdelphij			if (pread(fd, nbuf, xsh_size, xsh_offset) <
1287290152Sdelphij			    (ssize_t)xsh_size) {
1288159764Sobrien				file_badread(ms);
1289159764Sobrien				free(nbuf);
1290159764Sobrien				return -1;
1291159764Sobrien			}
1292159764Sobrien
1293159764Sobrien			noff = 0;
1294159764Sobrien			for (;;) {
1295191736Sobrien				if (noff >= (off_t)xsh_size)
1296159764Sobrien					break;
1297159764Sobrien				noff = donote(ms, nbuf, (size_t)noff,
1298300899Sdelphij				    xsh_size, clazz, swap, 4, flags, notecount,
1299300899Sdelphij				    fd, 0, 0, 0);
1300159764Sobrien				if (noff == 0)
1301159764Sobrien					break;
1302159764Sobrien			}
1303159764Sobrien			free(nbuf);
1304159764Sobrien			break;
1305186690Sobrien		case SHT_SUNW_cap:
1306267843Sdelphij			switch (mach) {
1307267843Sdelphij			case EM_SPARC:
1308267843Sdelphij			case EM_SPARCV9:
1309267843Sdelphij			case EM_IA_64:
1310267843Sdelphij			case EM_386:
1311267843Sdelphij			case EM_AMD64:
1312267843Sdelphij				break;
1313267843Sdelphij			default:
1314267843Sdelphij				goto skip;
1315267843Sdelphij			}
1316267843Sdelphij
1317275668Sdelphij			if (nbadcap > 5)
1318275668Sdelphij				break;
1319267843Sdelphij			if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) {
1320226048Sobrien				file_badseek(ms);
1321186690Sobrien				return -1;
1322186690Sobrien			}
1323186690Sobrien			coff = 0;
1324186690Sobrien			for (;;) {
1325186690Sobrien				Elf32_Cap cap32;
1326186690Sobrien				Elf64_Cap cap64;
1327191736Sobrien				char cbuf[/*CONSTCOND*/
1328191736Sobrien				    MAX(sizeof cap32, sizeof cap64)];
1329226048Sobrien				if ((coff += xcap_sizeof) > (off_t)xsh_size)
1330186690Sobrien					break;
1331186690Sobrien				if (read(fd, cbuf, (size_t)xcap_sizeof) !=
1332186690Sobrien				    (ssize_t)xcap_sizeof) {
1333186690Sobrien					file_badread(ms);
1334186690Sobrien					return -1;
1335186690Sobrien				}
1336267843Sdelphij				if (cbuf[0] == 'A') {
1337267843Sdelphij#ifdef notyet
1338267843Sdelphij					char *p = cbuf + 1;
1339267843Sdelphij					uint32_t len, tag;
1340267843Sdelphij					memcpy(&len, p, sizeof(len));
1341267843Sdelphij					p += 4;
1342267843Sdelphij					len = getu32(swap, len);
1343267843Sdelphij					if (memcmp("gnu", p, 3) != 0) {
1344267843Sdelphij					    if (file_printf(ms,
1345267843Sdelphij						", unknown capability %.3s", p)
1346267843Sdelphij						== -1)
1347267843Sdelphij						return -1;
1348267843Sdelphij					    break;
1349267843Sdelphij					}
1350267843Sdelphij					p += strlen(p) + 1;
1351267843Sdelphij					tag = *p++;
1352267843Sdelphij					memcpy(&len, p, sizeof(len));
1353267843Sdelphij					p += 4;
1354267843Sdelphij					len = getu32(swap, len);
1355267843Sdelphij					if (tag != 1) {
1356267843Sdelphij					    if (file_printf(ms, ", unknown gnu"
1357267843Sdelphij						" capability tag %d", tag)
1358267843Sdelphij						== -1)
1359267843Sdelphij						return -1;
1360267843Sdelphij					    break;
1361267843Sdelphij					}
1362267843Sdelphij					// gnu attributes
1363267843Sdelphij#endif
1364267843Sdelphij					break;
1365267843Sdelphij				}
1366186690Sobrien				(void)memcpy(xcap_addr, cbuf, xcap_sizeof);
1367186690Sobrien				switch (xcap_tag) {
1368186690Sobrien				case CA_SUNW_NULL:
1369186690Sobrien					break;
1370186690Sobrien				case CA_SUNW_HW_1:
1371186690Sobrien					cap_hw1 |= xcap_val;
1372186690Sobrien					break;
1373186690Sobrien				case CA_SUNW_SF_1:
1374186690Sobrien					cap_sf1 |= xcap_val;
1375186690Sobrien					break;
1376186690Sobrien				default:
1377186690Sobrien					if (file_printf(ms,
1378186690Sobrien					    ", with unknown capability "
1379328875Seadler					    "%#" INT64_T_FORMAT "x = %#"
1380226048Sobrien					    INT64_T_FORMAT "x",
1381191736Sobrien					    (unsigned long long)xcap_tag,
1382191736Sobrien					    (unsigned long long)xcap_val) == -1)
1383186690Sobrien						return -1;
1384275668Sdelphij					if (nbadcap++ > 2)
1385275668Sdelphij						coff = xsh_size;
1386186690Sobrien					break;
1387186690Sobrien				}
1388186690Sobrien			}
1389267843Sdelphij			/*FALLTHROUGH*/
1390267843Sdelphij		skip:
1391226048Sobrien		default:
1392226048Sobrien			break;
1393133359Sobrien		}
1394133359Sobrien	}
1395267843Sdelphij
1396328875Seadler	if (has_debug_info) {
1397328875Seadler		if (file_printf(ms, ", with debug_info") == -1)
1398328875Seadler			return -1;
1399328875Seadler	}
1400159764Sobrien	if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
1401133359Sobrien		return -1;
1402186690Sobrien	if (cap_hw1) {
1403186690Sobrien		const cap_desc_t *cdp;
1404186690Sobrien		switch (mach) {
1405186690Sobrien		case EM_SPARC:
1406186690Sobrien		case EM_SPARC32PLUS:
1407186690Sobrien		case EM_SPARCV9:
1408186690Sobrien			cdp = cap_desc_sparc;
1409186690Sobrien			break;
1410186690Sobrien		case EM_386:
1411186690Sobrien		case EM_IA_64:
1412186690Sobrien		case EM_AMD64:
1413186690Sobrien			cdp = cap_desc_386;
1414186690Sobrien			break;
1415186690Sobrien		default:
1416186690Sobrien			cdp = NULL;
1417186690Sobrien			break;
1418186690Sobrien		}
1419186690Sobrien		if (file_printf(ms, ", uses") == -1)
1420186690Sobrien			return -1;
1421186690Sobrien		if (cdp) {
1422186690Sobrien			while (cdp->cd_name) {
1423186690Sobrien				if (cap_hw1 & cdp->cd_mask) {
1424186690Sobrien					if (file_printf(ms,
1425186690Sobrien					    " %s", cdp->cd_name) == -1)
1426186690Sobrien						return -1;
1427186690Sobrien					cap_hw1 &= ~cdp->cd_mask;
1428186690Sobrien				}
1429186690Sobrien				++cdp;
1430186690Sobrien			}
1431186690Sobrien			if (cap_hw1)
1432186690Sobrien				if (file_printf(ms,
1433328875Seadler				    " unknown hardware capability %#"
1434226048Sobrien				    INT64_T_FORMAT "x",
1435191736Sobrien				    (unsigned long long)cap_hw1) == -1)
1436186690Sobrien					return -1;
1437186690Sobrien		} else {
1438186690Sobrien			if (file_printf(ms,
1439328875Seadler			    " hardware capability %#" INT64_T_FORMAT "x",
1440191736Sobrien			    (unsigned long long)cap_hw1) == -1)
1441186690Sobrien				return -1;
1442186690Sobrien		}
1443186690Sobrien	}
1444186690Sobrien	if (cap_sf1) {
1445186690Sobrien		if (cap_sf1 & SF1_SUNW_FPUSED) {
1446186690Sobrien			if (file_printf(ms,
1447186690Sobrien			    (cap_sf1 & SF1_SUNW_FPKNWN)
1448186690Sobrien			    ? ", uses frame pointer"
1449186690Sobrien			    : ", not known to use frame pointer") == -1)
1450186690Sobrien				return -1;
1451186690Sobrien		}
1452186690Sobrien		cap_sf1 &= ~SF1_SUNW_MASK;
1453186690Sobrien		if (cap_sf1)
1454186690Sobrien			if (file_printf(ms,
1455328875Seadler			    ", with unknown software capability %#"
1456226048Sobrien			    INT64_T_FORMAT "x",
1457191736Sobrien			    (unsigned long long)cap_sf1) == -1)
1458186690Sobrien				return -1;
1459186690Sobrien	}
1460133359Sobrien	return 0;
1461133359Sobrien}
1462133359Sobrien
1463133359Sobrien/*
1464133359Sobrien * Look through the program headers of an executable image, searching
1465133359Sobrien * for a PT_INTERP section; if one is found, it's dynamically linked,
1466133359Sobrien * otherwise it's statically linked.
1467133359Sobrien */
1468133359Sobrienprivate int
1469186690Sobriendophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
1470277592Sdelphij    int num, size_t size, off_t fsize, int sh_num, int *flags,
1471277592Sdelphij    uint16_t *notecount)
1472133359Sobrien{
1473133359Sobrien	Elf32_Phdr ph32;
1474133359Sobrien	Elf64_Phdr ph64;
1475133359Sobrien	const char *linking_style = "statically";
1476277592Sdelphij	const char *interp = "";
1477133359Sobrien	unsigned char nbuf[BUFSIZ];
1478277592Sdelphij	char ibuf[BUFSIZ];
1479226048Sobrien	ssize_t bufsize;
1480267843Sdelphij	size_t offset, align, len;
1481169942Sobrien
1482159764Sobrien	if (size != xph_sizeof) {
1483133359Sobrien		if (file_printf(ms, ", corrupted program header size") == -1)
1484186690Sobrien			return -1;
1485133359Sobrien		return 0;
1486133359Sobrien	}
1487169942Sobrien
1488133359Sobrien  	for ( ; num; num--) {
1489277592Sdelphij		if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) {
1490267843Sdelphij			file_badread(ms);
1491133359Sobrien			return -1;
1492133359Sobrien		}
1493169942Sobrien
1494226048Sobrien		off += size;
1495277592Sdelphij		bufsize = 0;
1496277592Sdelphij		align = 4;
1497169942Sobrien
1498226048Sobrien		/* Things we can determine before we seek */
1499159764Sobrien		switch (xph_type) {
1500133359Sobrien		case PT_DYNAMIC:
1501133359Sobrien			linking_style = "dynamically";
1502133359Sobrien			break;
1503277592Sdelphij		case PT_NOTE:
1504277592Sdelphij			if (sh_num)	/* Did this through section headers */
1505277592Sdelphij				continue;
1506277592Sdelphij			if (((align = xph_align) & 0x80000000UL) != 0 ||
1507277592Sdelphij			    align < 4) {
1508277592Sdelphij				if (file_printf(ms,
1509328875Seadler				    ", invalid note alignment %#lx",
1510277592Sdelphij				    (unsigned long)align) == -1)
1511277592Sdelphij					return -1;
1512277592Sdelphij				align = 4;
1513277592Sdelphij			}
1514277592Sdelphij			/*FALLTHROUGH*/
1515133359Sobrien		case PT_INTERP:
1516277592Sdelphij			len = xph_filesz < sizeof(nbuf) ? xph_filesz
1517277592Sdelphij			    : sizeof(nbuf);
1518277592Sdelphij			bufsize = pread(fd, nbuf, len, xph_offset);
1519277592Sdelphij			if (bufsize == -1) {
1520277592Sdelphij				file_badread(ms);
1521277592Sdelphij				return -1;
1522277592Sdelphij			}
1523133359Sobrien			break;
1524226048Sobrien		default:
1525276415Sdelphij			if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
1526226048Sobrien				/* Maybe warn here? */
1527226048Sobrien				continue;
1528226048Sobrien			}
1529226048Sobrien			break;
1530226048Sobrien		}
1531226048Sobrien
1532226048Sobrien		/* Things we can determine when we seek */
1533226048Sobrien		switch (xph_type) {
1534277592Sdelphij		case PT_INTERP:
1535277592Sdelphij			if (bufsize && nbuf[0]) {
1536277592Sdelphij				nbuf[bufsize - 1] = '\0';
1537277592Sdelphij				interp = (const char *)nbuf;
1538277592Sdelphij			} else
1539277592Sdelphij				interp = "*empty*";
1540277592Sdelphij			break;
1541133359Sobrien		case PT_NOTE:
1542133359Sobrien			/*
1543133359Sobrien			 * This is a PT_NOTE section; loop through all the notes
1544133359Sobrien			 * in the section.
1545133359Sobrien			 */
1546133359Sobrien			offset = 0;
1547133359Sobrien			for (;;) {
1548133359Sobrien				if (offset >= (size_t)bufsize)
1549133359Sobrien					break;
1550133359Sobrien				offset = donote(ms, nbuf, offset,
1551186690Sobrien				    (size_t)bufsize, clazz, swap, align,
1552300899Sdelphij				    flags, notecount, fd, 0, 0, 0);
1553133359Sobrien				if (offset == 0)
1554133359Sobrien					break;
1555133359Sobrien			}
1556133359Sobrien			break;
1557175296Sobrien		default:
1558175296Sobrien			break;
1559133359Sobrien		}
1560133359Sobrien	}
1561277592Sdelphij	if (file_printf(ms, ", %s linked", linking_style)
1562133359Sobrien	    == -1)
1563277592Sdelphij		return -1;
1564277592Sdelphij	if (interp[0])
1565277592Sdelphij		if (file_printf(ms, ", interpreter %s",
1566277592Sdelphij		    file_printable(ibuf, sizeof(ibuf), interp)) == -1)
1567277592Sdelphij			return -1;
1568133359Sobrien	return 0;
1569133359Sobrien}
1570133359Sobrien
1571133359Sobrien
1572133359Sobrienprotected int
1573133359Sobrienfile_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
1574133359Sobrien    size_t nbytes)
1575133359Sobrien{
157668349Sobrien	union {
1577103373Sobrien		int32_t l;
1578103373Sobrien		char c[sizeof (int32_t)];
157968349Sobrien	} u;
1580186690Sobrien	int clazz;
158168349Sobrien	int swap;
1582169942Sobrien	struct stat st;
1583169942Sobrien	off_t fsize;
1584169942Sobrien	int flags = 0;
1585186690Sobrien	Elf32_Ehdr elf32hdr;
1586186690Sobrien	Elf64_Ehdr elf64hdr;
1587277592Sdelphij	uint16_t type, phnum, shnum, notecount;
158868349Sobrien
1589284778Sdelphij	if (ms->flags & (MAGIC_MIME|MAGIC_APPLE|MAGIC_EXTENSION))
1590186690Sobrien		return 0;
159168349Sobrien	/*
159268349Sobrien	 * ELF executables have multiple section headers in arbitrary
159368349Sobrien	 * file locations and thus file(1) cannot determine it from easily.
159468349Sobrien	 * Instead we traverse thru all section headers until a symbol table
159568349Sobrien	 * one is found or else the binary is stripped.
1596186690Sobrien	 * Return immediately if it's not ELF (so we avoid pipe2file unless needed).
159768349Sobrien	 */
159868349Sobrien	if (buf[EI_MAG0] != ELFMAG0
159968349Sobrien	    || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
160068349Sobrien	    || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
1601186690Sobrien		return 0;
160268349Sobrien
1603186690Sobrien	/*
1604186690Sobrien	 * If we cannot seek, it must be a pipe, socket or fifo.
1605186690Sobrien	 */
1606186690Sobrien	if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
1607186690Sobrien		fd = file_pipe2file(ms, fd, buf, nbytes);
160868349Sobrien
1609186690Sobrien	if (fstat(fd, &st) == -1) {
1610186690Sobrien  		file_badread(ms);
1611186690Sobrien		return -1;
161268349Sobrien	}
1613276415Sdelphij	if (S_ISREG(st.st_mode) || st.st_size != 0)
1614276415Sdelphij		fsize = st.st_size;
1615276415Sdelphij	else
1616276415Sdelphij		fsize = SIZE_UNKNOWN;
161768349Sobrien
1618186690Sobrien	clazz = buf[EI_CLASS];
161968349Sobrien
1620186690Sobrien	switch (clazz) {
1621186690Sobrien	case ELFCLASS32:
1622186690Sobrien#undef elf_getu
1623186690Sobrien#define elf_getu(a, b)	elf_getu32(a, b)
1624186690Sobrien#undef elfhdr
1625186690Sobrien#define elfhdr elf32hdr
1626186690Sobrien#include "elfclass.h"
1627186690Sobrien	case ELFCLASS64:
1628186690Sobrien#undef elf_getu
1629186690Sobrien#define elf_getu(a, b)	elf_getu64(a, b)
1630186690Sobrien#undef elfhdr
1631186690Sobrien#define elfhdr elf64hdr
1632186690Sobrien#include "elfclass.h"
1633186690Sobrien	default:
1634186690Sobrien	    if (file_printf(ms, ", unknown class %d", clazz) == -1)
1635186690Sobrien		    return -1;
1636186690Sobrien	    break;
163768349Sobrien	}
1638133359Sobrien	return 0;
163968349Sobrien}
164068349Sobrien#endif
1641