readelf.c revision 1.8
1/*	$NetBSD: readelf.c,v 1.8 2014/06/13 02:08:06 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.103 2014/05/02 02:25:10 christos Exp $")
33#else
34__RCSID("$NetBSD: readelf.c,v 1.8 2014/06/13 02:08:06 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 };
477	Elf64_Nhdr nh64 = { 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	(void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
486	offset += xnh_sizeof;
487
488	namesz = xnh_namesz;
489	descsz = xnh_descsz;
490	if ((namesz == 0) && (descsz == 0)) {
491		/*
492		 * We're out of note headers.
493		 */
494		return (offset >= size) ? offset : size;
495	}
496
497	if (namesz & 0x80000000) {
498	    (void)file_printf(ms, ", bad note name size 0x%lx",
499		(unsigned long)namesz);
500	    return offset;
501	}
502
503	if (descsz & 0x80000000) {
504	    (void)file_printf(ms, ", bad note description size 0x%lx",
505		(unsigned long)descsz);
506	    return offset;
507	}
508
509
510	noff = offset;
511	doff = ELF_ALIGN(offset + namesz);
512
513	if (offset + namesz > size) {
514		/*
515		 * We're past the end of the buffer.
516		 */
517		return doff;
518	}
519
520	offset = ELF_ALIGN(doff + descsz);
521	if (doff + descsz > size) {
522		/*
523		 * We're past the end of the buffer.
524		 */
525		return (offset >= size) ? offset : size;
526	}
527
528	if ((*flags & (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) ==
529	    (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID))
530		goto core;
531
532	if (namesz == 5 && strcmp((char *)&nbuf[noff], "SuSE") == 0 &&
533	    xnh_type == NT_GNU_VERSION && descsz == 2) {
534	    file_printf(ms, ", for SuSE %d.%d", nbuf[doff], nbuf[doff + 1]);
535	}
536	if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
537	    xnh_type == NT_GNU_VERSION && descsz == 16) {
538		uint32_t desc[4];
539		(void)memcpy(desc, &nbuf[doff], sizeof(desc));
540
541		if (file_printf(ms, ", for GNU/") == -1)
542			return size;
543		switch (elf_getu32(swap, desc[0])) {
544		case GNU_OS_LINUX:
545			if (file_printf(ms, "Linux") == -1)
546				return size;
547			break;
548		case GNU_OS_HURD:
549			if (file_printf(ms, "Hurd") == -1)
550				return size;
551			break;
552		case GNU_OS_SOLARIS:
553			if (file_printf(ms, "Solaris") == -1)
554				return size;
555			break;
556		case GNU_OS_KFREEBSD:
557			if (file_printf(ms, "kFreeBSD") == -1)
558				return size;
559			break;
560		case GNU_OS_KNETBSD:
561			if (file_printf(ms, "kNetBSD") == -1)
562				return size;
563			break;
564		default:
565			if (file_printf(ms, "<unknown>") == -1)
566				return size;
567		}
568		if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
569		    elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
570			return size;
571		*flags |= FLAGS_DID_NOTE;
572		return size;
573	}
574
575	if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 &&
576	    xnh_type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) {
577	    uint8_t desc[20];
578	    uint32_t i;
579	    if (file_printf(ms, ", BuildID[%s]=", descsz == 16 ? "md5/uuid" :
580		"sha1") == -1)
581		    return size;
582	    (void)memcpy(desc, &nbuf[doff], descsz);
583	    for (i = 0; i < descsz; i++)
584		if (file_printf(ms, "%02x", desc[i]) == -1)
585		    return size;
586	    *flags |= FLAGS_DID_BUILD_ID;
587	}
588
589	if (namesz == 4 && strcmp((char *)&nbuf[noff], "PaX") == 0 &&
590	    xnh_type == NT_NETBSD_PAX && descsz == 4) {
591		static const char *pax[] = {
592		    "+mprotect",
593		    "-mprotect",
594		    "+segvguard",
595		    "-segvguard",
596		    "+ASLR",
597		    "-ASLR",
598		};
599		uint32_t desc;
600		size_t i;
601		int did = 0;
602
603		(void)memcpy(&desc, &nbuf[doff], sizeof(desc));
604		desc = elf_getu32(swap, desc);
605
606		if (desc && file_printf(ms, ", PaX: ") == -1)
607			return size;
608
609		for (i = 0; i < __arraycount(pax); i++) {
610			if (((1 << i) & desc) == 0)
611				continue;
612			if (file_printf(ms, "%s%s", did++ ? "," : "",
613			    pax[i]) == -1)
614				return size;
615		}
616	}
617
618	if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) {
619		switch (xnh_type) {
620		case NT_NETBSD_VERSION:
621			if (descsz == 4) {
622				do_note_netbsd_version(ms, swap, &nbuf[doff]);
623				*flags |= FLAGS_DID_NOTE;
624				return size;
625			}
626			break;
627		case NT_NETBSD_MARCH:
628			if (file_printf(ms, ", compiled for: %.*s", (int)descsz,
629			    (const char *)&nbuf[doff]) == -1)
630				return size;
631			break;
632		case NT_NETBSD_CMODEL:
633			if (file_printf(ms, ", compiler model: %.*s",
634			    (int)descsz, (const char *)&nbuf[doff]) == -1)
635				return size;
636			break;
637		default:
638			if (file_printf(ms, ", note=%u", xnh_type) == -1)
639				return size;
640			break;
641		}
642		return size;
643	}
644
645	if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0) {
646	    	if (xnh_type == NT_FREEBSD_VERSION && descsz == 4) {
647			do_note_freebsd_version(ms, swap, &nbuf[doff]);
648			*flags |= FLAGS_DID_NOTE;
649			return size;
650		}
651	}
652
653	if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 &&
654	    xnh_type == NT_OPENBSD_VERSION && descsz == 4) {
655		if (file_printf(ms, ", for OpenBSD") == -1)
656			return size;
657		/* Content of note is always 0 */
658		*flags |= FLAGS_DID_NOTE;
659		return size;
660	}
661
662	if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 &&
663	    xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) {
664		uint32_t desc;
665		if (file_printf(ms, ", for DragonFly") == -1)
666			return size;
667		(void)memcpy(&desc, &nbuf[doff], sizeof(desc));
668		desc = elf_getu32(swap, desc);
669		if (file_printf(ms, " %d.%d.%d", desc / 100000,
670		    desc / 10000 % 10, desc % 10000) == -1)
671			return size;
672		*flags |= FLAGS_DID_NOTE;
673		return size;
674	}
675
676core:
677	/*
678	 * Sigh.  The 2.0.36 kernel in Debian 2.1, at
679	 * least, doesn't correctly implement name
680	 * sections, in core dumps, as specified by
681	 * the "Program Linking" section of "UNIX(R) System
682	 * V Release 4 Programmer's Guide: ANSI C and
683	 * Programming Support Tools", because my copy
684	 * clearly says "The first 'namesz' bytes in 'name'
685	 * contain a *null-terminated* [emphasis mine]
686	 * character representation of the entry's owner
687	 * or originator", but the 2.0.36 kernel code
688	 * doesn't include the terminating null in the
689	 * name....
690	 */
691	if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) ||
692	    (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) {
693		os_style = OS_STYLE_SVR4;
694	}
695
696	if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) {
697		os_style = OS_STYLE_FREEBSD;
698	}
699
700	if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11)
701	    == 0)) {
702		os_style = OS_STYLE_NETBSD;
703	}
704
705#ifdef ELFCORE
706	if ((*flags & FLAGS_DID_CORE) != 0)
707		return size;
708
709	if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
710		if (file_printf(ms, ", %s-style", os_style_names[os_style])
711		    == -1)
712			return size;
713		*flags |= FLAGS_DID_CORE_STYLE;
714	}
715
716	switch (os_style) {
717	case OS_STYLE_NETBSD:
718		if (xnh_type == NT_NETBSD_CORE_PROCINFO) {
719			uint32_t signo;
720			/*
721			 * Extract the program name.  It is at
722			 * offset 0x7c, and is up to 32-bytes,
723			 * including the terminating NUL.
724			 */
725			if (file_printf(ms, ", from '%.31s'",
726			    &nbuf[doff + 0x7c]) == -1)
727				return size;
728
729			/*
730			 * Extract the signal number.  It is at
731			 * offset 0x08.
732			 */
733			(void)memcpy(&signo, &nbuf[doff + 0x08],
734			    sizeof(signo));
735			if (file_printf(ms, " (signal %u)",
736			    elf_getu32(swap, signo)) == -1)
737				return size;
738			*flags |= FLAGS_DID_CORE;
739			return size;
740		}
741		break;
742
743	default:
744		if (xnh_type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
745			size_t i, j;
746			unsigned char c;
747			/*
748			 * Extract the program name.  We assume
749			 * it to be 16 characters (that's what it
750			 * is in SunOS 5.x and Linux).
751			 *
752			 * Unfortunately, it's at a different offset
753			 * in various OSes, so try multiple offsets.
754			 * If the characters aren't all printable,
755			 * reject it.
756			 */
757			for (i = 0; i < NOFFSETS; i++) {
758				unsigned char *cname, *cp;
759				size_t reloffset = prpsoffsets(i);
760				size_t noffset = doff + reloffset;
761				size_t k;
762				for (j = 0; j < 16; j++, noffset++,
763				    reloffset++) {
764					/*
765					 * Make sure we're not past
766					 * the end of the buffer; if
767					 * we are, just give up.
768					 */
769					if (noffset >= size)
770						goto tryanother;
771
772					/*
773					 * Make sure we're not past
774					 * the end of the contents;
775					 * if we are, this obviously
776					 * isn't the right offset.
777					 */
778					if (reloffset >= descsz)
779						goto tryanother;
780
781					c = nbuf[noffset];
782					if (c == '\0') {
783						/*
784						 * A '\0' at the
785						 * beginning is
786						 * obviously wrong.
787						 * Any other '\0'
788						 * means we're done.
789						 */
790						if (j == 0)
791							goto tryanother;
792						else
793							break;
794					} else {
795						/*
796						 * A nonprintable
797						 * character is also
798						 * wrong.
799						 */
800						if (!isprint(c) || isquote(c))
801							goto tryanother;
802					}
803				}
804				/*
805				 * Well, that worked.
806				 */
807
808				/*
809				 * Try next offsets, in case this match is
810				 * in the middle of a string.
811				 */
812				for (k = i + 1 ; k < NOFFSETS ; k++) {
813					size_t no;
814					int adjust = 1;
815					if (prpsoffsets(k) >= prpsoffsets(i))
816						continue;
817					for (no = doff + prpsoffsets(k);
818					     no < doff + prpsoffsets(i); no++)
819						adjust = adjust
820						         && isprint(nbuf[no]);
821					if (adjust)
822						i = k;
823				}
824
825				cname = (unsigned char *)
826				    &nbuf[doff + prpsoffsets(i)];
827				for (cp = cname; *cp && isprint(*cp); cp++)
828					continue;
829				/*
830				 * Linux apparently appends a space at the end
831				 * of the command line: remove it.
832				 */
833				while (cp > cname && isspace(cp[-1]))
834					cp--;
835				if (file_printf(ms, ", from '%.*s'",
836				    (int)(cp - cname), cname) == -1)
837					return size;
838				*flags |= FLAGS_DID_CORE;
839				return size;
840
841			tryanother:
842				;
843			}
844		}
845		break;
846	}
847#endif
848	return offset;
849}
850
851/* SunOS 5.x hardware capability descriptions */
852typedef struct cap_desc {
853	uint64_t cd_mask;
854	const char *cd_name;
855} cap_desc_t;
856
857static const cap_desc_t cap_desc_sparc[] = {
858	{ AV_SPARC_MUL32,		"MUL32" },
859	{ AV_SPARC_DIV32,		"DIV32" },
860	{ AV_SPARC_FSMULD,		"FSMULD" },
861	{ AV_SPARC_V8PLUS,		"V8PLUS" },
862	{ AV_SPARC_POPC,		"POPC" },
863	{ AV_SPARC_VIS,			"VIS" },
864	{ AV_SPARC_VIS2,		"VIS2" },
865	{ AV_SPARC_ASI_BLK_INIT,	"ASI_BLK_INIT" },
866	{ AV_SPARC_FMAF,		"FMAF" },
867	{ AV_SPARC_FJFMAU,		"FJFMAU" },
868	{ AV_SPARC_IMA,			"IMA" },
869	{ 0, NULL }
870};
871
872static const cap_desc_t cap_desc_386[] = {
873	{ AV_386_FPU,			"FPU" },
874	{ AV_386_TSC,			"TSC" },
875	{ AV_386_CX8,			"CX8" },
876	{ AV_386_SEP,			"SEP" },
877	{ AV_386_AMD_SYSC,		"AMD_SYSC" },
878	{ AV_386_CMOV,			"CMOV" },
879	{ AV_386_MMX,			"MMX" },
880	{ AV_386_AMD_MMX,		"AMD_MMX" },
881	{ AV_386_AMD_3DNow,		"AMD_3DNow" },
882	{ AV_386_AMD_3DNowx,		"AMD_3DNowx" },
883	{ AV_386_FXSR,			"FXSR" },
884	{ AV_386_SSE,			"SSE" },
885	{ AV_386_SSE2,			"SSE2" },
886	{ AV_386_PAUSE,			"PAUSE" },
887	{ AV_386_SSE3,			"SSE3" },
888	{ AV_386_MON,			"MON" },
889	{ AV_386_CX16,			"CX16" },
890	{ AV_386_AHF,			"AHF" },
891	{ AV_386_TSCP,			"TSCP" },
892	{ AV_386_AMD_SSE4A,		"AMD_SSE4A" },
893	{ AV_386_POPCNT,		"POPCNT" },
894	{ AV_386_AMD_LZCNT,		"AMD_LZCNT" },
895	{ AV_386_SSSE3,			"SSSE3" },
896	{ AV_386_SSE4_1,		"SSE4.1" },
897	{ AV_386_SSE4_2,		"SSE4.2" },
898	{ 0, NULL }
899};
900
901private int
902doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
903    size_t size, off_t fsize, int *flags, int mach, int strtab)
904{
905	Elf32_Shdr sh32;
906	Elf64_Shdr sh64;
907	int stripped = 1;
908	void *nbuf;
909	off_t noff, coff, name_off;
910	uint64_t cap_hw1 = 0;	/* SunOS 5.x hardware capabilites */
911	uint64_t cap_sf1 = 0;	/* SunOS 5.x software capabilites */
912	char name[50];
913
914	if (size != xsh_sizeof) {
915		if (file_printf(ms, ", corrupted section header size") == -1)
916			return -1;
917		return 0;
918	}
919
920	/* Read offset of name section to be able to read section names later */
921	if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) == -1) {
922		file_badread(ms);
923		return -1;
924	}
925	name_off = xsh_offset;
926
927	for ( ; num; num--) {
928		/* Read the name of this section. */
929		if (pread(fd, name, sizeof(name), name_off + xsh_name) == -1) {
930			file_badread(ms);
931			return -1;
932		}
933		name[sizeof(name) - 1] = '\0';
934		if (strcmp(name, ".debug_info") == 0)
935			stripped = 0;
936
937		if (pread(fd, xsh_addr, xsh_sizeof, off) == -1) {
938			file_badread(ms);
939			return -1;
940		}
941		off += size;
942
943		/* Things we can determine before we seek */
944		switch (xsh_type) {
945		case SHT_SYMTAB:
946#if 0
947		case SHT_DYNSYM:
948#endif
949			stripped = 0;
950			break;
951		default:
952			if (xsh_offset > fsize) {
953				/* Perhaps warn here */
954				continue;
955			}
956			break;
957		}
958
959		/* Things we can determine when we seek */
960		switch (xsh_type) {
961		case SHT_NOTE:
962			if ((nbuf = malloc(xsh_size)) == NULL) {
963				file_error(ms, errno, "Cannot allocate memory"
964				    " for note");
965				return -1;
966			}
967			if (pread(fd, nbuf, xsh_size, xsh_offset) == -1) {
968				file_badread(ms);
969				free(nbuf);
970				return -1;
971			}
972
973			noff = 0;
974			for (;;) {
975				if (noff >= (off_t)xsh_size)
976					break;
977				noff = donote(ms, nbuf, (size_t)noff,
978				    xsh_size, clazz, swap, 4, flags);
979				if (noff == 0)
980					break;
981			}
982			free(nbuf);
983			break;
984		case SHT_SUNW_cap:
985			switch (mach) {
986			case EM_SPARC:
987			case EM_SPARCV9:
988			case EM_IA_64:
989			case EM_386:
990			case EM_AMD64:
991				break;
992			default:
993				goto skip;
994			}
995
996			if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) {
997				file_badseek(ms);
998				return -1;
999			}
1000			coff = 0;
1001			for (;;) {
1002				Elf32_Cap cap32;
1003				Elf64_Cap cap64;
1004				char cbuf[/*CONSTCOND*/
1005				    MAX(sizeof cap32, sizeof cap64)];
1006				if ((coff += xcap_sizeof) > (off_t)xsh_size)
1007					break;
1008				if (read(fd, cbuf, (size_t)xcap_sizeof) !=
1009				    (ssize_t)xcap_sizeof) {
1010					file_badread(ms);
1011					return -1;
1012				}
1013				if (cbuf[0] == 'A') {
1014#ifdef notyet
1015					char *p = cbuf + 1;
1016					uint32_t len, tag;
1017					memcpy(&len, p, sizeof(len));
1018					p += 4;
1019					len = getu32(swap, len);
1020					if (memcmp("gnu", p, 3) != 0) {
1021					    if (file_printf(ms,
1022						", unknown capability %.3s", p)
1023						== -1)
1024						return -1;
1025					    break;
1026					}
1027					p += strlen(p) + 1;
1028					tag = *p++;
1029					memcpy(&len, p, sizeof(len));
1030					p += 4;
1031					len = getu32(swap, len);
1032					if (tag != 1) {
1033					    if (file_printf(ms, ", unknown gnu"
1034						" capability tag %d", tag)
1035						== -1)
1036						return -1;
1037					    break;
1038					}
1039					// gnu attributes
1040#endif
1041					break;
1042				}
1043				(void)memcpy(xcap_addr, cbuf, xcap_sizeof);
1044				switch (xcap_tag) {
1045				case CA_SUNW_NULL:
1046					break;
1047				case CA_SUNW_HW_1:
1048					cap_hw1 |= xcap_val;
1049					break;
1050				case CA_SUNW_SF_1:
1051					cap_sf1 |= xcap_val;
1052					break;
1053				default:
1054					if (file_printf(ms,
1055					    ", with unknown capability "
1056					    "0x%" INT64_T_FORMAT "x = 0x%"
1057					    INT64_T_FORMAT "x",
1058					    (unsigned long long)xcap_tag,
1059					    (unsigned long long)xcap_val) == -1)
1060						return -1;
1061					break;
1062				}
1063			}
1064			/*FALLTHROUGH*/
1065		skip:
1066		default:
1067			break;
1068		}
1069	}
1070
1071	if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
1072		return -1;
1073	if (cap_hw1) {
1074		const cap_desc_t *cdp;
1075		switch (mach) {
1076		case EM_SPARC:
1077		case EM_SPARC32PLUS:
1078		case EM_SPARCV9:
1079			cdp = cap_desc_sparc;
1080			break;
1081		case EM_386:
1082		case EM_IA_64:
1083		case EM_AMD64:
1084			cdp = cap_desc_386;
1085			break;
1086		default:
1087			cdp = NULL;
1088			break;
1089		}
1090		if (file_printf(ms, ", uses") == -1)
1091			return -1;
1092		if (cdp) {
1093			while (cdp->cd_name) {
1094				if (cap_hw1 & cdp->cd_mask) {
1095					if (file_printf(ms,
1096					    " %s", cdp->cd_name) == -1)
1097						return -1;
1098					cap_hw1 &= ~cdp->cd_mask;
1099				}
1100				++cdp;
1101			}
1102			if (cap_hw1)
1103				if (file_printf(ms,
1104				    " unknown hardware capability 0x%"
1105				    INT64_T_FORMAT "x",
1106				    (unsigned long long)cap_hw1) == -1)
1107					return -1;
1108		} else {
1109			if (file_printf(ms,
1110			    " hardware capability 0x%" INT64_T_FORMAT "x",
1111			    (unsigned long long)cap_hw1) == -1)
1112				return -1;
1113		}
1114	}
1115	if (cap_sf1) {
1116		if (cap_sf1 & SF1_SUNW_FPUSED) {
1117			if (file_printf(ms,
1118			    (cap_sf1 & SF1_SUNW_FPKNWN)
1119			    ? ", uses frame pointer"
1120			    : ", not known to use frame pointer") == -1)
1121				return -1;
1122		}
1123		cap_sf1 &= ~SF1_SUNW_MASK;
1124		if (cap_sf1)
1125			if (file_printf(ms,
1126			    ", with unknown software capability 0x%"
1127			    INT64_T_FORMAT "x",
1128			    (unsigned long long)cap_sf1) == -1)
1129				return -1;
1130	}
1131	return 0;
1132}
1133
1134/*
1135 * Look through the program headers of an executable image, searching
1136 * for a PT_INTERP section; if one is found, it's dynamically linked,
1137 * otherwise it's statically linked.
1138 */
1139private int
1140dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
1141    int num, size_t size, off_t fsize, int *flags, int sh_num)
1142{
1143	Elf32_Phdr ph32;
1144	Elf64_Phdr ph64;
1145	const char *linking_style = "statically";
1146	const char *shared_libraries = "";
1147	unsigned char nbuf[BUFSIZ];
1148	ssize_t bufsize;
1149	size_t offset, align, len;
1150
1151	if (size != xph_sizeof) {
1152		if (file_printf(ms, ", corrupted program header size") == -1)
1153			return -1;
1154		return 0;
1155	}
1156
1157  	for ( ; num; num--) {
1158		if (pread(fd, xph_addr, xph_sizeof, off) == -1) {
1159			file_badread(ms);
1160			return -1;
1161		}
1162
1163		off += size;
1164
1165		/* Things we can determine before we seek */
1166		switch (xph_type) {
1167		case PT_DYNAMIC:
1168			linking_style = "dynamically";
1169			break;
1170		case PT_INTERP:
1171			shared_libraries = " (uses shared libs)";
1172			break;
1173		default:
1174			if (xph_offset > fsize) {
1175				/* Maybe warn here? */
1176				continue;
1177			}
1178			break;
1179		}
1180
1181		/* Things we can determine when we seek */
1182		switch (xph_type) {
1183		case PT_NOTE:
1184			if ((align = xph_align) & 0x80000000UL) {
1185				if (file_printf(ms,
1186				    ", invalid note alignment 0x%lx",
1187				    (unsigned long)align) == -1)
1188					return -1;
1189				align = 4;
1190			}
1191			if (sh_num)
1192				break;
1193			/*
1194			 * This is a PT_NOTE section; loop through all the notes
1195			 * in the section.
1196			 */
1197			len = xph_filesz < sizeof(nbuf) ? xph_filesz
1198			    : sizeof(nbuf);
1199			bufsize = pread(fd, nbuf, len, xph_offset);
1200			if (bufsize == -1) {
1201				file_badread(ms);
1202				return -1;
1203			}
1204			offset = 0;
1205			for (;;) {
1206				if (offset >= (size_t)bufsize)
1207					break;
1208				offset = donote(ms, nbuf, offset,
1209				    (size_t)bufsize, clazz, swap, align,
1210				    flags);
1211				if (offset == 0)
1212					break;
1213			}
1214			break;
1215		default:
1216			break;
1217		}
1218	}
1219	if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries)
1220	    == -1)
1221	    return -1;
1222	return 0;
1223}
1224
1225
1226protected int
1227file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf,
1228    size_t nbytes)
1229{
1230	union {
1231		int32_t l;
1232		char c[sizeof (int32_t)];
1233	} u;
1234	int clazz;
1235	int swap;
1236	struct stat st;
1237	off_t fsize;
1238	int flags = 0;
1239	Elf32_Ehdr elf32hdr;
1240	Elf64_Ehdr elf64hdr;
1241	uint16_t type;
1242
1243	if (ms->flags & (MAGIC_MIME|MAGIC_APPLE))
1244		return 0;
1245	/*
1246	 * ELF executables have multiple section headers in arbitrary
1247	 * file locations and thus file(1) cannot determine it from easily.
1248	 * Instead we traverse thru all section headers until a symbol table
1249	 * one is found or else the binary is stripped.
1250	 * Return immediately if it's not ELF (so we avoid pipe2file unless needed).
1251	 */
1252	if (buf[EI_MAG0] != ELFMAG0
1253	    || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
1254	    || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
1255		return 0;
1256
1257	/*
1258	 * If we cannot seek, it must be a pipe, socket or fifo.
1259	 */
1260	if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE))
1261		fd = file_pipe2file(ms, fd, buf, nbytes);
1262
1263	if (fstat(fd, &st) == -1) {
1264  		file_badread(ms);
1265		return -1;
1266	}
1267	fsize = st.st_size;
1268
1269	clazz = buf[EI_CLASS];
1270
1271	switch (clazz) {
1272	case ELFCLASS32:
1273#undef elf_getu
1274#define elf_getu(a, b)	elf_getu32(a, b)
1275#undef elfhdr
1276#define elfhdr elf32hdr
1277#include "elfclass.h"
1278	case ELFCLASS64:
1279#undef elf_getu
1280#define elf_getu(a, b)	elf_getu64(a, b)
1281#undef elfhdr
1282#define elfhdr elf64hdr
1283#include "elfclass.h"
1284	default:
1285	    if (file_printf(ms, ", unknown class %d", clazz) == -1)
1286		    return -1;
1287	    break;
1288	}
1289	return 0;
1290}
1291#endif
1292