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