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