xref: /titanic_51/usr/src/cmd/file/file.c (revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 
25 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
26 /*	  All Rights Reserved	*/
27 
28 /*
29  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
30  * Use is subject to license terms.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #define	_LARGEFILE64_SOURCE
36 
37 /* Get definitions for the relocation types supported. */
38 #define	ELF_TARGET_ALL
39 
40 #include <ctype.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <libelf.h>
46 #include <stdlib.h>
47 #include <limits.h>
48 #include <locale.h>
49 #include <wctype.h>
50 #include <string.h>
51 #include <errno.h>
52 #include <door.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/mkdev.h>
56 #include <sys/stat.h>
57 #include <sys/elf.h>
58 #include <procfs.h>
59 #include <sys/core.h>
60 #include <sys/dumphdr.h>
61 #include <netinet/in.h>
62 #include <gelf.h>
63 #include <elfcap.h>
64 #include <sgsrtcid.h>
65 #include "file.h"
66 
67 typedef Elf64_Nhdr	GElf_Nhdr;
68 
69 /*
70  *	Misc
71  */
72 
73 #define	FBSZ		512
74 #define	MLIST_SZ	12
75 
76 /*
77  * The 0x8FCA0102 magic string was used in crash dumps generated by releases
78  * prior to Solaris 7.
79  */
80 #define	OLD_DUMP_MAGIC	0x8FCA0102
81 
82 #if defined(__sparc)
83 #define	NATIVE_ISA	"SPARC"
84 #define	OTHER_ISA	"Intel"
85 #else
86 #define	NATIVE_ISA	"Intel"
87 #define	OTHER_ISA	"SPARC"
88 #endif
89 
90 /* Assembly language comment char */
91 #ifdef pdp11
92 #define	ASCOMCHAR '/'
93 #else
94 #define	ASCOMCHAR '!'
95 #endif
96 
97 #pragma	align	16(fbuf)
98 static char	fbuf[FBSZ];
99 
100 /*
101  * Magic file variables
102  */
103 static intmax_t maxmagicoffset;
104 static intmax_t tmpmax;
105 static char	*magicbuf;
106 
107 static char	*dfile;
108 static char	*troff[] = {	/* new troff intermediate lang */
109 		"x", "T", "res", "init", "font", "202", "V0", "p1", 0};
110 
111 static char	*fort[] = {			/* FORTRAN */
112 		"function", "subroutine", "common", "dimension", "block",
113 		"integer", "real", "data", "double",
114 		"FUNCTION", "SUBROUTINE", "COMMON", "DIMENSION", "BLOCK",
115 		"INTEGER", "REAL", "DATA", "DOUBLE", 0};
116 
117 static char	*asc[] = {		/* Assembler Commands */
118 		"sys", "mov", "tst", "clr", "jmp", "cmp", "set", "inc",
119 		"dec", 0};
120 
121 static char	*c[] = {			/* C Language */
122 		"int", "char", "float", "double", "short", "long", "unsigned",
123 		"register", "static", "struct", "extern", 0};
124 
125 static char	*as[] = {	/* Assembler Pseudo Ops, prepended with '.' */
126 		"globl", "global", "ident", "file", "byte", "even",
127 		"text", "data", "bss", "comm", 0};
128 
129 /*
130  * The line and debug section names are used by the strip command.
131  * Any changes in the strip implementation need to be reflected here.
132  */
133 static char	*debug_sections[] = { /* Debug sections in a ELF file */
134 		".debug", ".stab", ".dwarf", ".line", NULL};
135 
136 
137 /* start for MB env */
138 static wchar_t wchar;
139 static int	length;
140 static int	IS_ascii;
141 static int	Max;
142 /* end for MB env */
143 static int	i;	/* global index into first 'fbsz' bytes of file */
144 static int	fbsz;
145 static int	ifd = -1;
146 static int	elffd = -1;
147 static int	tret;
148 static int	hflg;
149 static int	dflg;
150 static int	mflg;
151 static int	M_flg;
152 static int	iflg;
153 static struct stat64	mbuf;
154 
155 static char	**mlist1;	/* 1st ordered list of magic files */
156 static char	**mlist2;	/* 2nd ordered list of magic files */
157 static size_t	mlist1_sz;	/* number of ptrs allocated for mlist1 */
158 static size_t	mlist2_sz;	/* number of ptrs allocated for mlist2 */
159 static char	**mlist1p;	/* next entry in mlist1 */
160 static char	**mlist2p;	/* next entry in mlist2 */
161 
162 static ssize_t	mread;
163 
164 static void is_stripped(Elf *elf);
165 static Elf *is_elf_file(int elffd);
166 static void ar_coff_or_aout(int ifd);
167 static int type(char *file);
168 static int def_position_tests(void);
169 static void def_context_tests(void);
170 static int troffint(char *bp, int n);
171 static int lookup(char **tab);
172 static int ccom(void);
173 static int ascom(void);
174 static int sccs(void);
175 static int english(char *bp, int n);
176 static int old_core(Elf *elf, GElf_Ehdr *ehdr, int format);
177 static int core(Elf *elf, GElf_Ehdr *ehdr, int format);
178 static int shellscript(char buf[], struct stat64 *sb);
179 static int elf_check(Elf *elf);
180 static int get_door_target(char *, char *, size_t);
181 static int zipfile(char *, int);
182 static int is_crash_dump(const char *, int);
183 static void print_dumphdr(const int, const dumphdr_t *, uint32_t (*)(uint32_t),
184     const char *);
185 static uint32_t swap_uint32(uint32_t);
186 static uint32_t return_uint32(uint32_t);
187 static int is_in_list(char *[], char *);
188 static void usage(void);
189 static void default_magic(void);
190 static void add_to_mlist(char *, int);
191 static void fd_cleanup(void);
192 static int is_rtld_config(void);
193 
194 #ifdef XPG4
195 	/* SUSv3 requires a single <space> after the colon */
196 #define	prf(x)	(void) printf("%s: ", x);
197 #else	/* !XPG4 */
198 #define	prf(x)	(void) printf("%s:%s", x, (int)strlen(x) > 6 ? "\t" : "\t\t");
199 #endif	/* XPG4 */
200 
201 int
202 main(int argc, char **argv)
203 {
204 	char	*p;
205 	int	ch;
206 	FILE	*fl;
207 	int	cflg = 0;
208 	int	eflg = 0;
209 	int	fflg = 0;
210 	char	*ap = NULL;
211 	int	pathlen;
212 	char	**filep;
213 
214 	(void) setlocale(LC_ALL, "");
215 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
216 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
217 #endif
218 	(void) textdomain(TEXT_DOMAIN);
219 
220 	while ((ch = getopt(argc, argv, "M:cdf:him:")) != EOF) {
221 		switch (ch) {
222 
223 		case 'M':
224 			add_to_mlist(optarg, !dflg);
225 			M_flg++;
226 			break;
227 
228 		case 'c':
229 			cflg++;
230 			break;
231 
232 		case 'd':
233 			if (!dflg) {
234 				default_magic();
235 				add_to_mlist(dfile, 0);
236 				dflg++;
237 			}
238 			break;
239 
240 		case 'f':
241 			fflg++;
242 			if ((fl = fopen(optarg, "r")) == NULL) {
243 				(void) fprintf(stderr,
244 					gettext("cannot open %s\n"), optarg);
245 				usage();
246 			}
247 			pathlen = pathconf("/", _PC_PATH_MAX);
248 			if (pathlen == -1) {
249 				(void) fprintf(stderr,
250 				    gettext("pathconf: cannot determine "
251 					"maximum path length\n"));
252 				exit(1);
253 			}
254 			pathlen += 2; /* for null and newline in fgets */
255 			ap = malloc(pathlen * sizeof (char));
256 			if (ap == NULL) {
257 				perror("malloc");
258 				exit(1);
259 			}
260 			break;
261 
262 		case 'h':
263 			hflg++;
264 			break;
265 
266 		case 'i':
267 			iflg++;
268 			break;
269 
270 		case 'm':
271 			add_to_mlist(optarg, !dflg);
272 			mflg++;
273 			break;
274 
275 		case '?':
276 			eflg++;
277 			break;
278 		}
279 	}
280 	if (!cflg && !fflg && (eflg || optind == argc))
281 		usage();
282 	if (iflg && (dflg || mflg || M_flg)) {
283 		usage();
284 	}
285 	if (iflg && cflg) {
286 		usage();
287 	}
288 
289 	if (!dflg && !mflg && !M_flg && !iflg) {
290 	/* no -d, -m, nor -M option; also -i option doesn't need magic  */
291 		default_magic();
292 		if (f_mkmtab(dfile, cflg, 0) == -1) {
293 			exit(2);
294 		}
295 	}
296 
297 	else if (mflg && !M_flg && !dflg) {
298 	/* -m specified without -d nor -M */
299 
300 #ifdef XPG4	/* For SUSv3 only */
301 
302 		/*
303 		 * The default position-dependent magic file tests
304 		 * in /etc/magic will follow all the -m magic tests.
305 		 */
306 
307 		for (filep = mlist1; filep < mlist1p; filep++) {
308 			if (f_mkmtab(*filep, cflg, 1) == -1) {
309 				exit(2);
310 			}
311 		}
312 		default_magic();
313 		if (f_mkmtab(dfile, cflg, 0) == -1) {
314 			exit(2);
315 		}
316 #else	/* !XPG4 */
317 		/*
318 		 * Retain Solaris file behavior for -m before SUSv3,
319 		 * when the new -d and -M options are not specified.
320 		 * Use the -m file specified in place of the default
321 		 * /etc/magic file.  Solaris file will
322 		 * now allow more than one magic file to be specified
323 		 * with multiple -m options, for consistency with
324 		 * other behavior.
325 		 *
326 		 * Put the magic table(s) specified by -m into
327 		 * the second magic table instead of the first
328 		 * (as indicated by the last argument to f_mkmtab()),
329 		 * since they replace the /etc/magic tests and
330 		 * must be executed alongside the default
331 		 * position-sensitive tests.
332 		 */
333 
334 		for (filep = mlist1; filep < mlist1p; filep++) {
335 			if (f_mkmtab(*filep, cflg, 0) == -1) {
336 				exit(2);
337 			}
338 		}
339 #endif /* XPG4 */
340 	} else {
341 		/*
342 		 * For any other combination of -d, -m, and -M,
343 		 * use the magic files in command-line order.
344 		 * Store the entries from the two separate lists of magic
345 		 * files, if any, into two separate magic file tables.
346 		 * mlist1: magic tests executed before default magic tests
347 		 * mlist2: default magic tests and after
348 		 */
349 		for (filep = mlist1; filep && (filep < mlist1p); filep++) {
350 			if (f_mkmtab(*filep, cflg, 1) == -1) {
351 				exit(2);
352 			}
353 		}
354 		for (filep = mlist2; filep && (filep < mlist2p); filep++) {
355 			if (f_mkmtab(*filep, cflg, 0) == -1) {
356 				exit(2);
357 			}
358 		}
359 	}
360 
361 	/* Initialize the magic file variables; check both magic tables */
362 	tmpmax = f_getmaxoffset(1);
363 	maxmagicoffset = f_getmaxoffset(0);
364 	if (maxmagicoffset < tmpmax) {
365 		maxmagicoffset = tmpmax;
366 	}
367 	if (maxmagicoffset < (intmax_t)FBSZ)
368 		maxmagicoffset = (intmax_t)FBSZ;
369 	if ((magicbuf = (char *)malloc(maxmagicoffset)) == NULL) {
370 		(void) fprintf(stderr, gettext("malloc failed\n"));
371 		exit(2);
372 	}
373 
374 	if (cflg) {
375 		f_prtmtab();
376 		if (ferror(stdout) != 0) {
377 			(void) fprintf(stderr, gettext("file: error writing to "
378 			    "stdout\n"));
379 			exit(1);
380 		}
381 		if (fclose(stdout) != 0) {
382 			perror(gettext("file: fclose failed"));
383 			exit(1);
384 		}
385 		exit(0);
386 	}
387 	for (; fflg || optind < argc; optind += !fflg) {
388 		register int	l;
389 
390 		if (fflg) {
391 			if ((p = fgets(ap, pathlen, fl)) == NULL) {
392 				fflg = 0;
393 				optind--;
394 				continue;
395 			}
396 			l = strlen(p);
397 			if (l > 0)
398 				p[l - 1] = '\0';
399 		} else
400 			p = argv[optind];
401 		prf(p);				/* print "file_name:<tab>" */
402 
403 		if (type(p))
404 			tret = 1;
405 	}
406 	if (ap != NULL)
407 		free(ap);
408 	if (tret != 0) {
409 		exit(tret);
410 	}
411 	if (ferror(stdout) != 0) {
412 		(void) fprintf(stderr, gettext("file: error writing to "
413 		    "stdout\n"));
414 		exit(1);
415 	}
416 	if (fclose(stdout) != 0) {
417 		perror(gettext("file: fclose failed"));
418 		exit(1);
419 	}
420 	return (0);
421 }
422 
423 static int
424 type(char *file)
425 {
426 	int	cc;
427 	char	buf[BUFSIZ];
428 	int	(*statf)() = hflg ? lstat64 : stat64;
429 
430 	i = 0;		/* reset index to beginning of file */
431 	ifd = -1;
432 	if ((*statf)(file, &mbuf) < 0) {
433 		if (statf == lstat64 || lstat64(file, &mbuf) < 0) {
434 			(void) printf(gettext("cannot open: %s\n"),
435 			    strerror(errno));
436 			return (0);		/* POSIX.2 */
437 		}
438 	}
439 	switch (mbuf.st_mode & S_IFMT) {
440 	case S_IFREG:
441 		if (iflg) {
442 			(void) printf(gettext("regular file\n"));
443 			return (0);
444 		}
445 		break;
446 	case S_IFCHR:
447 		(void) printf(gettext("character"));
448 		goto spcl;
449 
450 	case S_IFDIR:
451 		(void) printf(gettext("directory\n"));
452 		return (0);
453 
454 	case S_IFIFO:
455 		(void) printf(gettext("fifo\n"));
456 		return (0);
457 
458 	case S_IFLNK:
459 		if ((cc = readlink(file, buf, BUFSIZ)) < 0) {
460 			(void) printf(gettext("readlink error: %s\n"),
461 				strerror(errno));
462 			return (1);
463 		}
464 		buf[cc] = '\0';
465 		(void) printf(gettext("symbolic link to %s\n"), buf);
466 		return (0);
467 
468 	case S_IFBLK:
469 		(void) printf(gettext("block"));
470 					/* major and minor, see sys/mkdev.h */
471 spcl:
472 		(void) printf(gettext(" special (%d/%d)\n"),
473 		    major(mbuf.st_rdev), minor(mbuf.st_rdev));
474 		return (0);
475 
476 	case S_IFSOCK:
477 		(void) printf("socket\n");
478 		/* FIXME, should open and try to getsockname. */
479 		return (0);
480 
481 	case S_IFDOOR:
482 		if (get_door_target(file, buf, sizeof (buf)) == 0)
483 			(void) printf(gettext("door to %s\n"), buf);
484 		else
485 			(void) printf(gettext("door\n"));
486 		return (0);
487 
488 	}
489 
490 	if (elf_version(EV_CURRENT) == EV_NONE) {
491 		(void) printf(gettext("libelf is out of date\n"));
492 		return (1);
493 	}
494 
495 	ifd = open64(file, O_RDONLY);
496 	if (ifd < 0) {
497 		(void) printf(gettext("cannot open: %s\n"), strerror(errno));
498 		return (0);			/* POSIX.2 */
499 	}
500 
501 	/* need another fd for elf, since we might want to read the file too */
502 	elffd = open64(file, O_RDONLY);
503 	if (elffd < 0) {
504 		(void) printf(gettext("cannot open: %s\n"), strerror(errno));
505 		(void) close(ifd);
506 		ifd = -1;
507 		return (0);			/* POSIX.2 */
508 	}
509 	if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) {
510 		(void) printf(gettext("cannot read: %s\n"), strerror(errno));
511 		(void) close(ifd);
512 		ifd = -1;
513 		return (0);			/* POSIX.2 */
514 	}
515 	if (fbsz == 0) {
516 		(void) printf(gettext("empty file\n"));
517 		fd_cleanup();
518 		return (0);
519 	}
520 
521 	/*
522 	 * First try user-specified position-dependent magic tests, if any,
523 	 * which need to execute before the default tests.
524 	 */
525 	if ((mread = pread(ifd, (void*)magicbuf, (size_t)maxmagicoffset,
526 			(off_t)0)) == -1) {
527 		(void) printf(gettext("cannot read: %s\n"), strerror(errno));
528 		fd_cleanup();
529 		return (0);
530 	}
531 
532 	/*
533 	 * ChecK against Magic Table entries.
534 	 * Check first magic table for magic tests to be applied
535 	 * before default tests.
536 	 * If no default tests are to be applied, all magic tests
537 	 * should occur in this magic table.
538 	 */
539 	switch (f_ckmtab(magicbuf, mread, 1)) {
540 		case -1:	/* Error */
541 			exit(2);
542 			break;
543 		case 0:		/* Not magic */
544 			break;
545 		default:	/* Switch is magic index */
546 			(void) putchar('\n');
547 			fd_cleanup();
548 			return (0);
549 			/* NOTREACHED */
550 			break;
551 	}
552 
553 	if (dflg || !M_flg) {
554 		/*
555 		 * default position-dependent tests,
556 		 * plus non-default magic tests, if any
557 		 */
558 		switch (def_position_tests()) {
559 			case -1:	/* error */
560 				fd_cleanup();
561 				return (1);
562 			case 1:	/* matching type found */
563 				fd_cleanup();
564 				return (0);
565 				/* NOTREACHED */
566 				break;
567 			case 0:		/* no matching type found */
568 				break;
569 		}
570 		/* default context-sensitive tests */
571 		def_context_tests();
572 	} else {
573 		/* no more tests to apply; no match was found */
574 		(void) printf(gettext("data\n"));
575 	}
576 	fd_cleanup();
577 	return (0);
578 }
579 
580 /*
581  * def_position_tests() - applies default position-sensitive tests,
582  *	looking for values in specific positions in the file.
583  *	These are followed by default (followed by possibly some
584  *	non-default) magic file tests.
585  *
586  *	All position-sensitive tests, default or otherwise, must
587  *	be applied before context-sensitive tests, to avoid
588  *	false context-sensitive matches.
589  *
590  * 	Returns -1 on error which should result in error (non-zero)
591  *	exit status for the file utility.
592  *	Returns 0 if no matching file type found.
593  *	Returns 1 if matching file type found.
594  */
595 
596 static int
597 def_position_tests(void)
598 {
599 	Elf	*elf;
600 
601 	if (sccs()) {	/* look for "1hddddd" where d is a digit */
602 		(void) printf("sccs \n");
603 		return (1);
604 	}
605 	if (fbuf[0] == '#' && fbuf[1] == '!' && shellscript(fbuf+2, &mbuf))
606 		return (1);
607 	if ((elf = is_elf_file(elffd)) != NULL) {
608 		(void) elf_check(elf);
609 		(void) elf_end(elf);
610 		(void) putchar('\n');
611 		return (1);
612 
613 
614 	/* LINTED: pointer cast may result in improper alignment */
615 	} else if (*(int *)fbuf == CORE_MAGIC) {
616 		/* LINTED: pointer cast may result in improper alignment */
617 		struct core *corep = (struct core *)fbuf;
618 
619 		(void) printf("a.out core file");
620 
621 		if (*(corep->c_cmdname) != '\0')
622 			(void) printf(" from '%s'", corep->c_cmdname);
623 		(void) putchar('\n');
624 		return (1);
625 	}
626 
627 	/*
628 	 * Runtime linker (ld.so.1) configuration file.
629 	 */
630 	if (is_rtld_config())
631 		return (1);
632 
633 	/*
634 	 * ZIP files, JAR files, and Java executables
635 	 */
636 	if (zipfile(fbuf, ifd))
637 		return (1);
638 
639 	if (is_crash_dump(fbuf, ifd))
640 		return (1);
641 
642 	/*
643 	 * ChecK against Magic Table entries.
644 	 * The magic entries checked here always start with default
645 	 * magic tests and may be followed by other, non-default magic
646 	 * tests.  If no default tests are to be executed, all the
647 	 * magic tests should have been in the first magic table.
648 	 */
649 	switch (f_ckmtab(magicbuf, mread, 0)) {
650 		case -1:	/* Error */
651 			exit(2);
652 			break;
653 		case 0:		/* Not magic */
654 			return (0);
655 			/* NOTREACHED */
656 			break;
657 		default:	/* Switch is magic index */
658 
659 			/*
660 			 * f_ckmtab recognizes file type,
661 			 * check if it is PostScript.
662 			 * if not, check if elf or a.out
663 			 */
664 			if (magicbuf[0] == '%' && magicbuf[1] == '!') {
665 				(void) putchar('\n');
666 			} else {
667 
668 				/*
669 				 * Check that the file is executable (dynamic
670 				 * objects must be executable to be exec'ed,
671 				 * shared objects need not be, but by convention
672 				 * should be executable).
673 				 *
674 				 * Note that we should already have processed
675 				 * the file if it was an ELF file.
676 				 */
677 				ar_coff_or_aout(elffd);
678 				(void) putchar('\n');
679 			}
680 			return (1);
681 			/* NOTREACHED */
682 			break;
683 	}
684 
685 	return (0);	/* file was not identified */
686 }
687 
688 /*
689  * def_context_tests() - default context-sensitive tests.
690  *	These are the last tests to be applied.
691  *	If no match is found, prints out "data".
692  */
693 
694 static void
695 def_context_tests(void)
696 {
697 	int	j;
698 	int	nl;
699 	char	ch;
700 	int	len;
701 
702 	if (ccom() == 0)
703 		goto notc;
704 	while (fbuf[i] == '#') {
705 		j = i;
706 		while (fbuf[i++] != '\n') {
707 			if (i - j > 255) {
708 				(void) printf(gettext("data\n"));
709 				return;
710 			}
711 			if (i >= fbsz)
712 				goto notc;
713 		}
714 		if (ccom() == 0)
715 			goto notc;
716 	}
717 check:
718 	if (lookup(c) == 1) {
719 		while ((ch = fbuf[i]) != ';' && ch != '{') {
720 			if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
721 				len = 1;
722 			i += len;
723 			if (i >= fbsz)
724 				goto notc;
725 		}
726 		(void) printf(gettext("c program text"));
727 		goto outa;
728 	}
729 	nl = 0;
730 	while (fbuf[i] != '(') {
731 		if (fbuf[i] <= 0)
732 			goto notas;
733 		if (fbuf[i] == ';') {
734 			i++;
735 			goto check;
736 		}
737 		if (fbuf[i++] == '\n')
738 			if (nl++ > 6)
739 				goto notc;
740 		if (i >= fbsz)
741 			goto notc;
742 	}
743 	while (fbuf[i] != ')') {
744 		if (fbuf[i++] == '\n')
745 			if (nl++ > 6)
746 				goto notc;
747 		if (i >= fbsz)
748 			goto notc;
749 	}
750 	while (fbuf[i] != '{') {
751 		if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
752 			len = 1;
753 		if (fbuf[i] == '\n')
754 			if (nl++ > 6)
755 				goto notc;
756 		i += len;
757 		if (i >= fbsz)
758 			goto notc;
759 	}
760 	(void) printf(gettext("c program text"));
761 	goto outa;
762 notc:
763 	i = 0;			/* reset to begining of file again */
764 	while (fbuf[i] == 'c' || fbuf[i] == 'C'|| fbuf[i] == '!' ||
765 	    fbuf[i] == '*' || fbuf[i] == '\n') {
766 		while (fbuf[i++] != '\n')
767 			if (i >= fbsz)
768 				goto notfort;
769 	}
770 	if (lookup(fort) == 1) {
771 		(void) printf(gettext("fortran program text"));
772 		goto outa;
773 	}
774 notfort:			/* looking for assembler program */
775 	i = 0;			/* reset to beginning of file again */
776 	if (ccom() == 0)	/* assembler programs may contain */
777 				/* c-style comments */
778 		goto notas;
779 	if (ascom() == 0)
780 		goto notas;
781 	j = i - 1;
782 	if (fbuf[i] == '.') {
783 		i++;
784 		if (lookup(as) == 1) {
785 			(void) printf(gettext("assembler program text"));
786 			goto outa;
787 		} else if (j != -1 && fbuf[j] == '\n' && isalpha(fbuf[j + 2])) {
788 			(void) printf(
789 			    gettext("[nt]roff, tbl, or eqn input text"));
790 			goto outa;
791 		}
792 	}
793 	while (lookup(asc) == 0) {
794 		if (ccom() == 0)
795 			goto notas;
796 		if (ascom() == 0)
797 			goto notas;
798 		while (fbuf[i] != '\n' && fbuf[i++] != ':') {
799 			if (i >= fbsz)
800 				goto notas;
801 		}
802 		while (fbuf[i] == '\n' || fbuf[i] == ' ' || fbuf[i] == '\t')
803 			if (i++ >= fbsz)
804 				goto notas;
805 		j = i - 1;
806 		if (fbuf[i] == '.') {
807 			i++;
808 			if (lookup(as) == 1) {
809 				(void) printf(
810 				    gettext("assembler program text"));
811 				goto outa;
812 			} else if (fbuf[j] == '\n' && isalpha(fbuf[j+2])) {
813 				(void) printf(
814 				    gettext("[nt]roff, tbl, or eqn input "
815 				    "text"));
816 				goto outa;
817 			}
818 		}
819 	}
820 	(void) printf(gettext("assembler program text"));
821 	goto outa;
822 notas:
823 	/* start modification for multibyte env */
824 	IS_ascii = 1;
825 	if (fbsz < FBSZ)
826 		Max = fbsz;
827 	else
828 		Max = FBSZ - MB_LEN_MAX; /* prevent cut of wchar read */
829 	/* end modification for multibyte env */
830 
831 	for (i = 0; i < Max; /* null */)
832 		if (fbuf[i] & 0200) {
833 			IS_ascii = 0;
834 			if (fbuf[0] == '\100' && fbuf[1] == '\357') {
835 				(void) printf(gettext("troff output\n"));
836 				return;
837 			}
838 		/* start modification for multibyte env */
839 			if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
840 			    <= 0 || !iswprint(wchar)) {
841 				(void) printf(gettext("data\n"));
842 				return;
843 			}
844 			i += length;
845 		}
846 		else
847 			i++;
848 	i = fbsz;
849 		/* end modification for multibyte env */
850 	if (mbuf.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH))
851 		(void) printf(gettext("commands text"));
852 	else if (troffint(fbuf, fbsz))
853 		(void) printf(gettext("troff intermediate output text"));
854 	else if (english(fbuf, fbsz))
855 		(void) printf(gettext("English text"));
856 	else if (IS_ascii)
857 		(void) printf(gettext("ascii text"));
858 	else
859 		(void) printf(gettext("text")); /* for multibyte env */
860 outa:
861 	/*
862 	 * This code is to make sure that no MB char is cut in half
863 	 * while still being used.
864 	 */
865 	fbsz = (fbsz < FBSZ ? fbsz : fbsz - MB_CUR_MAX + 1);
866 	while (i < fbsz) {
867 		if (isascii(fbuf[i])) {
868 			i++;
869 			continue;
870 		} else {
871 			if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
872 			    <= 0 || !iswprint(wchar)) {
873 				(void) printf(gettext(" with garbage\n"));
874 				return;
875 			}
876 			i = i + length;
877 		}
878 	}
879 	(void) printf("\n");
880 }
881 
882 static int
883 troffint(char *bp, int n)
884 {
885 	int k;
886 
887 	i = 0;
888 	for (k = 0; k < 6; k++) {
889 		if (lookup(troff) == 0)
890 			return (0);
891 		if (lookup(troff) == 0)
892 			return (0);
893 		while (i < n && bp[i] != '\n')
894 			i++;
895 		if (i++ >= n)
896 			return (0);
897 	}
898 	return (1);
899 }
900 
901 /*
902  * Determine if the passed descriptor describes an ELF file.
903  * If so, return the Elf handle.
904  */
905 static Elf *
906 is_elf_file(int elffd)
907 {
908 	Elf *elf;
909 
910 	elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
911 	switch (elf_kind(elf)) {
912 	case ELF_K_ELF:
913 		break;
914 	default:
915 		(void) elf_end(elf);
916 		elf = NULL;
917 		break;
918 	}
919 	return (elf);
920 }
921 
922 static void
923 ar_coff_or_aout(int elffd)
924 {
925 	Elf *elf;
926 
927 	/*
928 	 * Get the files elf descriptor and process it as an elf or
929 	 * a.out (4.x) file.
930 	 */
931 
932 	elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
933 	switch (elf_kind(elf)) {
934 		case ELF_K_AR :
935 			(void) printf(gettext(", not a dynamic executable "
936 			    "or shared object"));
937 			break;
938 		case ELF_K_COFF:
939 			(void) printf(gettext(", unsupported or unknown "
940 			    "file type"));
941 			break;
942 		default:
943 			/*
944 			 * This is either an unknown file or an aout format
945 			 * At this time, we don't print dynamic/stripped
946 			 * info. on a.out or non-Elf binaries.
947 			 */
948 			break;
949 	}
950 	(void) elf_end(elf);
951 }
952 
953 
954 static void
955 print_elf_type(Elf *elf, GElf_Ehdr *ehdr, int format)
956 {
957 	switch (ehdr->e_type) {
958 	case ET_NONE:
959 		(void) printf(" %s", gettext("unknown type"));
960 		break;
961 	case ET_REL:
962 		(void) printf(" %s", gettext("relocatable"));
963 		break;
964 	case ET_EXEC:
965 		(void) printf(" %s", gettext("executable"));
966 		break;
967 	case ET_DYN:
968 		(void) printf(" %s", gettext("dynamic lib"));
969 		break;
970 	case ET_CORE:
971 		if (old_core(elf, ehdr, format))
972 			(void) printf(" %s", gettext("pre-2.6 core file"));
973 		else
974 			(void) printf(" %s", gettext("core file"));
975 		break;
976 	default:
977 		break;
978 	}
979 }
980 
981 static void
982 print_elf_machine(int machine)
983 {
984 	/*
985 	 * This table must be kept in sync with the EM_ constants
986 	 * in /usr/include/sys/elf.h.
987 	 */
988 	static const char *mach_str[EM_NUM] = {
989 		"unknown machine",		/* 0 - EM_NONE */
990 		"WE32100",			/* 1 - EM_M32 */
991 		"SPARC",			/* 2 - EM_SPARC */
992 		"80386",			/* 3 - EM_386 */
993 		"M68000",			/* 4 - EM_68K */
994 		"M88000",			/* 5 - EM_88K */
995 		"80486",			/* 6 - EM_486 */
996 		"i860",				/* 7 - EM_860 */
997 		"MIPS RS3000 Big-Endian",	/* 8 - EM_MIPS */
998 		"S/370",			/* 9 - EM_S370 */
999 		"MIPS RS3000 Little-Endian",	/* 10 - EM_MIPS_RS3_LE */
1000 		"MIPS RS6000",			/* 11 - EM_RS6000 */
1001 		NULL,				/* 12 - EM_UNKNOWN12 */
1002 		NULL,				/* 13 - EM_UNKNOWN13 */
1003 		NULL,				/* 14 - EM_UNKNOWN14 */
1004 		"PA-RISC",			/* 15 - EM_PA_RISC */
1005 		"nCUBE",			/* 16 - EM_nCUBE */
1006 		"VPP500",			/* 17 - EM_VPP500 */
1007 		"SPARC32PLUS",			/* 18 - EM_SPARC32PLUS */
1008 		"i960",				/* 19 - EM_960 */
1009 		"PowerPC",			/* 20 - EM_PPC */
1010 		"PowerPC64",			/* 21 - EM_PPC64 */
1011 		NULL,				/* 22 - EM_UNKNOWN22 */
1012 		NULL,				/* 23 - EM_UNKNOWN23 */
1013 		NULL,				/* 24 - EM_UNKNOWN24 */
1014 		NULL,				/* 25 - EM_UNKNOWN25 */
1015 		NULL,				/* 26 - EM_UNKNOWN26 */
1016 		NULL,				/* 27 - EM_UNKNOWN27 */
1017 		NULL,				/* 28 - EM_UNKNOWN28 */
1018 		NULL,				/* 29 - EM_UNKNOWN29 */
1019 		NULL,				/* 30 - EM_UNKNOWN30 */
1020 		NULL,				/* 31 - EM_UNKNOWN31 */
1021 		NULL,				/* 32 - EM_UNKNOWN32 */
1022 		NULL,				/* 33 - EM_UNKNOWN33 */
1023 		NULL,				/* 34 - EM_UNKNOWN34 */
1024 		NULL,				/* 35 - EM_UNKNOWN35 */
1025 		"V800",				/* 36 - EM_V800 */
1026 		"FR20",				/* 37 - EM_FR20 */
1027 		"RH32",				/* 38 - EM_RH32 */
1028 		"RCE",				/* 39 - EM_RCE */
1029 		"ARM",				/* 40 - EM_ARM */
1030 		"Alpha",			/* 41 - EM_ALPHA */
1031 		"S/390",			/* 42 - EM_SH */
1032 		"SPARCV9",			/* 43 - EM_SPARCV9 */
1033 		"Tricore",			/* 44 - EM_TRICORE */
1034 		"ARC",				/* 45 - EM_ARC */
1035 		"H8/300",			/* 46 - EM_H8_300 */
1036 		"H8/300H",			/* 47 - EM_H8_300H */
1037 		"H8S",				/* 48 - EM_H8S */
1038 		"H8/500",			/* 49 - EM_H8_500 */
1039 		"IA64",				/* 50 - EM_IA_64 */
1040 		"MIPS-X",			/* 51 - EM_MIPS_X */
1041 		"Coldfire",			/* 52 - EM_COLDFIRE */
1042 		"M68HC12",			/* 53 - EM_68HC12 */
1043 		"MMA",				/* 54 - EM_MMA */
1044 		"PCP",				/* 55 - EM_PCP */
1045 		"nCPU",				/* 56 - EM_NCPU */
1046 		"NDR1",				/* 57 - EM_NDR1 */
1047 		"Starcore",			/* 58 - EM_STARCORE */
1048 		"ME16",				/* 59 - EM_ME16 */
1049 		"ST100",			/* 60 - EM_ST100 */
1050 		"TINYJ",			/* 61 - EM_TINYJ */
1051 		"AMD64",			/* 62 - EM_AMD64 */
1052 		"PDSP",				/* 63 - EM_PDSP */
1053 		NULL,				/* 64 - EM_UNKNOWN64 */
1054 		NULL,				/* 65 - EM_UNKNOWN65 */
1055 		"FX66",				/* 66 - EM_FX66 */
1056 		"ST9 PLUS",			/* 67 - EM_ST9PLUS */
1057 		"ST7",				/* 68 - EM_ST7 */
1058 		"68HC16",			/* 69 - EM_68HC16 */
1059 		"68HC11",			/* 70 - EM_68HC11 */
1060 		"68H08",			/* 71 - EM_68HC08 */
1061 		"68HC05",			/* 72 - EM_68HC05 */
1062 		"SVX",				/* 73 - EM_SVX */
1063 		"ST19",				/* 74 - EM_ST19 */
1064 		"VAX",				/* 75 - EM_VAX */
1065 		"CRIS",				/* 76 - EM_CRIS */
1066 		"Javelin",			/* 77 - EM_JAVELIN */
1067 		"Firepath",			/* 78 - EM_FIREPATH */
1068 		"ZSP",				/* 79 - EM_ZSP */
1069 		"MMIX",				/* 80 - EM_MMIX */
1070 		"HUANY",			/* 81 - EM_HUANY */
1071 		"Prism",			/* 82 - EM_PRISM */
1072 		"AVR",				/* 83 - EM_AVR */
1073 		"FR30",				/* 84 - EM_FR30 */
1074 		"D10V",				/* 85 - EM_D10V */
1075 		"D30V",				/* 86 - EM_D30V */
1076 		"V850",				/* 87 - EM_V850 */
1077 		"M32R",				/* 88 - EM_M32R */
1078 		"MN10300",			/* 89 - EM_MN10300 */
1079 		"MN10200",			/* 90 - EM_MN10200 */
1080 		"picoJava",			/* 91 - EM_PJ */
1081 		"OpenRISC",			/* 92 - EM_OPENRISC */
1082 		"Tangent-A5",			/* 93 - EM_ARC_A5 */
1083 		"Xtensa"			/* 94 - EM_XTENSA */
1084 	};
1085 	/* If new machine is added, refuse to compile until we're updated */
1086 #if EM_NUM != 95
1087 #error "Number of known ELF machine constants has changed"
1088 #endif
1089 
1090 	const char *str;
1091 
1092 	if ((machine < EM_NONE) || (machine >= EM_NUM))
1093 		machine = EM_NONE;
1094 
1095 	str = mach_str[machine];
1096 	if (str)
1097 		(void) printf(" %s", str);
1098 }
1099 
1100 static void
1101 print_elf_datatype(int datatype)
1102 {
1103 	switch (datatype) {
1104 	case ELFDATA2LSB:
1105 		(void) printf(" LSB");
1106 		break;
1107 	case ELFDATA2MSB:
1108 		(void) printf(" MSB");
1109 		break;
1110 	default:
1111 		break;
1112 	}
1113 }
1114 
1115 static void
1116 print_elf_class(int class)
1117 {
1118 	switch (class) {
1119 	case ELFCLASS32:
1120 		(void) printf(" %s", gettext("32-bit"));
1121 		break;
1122 	case ELFCLASS64:
1123 		(void) printf(" %s", gettext("64-bit"));
1124 		break;
1125 	default:
1126 		break;
1127 	}
1128 }
1129 
1130 static void
1131 print_elf_flags(int machine, unsigned int flags)
1132 {
1133 	switch (machine) {
1134 	case EM_SPARCV9:
1135 		if (flags & EF_SPARC_EXT_MASK) {
1136 			if (flags & EF_SPARC_SUN_US3) {
1137 				(void) printf("%s", gettext(
1138 				    ", UltraSPARC3 Extensions Required"));
1139 			} else if (flags & EF_SPARC_SUN_US1) {
1140 				(void) printf("%s", gettext(
1141 				    ", UltraSPARC1 Extensions Required"));
1142 			}
1143 			if (flags & EF_SPARC_HAL_R1)
1144 				(void) printf("%s", gettext(
1145 				    ", HaL R1 Extensions Required"));
1146 		}
1147 		break;
1148 	case EM_SPARC32PLUS:
1149 		if (flags & EF_SPARC_32PLUS)
1150 			(void) printf("%s", gettext(", V8+ Required"));
1151 		if (flags & EF_SPARC_SUN_US3) {
1152 			(void) printf("%s",
1153 				gettext(", UltraSPARC3 Extensions Required"));
1154 		} else if (flags & EF_SPARC_SUN_US1) {
1155 			(void) printf("%s",
1156 				gettext(", UltraSPARC1 Extensions Required"));
1157 		}
1158 		if (flags & EF_SPARC_HAL_R1)
1159 			(void) printf("%s",
1160 				gettext(", HaL R1 Extensions Required"));
1161 		break;
1162 	default:
1163 		break;
1164 	}
1165 }
1166 
1167 static int
1168 print_cap(Elf *elf, GElf_Ehdr *ehdr)
1169 {
1170 	Elf_Scn	*scn = 0;
1171 
1172 	/*
1173 	 * Traverse the files sections to see if any software/hardware
1174 	 * capabilities are available.
1175 	 */
1176 	while ((scn = elf_nextscn(elf, scn)) != 0) {
1177 		GElf_Word	ndx, capn;
1178 		GElf_Shdr	shdr;
1179 		Elf_Data	*data;
1180 
1181 		if (gelf_getshdr(scn, &shdr) == 0) {
1182 			(void) fprintf(stderr,
1183 			    gettext("can't read ELF section header\n"));
1184 			return (1);
1185 		}
1186 		if (shdr.sh_type != SHT_SUNW_cap)
1187 			continue;
1188 
1189 		/*
1190 		 * Get the data associated with the .cap section.
1191 		 */
1192 		if ((data = elf_getdata(scn, 0)) == 0) {
1193 			(void) fprintf(stderr,
1194 				gettext("can't read ELF section data\n"));
1195 			return (1);
1196 		}
1197 
1198 		capn = (GElf_Word)(shdr.sh_size / shdr.sh_entsize);
1199 		for (ndx = 0; ndx < capn; ndx++) {
1200 			char		str[100];
1201 			GElf_Cap	cap;
1202 
1203 			if (gelf_getcap(data, ndx, &cap) == NULL) {
1204 				(void) fprintf(stderr,
1205 				    gettext("can't read capabilities data\n"));
1206 				return (1);
1207 			}
1208 			if (cap.c_tag != CA_SUNW_NULL) {
1209 				(void) cap_val2str(cap.c_tag, cap.c_un.c_val,
1210 				    str, sizeof (str), 0, ehdr->e_machine);
1211 				(void) printf(" [%s]", str);
1212 			}
1213 		}
1214 	}
1215 	return (0);
1216 }
1217 
1218 static int
1219 elf_check(Elf *elf)
1220 {
1221 	GElf_Ehdr	ehdr;
1222 	GElf_Phdr	phdr;
1223 	int		dynamic, cnt;
1224 	char	*ident;
1225 	size_t	size;
1226 
1227 	/*
1228 	 * verify information in file header
1229 	 */
1230 	if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)0) {
1231 		(void) fprintf(stderr, gettext("can't read ELF header\n"));
1232 		return (1);
1233 	}
1234 	ident = elf_getident(elf, &size);
1235 	(void) printf("%s", gettext("ELF"));
1236 	print_elf_class(ident[EI_CLASS]);
1237 	print_elf_datatype(ident[EI_DATA]);
1238 	print_elf_type(elf, &ehdr, ident[EI_DATA]);
1239 	print_elf_machine(ehdr.e_machine);
1240 	if (ehdr.e_version == 1)
1241 		(void) printf(" %s %d",
1242 		    gettext("Version"), (int)ehdr.e_version);
1243 	print_elf_flags(ehdr.e_machine, ehdr.e_flags);
1244 
1245 	if (core(elf, &ehdr, ident[EI_DATA]))	/* check for core file */
1246 		return (0);
1247 
1248 	if (print_cap(elf, &ehdr))
1249 		return (1);
1250 
1251 	/*
1252 	 * check type
1253 	 */
1254 	if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN))
1255 		return (1);
1256 
1257 	/*
1258 	 * read program header and check for dynamic section
1259 	 */
1260 	if (ehdr.e_phnum == 0) {
1261 		(void) fprintf(stderr, gettext("can't read program header\n"));
1262 		return (1);
1263 	}
1264 
1265 	for (dynamic = 0, cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) {
1266 		if (gelf_getphdr(elf, cnt, &phdr) == NULL) {
1267 			(void) fprintf(stderr,
1268 				gettext("can't read program header\n"));
1269 			return (1);
1270 		}
1271 		if (phdr.p_type == PT_DYNAMIC) {
1272 			dynamic = 1;
1273 			break;
1274 		}
1275 	}
1276 	if (dynamic)
1277 		(void) printf(gettext(", dynamically linked"));
1278 	else
1279 		(void) printf(gettext(", statically linked"));
1280 
1281 	is_stripped(elf);
1282 	return (0);
1283 }
1284 
1285 /*
1286  * is_stripped prints information on whether the executable has
1287  * been stripped.
1288  */
1289 static void
1290 is_stripped(Elf *elf)
1291 {
1292 	GElf_Shdr	shdr;
1293 	GElf_Ehdr	ehdr;
1294 	Elf_Scn		*scn, *nextscn;
1295 	char		*section_name;
1296 	int		symtab = 0;
1297 	int		debuginfo = 0;
1298 
1299 
1300 	if (gelf_getehdr(elf, &ehdr) == NULL) {
1301 		return;
1302 	}
1303 
1304 	/*
1305 	 * Definition time:
1306 	 *	- "not stripped" means that an executable file
1307 	 *	contains a Symbol Table (.symtab)
1308 	 *	- "stripped" means that an executable file
1309 	 *	does not contain a Symbol Table.
1310 	 * When strip -l or strip -x is run, it strips the
1311 	 * debugging information (.line section name (strip -l),
1312 	 * .line, .debug*, .stabs*, .dwarf* section names
1313 	 * and SHT_SUNW_DEBUGSTR and SHT_SUNW_DEBUG
1314 	 * section types (strip -x), however the Symbol
1315 	 * Table will still be present.
1316 	 * Therefore, if
1317 	 *	- No Symbol Table present, then report
1318 	 *		"stripped"
1319 	 *	- Symbol Table present with debugging
1320 	 *	information (line number or debug section names,
1321 	 *	or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
1322 	 *	types) then report:
1323 	 *		"not stripped"
1324 	 *	- Symbol Table present with no debugging
1325 	 *	information (line number or debug section names,
1326 	 *	or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
1327 	 *	types) then report:
1328 	 *		"not stripped, no debugging information
1329 	 *		available"
1330 	 */
1331 	scn = NULL;
1332 	while ((nextscn = elf_nextscn(elf, scn)) != NULL) {
1333 		if (symtab && debuginfo) {
1334 			break;
1335 		}
1336 
1337 		scn = nextscn;
1338 		if (gelf_getshdr(scn, &shdr) == NULL) {
1339 			continue;
1340 		}
1341 
1342 		if (!symtab && (shdr.sh_type == SHT_SYMTAB)) {
1343 			symtab++;
1344 			continue;
1345 		}
1346 
1347 		if (!debuginfo &&
1348 		    ((shdr.sh_type == SHT_SUNW_DEBUG) ||
1349 		    (shdr.sh_type == SHT_SUNW_DEBUGSTR) ||
1350 		    (((section_name = elf_strptr(elf, ehdr.e_shstrndx,
1351 		    (size_t)shdr.sh_name)) != NULL) &&
1352 		    (is_in_list(debug_sections, section_name))))) {
1353 			debuginfo++;
1354 		}
1355 	}
1356 
1357 	/*
1358 	 * Now that we've scanned all sections, print out the appropriate
1359 	 * diagnostic.
1360 	 */
1361 	if (symtab) {
1362 		(void) printf(gettext(", not stripped"));
1363 		if (!debuginfo) {
1364 			(void) printf(gettext(
1365 			    ", no debugging information available"));
1366 		}
1367 	} else {
1368 		(void) printf(gettext(", stripped"));
1369 	}
1370 }
1371 
1372 /*
1373  * is_rtld_config - If file is a runtime linker config file, prints
1374  * the description and returns True (1). Otherwise, silently returns
1375  * False (0).
1376  */
1377 int
1378 is_rtld_config(void)
1379 {
1380 	Rtc_id *id;
1381 
1382 	if ((fbsz >= sizeof (*id)) && RTC_ID_TEST(fbuf)) {
1383 		(void) printf(gettext("Runtime Linking Configuration"));
1384 		id = (Rtc_id *) fbuf;
1385 		print_elf_class(id->id_class);
1386 		print_elf_datatype(id->id_data);
1387 		print_elf_machine(id->id_machine);
1388 		(void) printf("\n");
1389 		return (1);
1390 	}
1391 
1392 	return (0);
1393 }
1394 
1395 /*
1396  * lookup -
1397  * Attempts to match one of the strings from a list, 'tab',
1398  * with what is in the file, starting at the current index position 'i'.
1399  * Looks past any initial whitespace and expects whitespace or other
1400  * delimiting characters to follow the matched string.
1401  * A match identifies the file as being 'assembler', 'fortran', 'c', etc.
1402  * Returns 1 for a successful match, 0 otherwise.
1403  */
1404 static int
1405 lookup(char **tab)
1406 {
1407 	register char	r;
1408 	register int	k, j, l;
1409 
1410 	while (fbuf[i] == ' ' || fbuf[i] == '\t' || fbuf[i] == '\n')
1411 		i++;
1412 	for (j = 0; tab[j] != 0; j++) {
1413 		l = 0;
1414 		for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++);
1415 		if (r == '\0')
1416 			if (fbuf[k] == ' ' || fbuf[k] == '\n' ||
1417 			    fbuf[k] == '\t' || fbuf[k] == '{' ||
1418 			    fbuf[k] == '/') {
1419 				i = k;
1420 				return (1);
1421 			}
1422 	}
1423 	return (0);
1424 }
1425 
1426 /*
1427  * ccom -
1428  * Increments the current index 'i' into the file buffer 'fbuf' past any
1429  * whitespace lines and C-style comments found, starting at the current
1430  * position of 'i'.  Returns 1 as long as we don't increment i past the
1431  * size of fbuf (fbsz).  Otherwise, returns 0.
1432  */
1433 
1434 static int
1435 ccom(void)
1436 {
1437 	register char	cc;
1438 	int		len;
1439 
1440 	while ((cc = fbuf[i]) == ' ' || cc == '\t' || cc == '\n')
1441 		if (i++ >= fbsz)
1442 			return (0);
1443 	if (fbuf[i] == '/' && fbuf[i+1] == '*') {
1444 		i += 2;
1445 		while (fbuf[i] != '*' || fbuf[i+1] != '/') {
1446 			if (fbuf[i] == '\\')
1447 				i++;
1448 			if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
1449 				len = 1;
1450 			i += len;
1451 			if (i >= fbsz)
1452 				return (0);
1453 		}
1454 		if ((i += 2) >= fbsz)
1455 			return (0);
1456 	}
1457 	if (fbuf[i] == '\n')
1458 		if (ccom() == 0)
1459 			return (0);
1460 	return (1);
1461 }
1462 
1463 /*
1464  * ascom -
1465  * Increments the current index 'i' into the file buffer 'fbuf' past
1466  * consecutive assembler program comment lines starting with ASCOMCHAR,
1467  * starting at the current position of 'i'.
1468  * Returns 1 as long as we don't increment i past the
1469  * size of fbuf (fbsz).  Otherwise returns 0.
1470  */
1471 
1472 static int
1473 ascom(void)
1474 {
1475 	while (fbuf[i] == ASCOMCHAR) {
1476 		i++;
1477 		while (fbuf[i++] != '\n')
1478 			if (i >= fbsz)
1479 				return (0);
1480 		while (fbuf[i] == '\n')
1481 			if (i++ >= fbsz)
1482 				return (0);
1483 	}
1484 	return (1);
1485 }
1486 
1487 static int
1488 sccs(void)
1489 {				/* look for "1hddddd" where d is a digit */
1490 	register int j;
1491 
1492 	if (fbuf[0] == 1 && fbuf[1] == 'h') {
1493 		for (j = 2; j <= 6; j++) {
1494 			if (isdigit(fbuf[j]))
1495 				continue;
1496 			else
1497 				return (0);
1498 		}
1499 	} else {
1500 		return (0);
1501 	}
1502 	return (1);
1503 }
1504 
1505 static int
1506 english(char *bp, int n)
1507 {
1508 #define	NASC 128		/* number of ascii char ?? */
1509 	register int	j, vow, freq, rare, len;
1510 	register int	badpun = 0, punct = 0;
1511 	int	ct[NASC];
1512 
1513 	if (n < 50)
1514 		return (0); /* no point in statistics on squibs */
1515 	for (j = 0; j < NASC; j++)
1516 		ct[j] = 0;
1517 	for (j = 0; j < n; j += len) {
1518 		if ((unsigned char)bp[j] < NASC)
1519 			ct[bp[j]|040]++;
1520 		switch (bp[j]) {
1521 		case '.':
1522 		case ',':
1523 		case ')':
1524 		case '%':
1525 		case ';':
1526 		case ':':
1527 		case '?':
1528 			punct++;
1529 			if (j < n-1 && bp[j+1] != ' ' && bp[j+1] != '\n')
1530 				badpun++;
1531 		}
1532 		if ((len = mblen(&bp[j], MB_CUR_MAX)) <= 0)
1533 			len = 1;
1534 	}
1535 	if (badpun*5 > punct)
1536 		return (0);
1537 	vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u'];
1538 	freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n'];
1539 	rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z'];
1540 	if (2*ct[';'] > ct['e'])
1541 		return (0);
1542 	if ((ct['>'] + ct['<'] + ct['/']) > ct['e'])
1543 		return (0);	/* shell file test */
1544 	return (vow * 5 >= n - ct[' '] && freq >= 10 * rare);
1545 }
1546 
1547 /*
1548  * Convert a word from an elf file to native format.
1549  * This is needed because there's no elf routine to
1550  * get and decode a Note section header.
1551  */
1552 static void
1553 convert_gelf_word(Elf *elf, GElf_Word *data, int version, int format)
1554 {
1555 	Elf_Data src, dst;
1556 
1557 	dst.d_buf = data;
1558 	dst.d_version = version;
1559 	dst.d_size = sizeof (GElf_Word);
1560 	dst.d_type = ELF_T_WORD;
1561 	src.d_buf = data;
1562 	src.d_version = version;
1563 	src.d_size = sizeof (GElf_Word);
1564 	src.d_type = ELF_T_WORD;
1565 	(void) gelf_xlatetom(elf, &dst, &src, format);
1566 }
1567 
1568 static void
1569 convert_gelf_nhdr(Elf *elf, GElf_Nhdr *nhdr, GElf_Word version, int format)
1570 {
1571 	convert_gelf_word(elf, &nhdr->n_namesz, version, format);
1572 	convert_gelf_word(elf, &nhdr->n_descsz, version, format);
1573 	convert_gelf_word(elf, &nhdr->n_type, version, format);
1574 }
1575 
1576 /*
1577  * Return true if it is an old (pre-restructured /proc) core file.
1578  */
1579 static int
1580 old_core(Elf *elf, GElf_Ehdr *ehdr, int format)
1581 {
1582 	register int inx;
1583 	GElf_Phdr phdr;
1584 	GElf_Phdr nphdr;
1585 	GElf_Nhdr nhdr;
1586 	off_t offset;
1587 
1588 	if (ehdr->e_type != ET_CORE)
1589 		return (0);
1590 	for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
1591 		if (gelf_getphdr(elf, inx, &phdr) == NULL) {
1592 			return (0);
1593 		}
1594 		if (phdr.p_type == PT_NOTE) {
1595 			/*
1596 			 * If the next segment is also a note, use it instead.
1597 			 */
1598 			if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
1599 				return (0);
1600 			}
1601 			if (nphdr.p_type == PT_NOTE)
1602 				phdr = nphdr;
1603 			offset = (off_t)phdr.p_offset;
1604 			(void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
1605 			convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
1606 			/*
1607 			 * Old core files have type NT_PRPSINFO.
1608 			 */
1609 			if (nhdr.n_type == NT_PRPSINFO)
1610 				return (1);
1611 			return (0);
1612 		}
1613 	}
1614 	return (0);
1615 }
1616 
1617 /*
1618  * If it's a core file, print out the name of the file that dumped core.
1619  */
1620 static int
1621 core(Elf *elf, GElf_Ehdr *ehdr, int format)
1622 {
1623 	register int inx;
1624 	char *psinfo;
1625 	GElf_Phdr phdr;
1626 	GElf_Phdr nphdr;
1627 	GElf_Nhdr nhdr;
1628 	off_t offset;
1629 
1630 	if (ehdr->e_type != ET_CORE)
1631 		return (0);
1632 	for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
1633 		if (gelf_getphdr(elf, inx, &phdr) == NULL) {
1634 			(void) fprintf(stderr,
1635 				gettext("can't read program header\n"));
1636 			return (0);
1637 		}
1638 		if (phdr.p_type == PT_NOTE) {
1639 			char *fname;
1640 			size_t size;
1641 			/*
1642 			 * If the next segment is also a note, use it instead.
1643 			 */
1644 			if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
1645 				(void) fprintf(stderr,
1646 				    gettext("can't read program header\n"));
1647 				return (0);
1648 			}
1649 			if (nphdr.p_type == PT_NOTE)
1650 				phdr = nphdr;
1651 			offset = (off_t)phdr.p_offset;
1652 			(void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
1653 			convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
1654 			/*
1655 			 * Note: the ABI states that n_namesz must
1656 			 * be rounded up to a 4 byte boundary.
1657 			 */
1658 			offset += sizeof (GElf_Nhdr) +
1659 			    ((nhdr.n_namesz + 0x03) & ~0x3);
1660 			size = nhdr.n_descsz;
1661 			psinfo = malloc(size);
1662 			(void) pread(ifd, psinfo, size, offset);
1663 			/*
1664 			 * We want to print the string contained
1665 			 * in psinfo->pr_fname[], where 'psinfo'
1666 			 * is either an old NT_PRPSINFO structure
1667 			 * or a new NT_PSINFO structure.
1668 			 *
1669 			 * Old core files have only type NT_PRPSINFO.
1670 			 * New core files have type NT_PSINFO.
1671 			 *
1672 			 * These structures are also different by
1673 			 * virtue of being contained in a core file
1674 			 * of either 32-bit or 64-bit type.
1675 			 *
1676 			 * To further complicate matters, we ourself
1677 			 * might be compiled either 32-bit or 64-bit.
1678 			 *
1679 			 * For these reason, we just *know* the offsets of
1680 			 * pr_fname[] into the four different structures
1681 			 * here, regardless of how we are compiled.
1682 			 */
1683 			if (gelf_getclass(elf) == ELFCLASS32) {
1684 				/* 32-bit core file, 32-bit structures */
1685 				if (nhdr.n_type == NT_PSINFO)
1686 					fname = psinfo + 88;
1687 				else	/* old: NT_PRPSINFO */
1688 					fname = psinfo + 84;
1689 			} else if (gelf_getclass(elf) == ELFCLASS64) {
1690 				/* 64-bit core file, 64-bit structures */
1691 				if (nhdr.n_type == NT_PSINFO)
1692 					fname = psinfo + 136;
1693 				else	/* old: NT_PRPSINFO */
1694 					fname = psinfo + 120;
1695 			} else {
1696 				free(psinfo);
1697 				break;
1698 			}
1699 			(void) printf(gettext(", from '%s'"), fname);
1700 			free(psinfo);
1701 			break;
1702 		}
1703 	}
1704 	return (1);
1705 }
1706 
1707 static int
1708 shellscript(char buf[], struct stat64 *sb)
1709 {
1710 	char *tp, *cp, *xp, *up, *gp;
1711 
1712 	cp = strchr(buf, '\n');
1713 	if (cp == NULL || cp - fbuf > fbsz)
1714 		return (0);
1715 	for (tp = buf; tp != cp && isspace((unsigned char)*tp); tp++)
1716 		if (!isascii(*tp))
1717 			return (0);
1718 	for (xp = tp; tp != cp && !isspace((unsigned char)*tp); tp++)
1719 		if (!isascii(*tp))
1720 			return (0);
1721 	if (tp == xp)
1722 		return (0);
1723 	if (sb->st_mode & S_ISUID)
1724 		up = gettext("set-uid ");
1725 	else
1726 		up = "";
1727 
1728 	if (sb->st_mode & S_ISGID)
1729 		gp = gettext("set-gid ");
1730 	else
1731 		gp = "";
1732 
1733 	if (strncmp(xp, "/bin/sh", tp - xp) == 0)
1734 		xp = gettext("shell");
1735 	else if (strncmp(xp, "/bin/csh", tp - xp) == 0)
1736 		xp = gettext("c-shell");
1737 	else if (strncmp(xp, "/usr/sbin/dtrace", tp - xp) == 0)
1738 		xp = gettext("DTrace");
1739 	else
1740 		*tp = '\0';
1741 	/*
1742 	 * TRANSLATION_NOTE
1743 	 * This message is printed by file command for shell scripts.
1744 	 * The first %s is for the translation for "set-uid " (if the script
1745 	 *   has the set-uid bit set), or is for an empty string (if the
1746 	 *   script does not have the set-uid bit set).
1747 	 * Similarly, the second %s is for the translation for "set-gid ",
1748 	 *   or is for an empty string.
1749 	 * The third %s is for the translation for either: "shell", "c-shell",
1750 	 *   or "DTrace", or is for the pathname of the program the script
1751 	 *   executes.
1752 	 */
1753 	(void) printf(gettext("%s%sexecutable %s script\n"), up, gp, xp);
1754 	return (1);
1755 }
1756 
1757 static int
1758 get_door_target(char *file, char *buf, size_t bufsize)
1759 {
1760 	int fd;
1761 	door_info_t di;
1762 	psinfo_t psinfo;
1763 
1764 	if ((fd = open64(file, O_RDONLY)) < 0 ||
1765 	    door_info(fd, &di) != 0) {
1766 		if (fd >= 0)
1767 			(void) close(fd);
1768 		return (-1);
1769 	}
1770 	(void) close(fd);
1771 
1772 	(void) sprintf(buf, "/proc/%ld/psinfo", di.di_target);
1773 	if ((fd = open64(buf, O_RDONLY)) < 0 ||
1774 	    read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1775 		if (fd >= 0)
1776 			(void) close(fd);
1777 		return (-1);
1778 	}
1779 	(void) close(fd);
1780 
1781 	(void) snprintf(buf, bufsize, "%s[%ld]", psinfo.pr_fname, di.di_target);
1782 	return (0);
1783 }
1784 
1785 /*
1786  * ZIP file header information
1787  */
1788 #define	SIGSIZ		4
1789 #define	LOCSIG		"PK\003\004"
1790 #define	LOCHDRSIZ	30
1791 
1792 #define	CH(b, n)	(((unsigned char *)(b))[n])
1793 #define	SH(b, n)	(CH(b, n) | (CH(b, n+1) << 8))
1794 #define	LG(b, n)	(SH(b, n) | (SH(b, n+2) << 16))
1795 
1796 #define	LOCNAM(b)	(SH(b, 26))	/* filename size */
1797 #define	LOCEXT(b)	(SH(b, 28))	/* extra field size */
1798 
1799 #define	XFHSIZ		4		/* header id, data size */
1800 #define	XFHID(b)	(SH(b, 0))	/* extract field header id */
1801 #define	XFDATASIZ(b)	(SH(b, 2))	/* extract field data size */
1802 #define	XFJAVASIG	0xcafe		/* java executables */
1803 
1804 static int
1805 zipfile(char *fbuf, int fd)
1806 {
1807 	off_t xoff, xoff_end;
1808 
1809 	if (strncmp(fbuf, LOCSIG, SIGSIZ) != 0)
1810 		return (0);
1811 
1812 	xoff = LOCHDRSIZ + LOCNAM(fbuf);
1813 	xoff_end = xoff + LOCEXT(fbuf);
1814 
1815 	while (xoff < xoff_end) {
1816 		char xfhdr[XFHSIZ];
1817 
1818 		if (pread(fd, xfhdr, XFHSIZ, xoff) != XFHSIZ)
1819 			break;
1820 
1821 		if (XFHID(xfhdr) == XFJAVASIG) {
1822 			(void) printf("%s\n", gettext("java program"));
1823 			return (1);
1824 		}
1825 		xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr);
1826 	}
1827 
1828 	/*
1829 	 * We could just print "ZIP archive" here.
1830 	 *
1831 	 * However, customers may be using their own entries in
1832 	 * /etc/magic to distinguish one kind of ZIP file from another, so
1833 	 * let's defer the printing of "ZIP archive" to there.
1834 	 */
1835 	return (0);
1836 }
1837 
1838 static int
1839 is_crash_dump(const char *buf, int fd)
1840 {
1841 	/* LINTED: pointer cast may result in improper alignment */
1842 	const dumphdr_t *dhp = (const dumphdr_t *)buf;
1843 
1844 	/*
1845 	 * The current DUMP_MAGIC string covers Solaris 7 and later releases.
1846 	 * The utsname struct is only present in dumphdr_t's with dump_version
1847 	 * greater than or equal to 9.
1848 	 */
1849 	if (dhp->dump_magic == DUMP_MAGIC) {
1850 		print_dumphdr(fd, dhp, return_uint32, NATIVE_ISA);
1851 
1852 	} else if (dhp->dump_magic == swap_uint32(DUMP_MAGIC)) {
1853 		print_dumphdr(fd, dhp, swap_uint32, OTHER_ISA);
1854 
1855 	} else if (dhp->dump_magic == OLD_DUMP_MAGIC ||
1856 	    dhp->dump_magic == swap_uint32(OLD_DUMP_MAGIC)) {
1857 		char *isa = (dhp->dump_magic == OLD_DUMP_MAGIC ?
1858 		    NATIVE_ISA : OTHER_ISA);
1859 		(void) printf(gettext("SunOS 32-bit %s crash dump\n"), isa);
1860 
1861 	} else {
1862 		return (0);
1863 	}
1864 
1865 	return (1);
1866 }
1867 
1868 static void
1869 print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t),
1870     const char *isa)
1871 {
1872 	dumphdr_t dh;
1873 
1874 	/*
1875 	 * A dumphdr_t is bigger than FBSZ, so we have to manually read the
1876 	 * rest of it.
1877 	 */
1878 	if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t),
1879 	    (off_t)0) == sizeof (dumphdr_t)) {
1880 		(void) printf(gettext(
1881 		    "%s %s %s %u-bit %s crash dump from '%s'\n"),
1882 		    dh.dump_utsname.sysname, dh.dump_utsname.release,
1883 		    dh.dump_utsname.version, swap(dh.dump_wordsize), isa,
1884 		    dh.dump_utsname.nodename);
1885 	} else {
1886 		(void) printf(gettext("SunOS %u-bit %s crash dump\n"),
1887 		    swap(dhp->dump_wordsize), isa);
1888 	}
1889 }
1890 
1891 static void
1892 usage(void)
1893 {
1894 	(void) fprintf(stderr, gettext(
1895 		"usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n"
1896 		"       file [-dh] [-M mfile] [-m mfile] -f ffile\n"
1897 		"       file -i [-h] [-f ffile] file ...\n"
1898 		"       file -i [-h] -f ffile\n"
1899 		"       file -c [-d] [-M mfile] [-m mfile]\n"));
1900 	exit(2);
1901 }
1902 
1903 static uint32_t
1904 swap_uint32(uint32_t in)
1905 {
1906 	uint32_t out;
1907 
1908 	out = (in & 0x000000ff) << 24;
1909 	out |= (in & 0x0000ff00) << 8; /* >> 8 << 16 */
1910 	out |= (in & 0x00ff0000) >> 8; /* >> 16 << 8 */
1911 	out |= (in & 0xff000000) >> 24;
1912 
1913 	return (out);
1914 }
1915 
1916 static uint32_t
1917 return_uint32(uint32_t in)
1918 {
1919 	return (in);
1920 }
1921 
1922 /*
1923  * Check if str is in the string list str_list.
1924  */
1925 static int
1926 is_in_list(char *str_list[], char *str)
1927 {
1928 	int i;
1929 
1930 	/*
1931 	 * Only need to compare the strlen(str_list[i]) bytes.
1932 	 * That way .stab will match on .stab* sections, and
1933 	 * .debug will match on .debug* sections.
1934 	 */
1935 	for (i = 0; str_list[i] != NULL; i++) {
1936 		if (strncmp(str_list[i], str, strlen(str_list[i])) == 0) {
1937 			return (1);
1938 		}
1939 	}
1940 	return (0);
1941 }
1942 
1943 /*
1944  * default_magic -
1945  *	allocate space for and create the default magic file
1946  *	name string.
1947  */
1948 
1949 static void
1950 default_magic(void)
1951 {
1952 	const char *msg_locale = setlocale(LC_MESSAGES, NULL);
1953 	struct stat	statbuf;
1954 
1955 	if ((dfile = (char *)malloc(strlen(msg_locale) + 35)) == NULL) {
1956 		perror("file");
1957 		exit(2);
1958 	}
1959 	(void) snprintf(dfile, strlen(msg_locale) + 35,
1960 	    "/usr/lib/locale/%s/LC_MESSAGES/magic", msg_locale);
1961 	if (stat(dfile, &statbuf) != 0) {
1962 		(void) strcpy(dfile, "/etc/magic");
1963 	}
1964 }
1965 
1966 /*
1967  * add_to_mlist -
1968  *	Add the given magic_file filename string to the list of magic
1969  *	files (mlist).  This list of files will later be examined, and
1970  *	each magic file's entries will be added in order to
1971  *	the mtab table.
1972  *
1973  *	The first flag is set to 1 to add to the first list, mlist1.
1974  *	The first flag is set to 0 to add to the second list, mlist2.
1975  */
1976 
1977 static void
1978 add_to_mlist(char *magic_file, int first)
1979 {
1980 	char	**mlist;	/* ordered list of magic files */
1981 	size_t	mlist_sz;	/* number of pointers allocated  for mlist */
1982 	char	**mlistp;	/* next entry in mlist */
1983 	size_t mlistp_off;
1984 
1985 	if (first) {
1986 		mlist = mlist1;
1987 		mlist_sz = mlist1_sz;
1988 		mlistp = mlist1p;
1989 	} else {
1990 		mlist = mlist2;
1991 		mlist_sz = mlist2_sz;
1992 		mlistp = mlist2p;
1993 	}
1994 
1995 	if (mlist == NULL) {	/* initial mlist allocation */
1996 		if ((mlist = (char **)calloc(MLIST_SZ, sizeof (char *)))
1997 		    == NULL) {
1998 			perror("file");
1999 			exit(2);
2000 		}
2001 		mlist_sz = MLIST_SZ;
2002 		mlistp = mlist;
2003 	}
2004 	if ((mlistp - mlist) >= mlist_sz) {
2005 		mlistp_off = mlistp - mlist;
2006 		mlist_sz *= 2;
2007 		if ((mlist = (char **)realloc(mlist,
2008 		    mlist_sz * sizeof (char *))) == NULL) {
2009 			perror("file");
2010 			exit(2);
2011 		}
2012 		mlistp = mlist + mlistp_off;
2013 	}
2014 	/*
2015 	 * now allocate memory for and copy the
2016 	 * magic file name string
2017 	 */
2018 	if ((*mlistp = malloc(strlen(magic_file) + 1)) == NULL) {
2019 		perror("file");
2020 		exit(2);
2021 	}
2022 	(void) strlcpy(*mlistp, magic_file, strlen(magic_file) + 1);
2023 	mlistp++;
2024 
2025 	if (first) {
2026 		mlist1 = mlist;
2027 		mlist1_sz = mlist_sz;
2028 		mlist1p = mlistp;
2029 	} else {
2030 		mlist2 = mlist;
2031 		mlist2_sz = mlist_sz;
2032 		mlist2p = mlistp;
2033 	}
2034 }
2035 
2036 static void
2037 fd_cleanup(void)
2038 {
2039 	if (ifd != -1) {
2040 		(void) close(ifd);
2041 		ifd = -1;
2042 	}
2043 	if (elffd != -1) {
2044 		(void) close(elffd);
2045 		elffd = -1;
2046 	}
2047 }
2048