/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* Copyright (c) 1987, 1988 Microsoft Corporation */ /* All Rights Reserved */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #define _LARGEFILE64_SOURCE /* Get definitions for the relocation types supported. */ #define ELF_TARGET_ALL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "file.h" typedef Elf64_Nhdr GElf_Nhdr; /* * Misc */ #define FBSZ 512 #define MLIST_SZ 12 /* * The 0x8FCA0102 magic string was used in crash dumps generated by releases * prior to Solaris 7. */ #define OLD_DUMP_MAGIC 0x8FCA0102 #if defined(__sparc) #define NATIVE_ISA "SPARC" #define OTHER_ISA "Intel" #else #define NATIVE_ISA "Intel" #define OTHER_ISA "SPARC" #endif /* Assembly language comment char */ #ifdef pdp11 #define ASCOMCHAR '/' #else #define ASCOMCHAR '!' #endif #pragma align 16(fbuf) static char fbuf[FBSZ]; /* * Magic file variables */ static intmax_t maxmagicoffset; static intmax_t tmpmax; static char *magicbuf; static char *dfile; static char *troff[] = { /* new troff intermediate lang */ "x", "T", "res", "init", "font", "202", "V0", "p1", 0}; static char *fort[] = { /* FORTRAN */ "function", "subroutine", "common", "dimension", "block", "integer", "real", "data", "double", "FUNCTION", "SUBROUTINE", "COMMON", "DIMENSION", "BLOCK", "INTEGER", "REAL", "DATA", "DOUBLE", 0}; static char *asc[] = { /* Assembler Commands */ "sys", "mov", "tst", "clr", "jmp", "cmp", "set", "inc", "dec", 0}; static char *c[] = { /* C Language */ "int", "char", "float", "double", "short", "long", "unsigned", "register", "static", "struct", "extern", 0}; static char *as[] = { /* Assembler Pseudo Ops, prepended with '.' */ "globl", "global", "ident", "file", "byte", "even", "text", "data", "bss", "comm", 0}; /* * The line and debug section names are used by the strip command. * Any changes in the strip implementation need to be reflected here. */ static char *debug_sections[] = { /* Debug sections in a ELF file */ ".debug", ".stab", ".dwarf", ".line", NULL}; /* start for MB env */ static wchar_t wchar; static int length; static int IS_ascii; static int Max; /* end for MB env */ static int i; /* global index into first 'fbsz' bytes of file */ static int fbsz; static int ifd = -1; static int elffd = -1; static int tret; static int hflg; static int dflg; static int mflg; static int M_flg; static int iflg; static struct stat64 mbuf; static char **mlist1; /* 1st ordered list of magic files */ static char **mlist2; /* 2nd ordered list of magic files */ static size_t mlist1_sz; /* number of ptrs allocated for mlist1 */ static size_t mlist2_sz; /* number of ptrs allocated for mlist2 */ static char **mlist1p; /* next entry in mlist1 */ static char **mlist2p; /* next entry in mlist2 */ static ssize_t mread; static void is_stripped(Elf *elf); static Elf *is_elf_file(int elffd); static void ar_coff_or_aout(int ifd); static int type(char *file); static int def_position_tests(void); static void def_context_tests(void); static int troffint(char *bp, int n); static int lookup(char **tab); static int ccom(void); static int ascom(void); static int sccs(void); static int english(char *bp, int n); static int old_core(Elf *elf, GElf_Ehdr *ehdr, int format); static int core(Elf *elf, GElf_Ehdr *ehdr, int format); static int shellscript(char buf[], struct stat64 *sb); static int elf_check(Elf *elf); static int get_door_target(char *, char *, size_t); static int zipfile(char *, int); static int is_crash_dump(const char *, int); static void print_dumphdr(const int, const dumphdr_t *, uint32_t (*)(uint32_t), const char *); static uint32_t swap_uint32(uint32_t); static uint32_t return_uint32(uint32_t); static int is_in_list(char *[], char *); static void usage(void); static void default_magic(void); static void add_to_mlist(char *, int); static void fd_cleanup(void); static int is_rtld_config(void); #ifdef XPG4 /* SUSv3 requires a single after the colon */ #define prf(x) (void) printf("%s: ", x); #else /* !XPG4 */ #define prf(x) (void) printf("%s:%s", x, (int)strlen(x) > 6 ? "\t" : "\t\t"); #endif /* XPG4 */ int main(int argc, char **argv) { char *p; int ch; FILE *fl; int cflg = 0; int eflg = 0; int fflg = 0; char *ap = NULL; int pathlen; char **filep; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); while ((ch = getopt(argc, argv, "M:cdf:him:")) != EOF) { switch (ch) { case 'M': add_to_mlist(optarg, !dflg); M_flg++; break; case 'c': cflg++; break; case 'd': if (!dflg) { default_magic(); add_to_mlist(dfile, 0); dflg++; } break; case 'f': fflg++; if ((fl = fopen(optarg, "r")) == NULL) { (void) fprintf(stderr, gettext("cannot open %s\n"), optarg); usage(); } pathlen = pathconf("/", _PC_PATH_MAX); if (pathlen == -1) { (void) fprintf(stderr, gettext("pathconf: cannot determine " "maximum path length\n")); exit(1); } pathlen += 2; /* for null and newline in fgets */ ap = malloc(pathlen * sizeof (char)); if (ap == NULL) { perror("malloc"); exit(1); } break; case 'h': hflg++; break; case 'i': iflg++; break; case 'm': add_to_mlist(optarg, !dflg); mflg++; break; case '?': eflg++; break; } } if (!cflg && !fflg && (eflg || optind == argc)) usage(); if (iflg && (dflg || mflg || M_flg)) { usage(); } if (iflg && cflg) { usage(); } if (!dflg && !mflg && !M_flg && !iflg) { /* no -d, -m, nor -M option; also -i option doesn't need magic */ default_magic(); if (f_mkmtab(dfile, cflg, 0) == -1) { exit(2); } } else if (mflg && !M_flg && !dflg) { /* -m specified without -d nor -M */ #ifdef XPG4 /* For SUSv3 only */ /* * The default position-dependent magic file tests * in /etc/magic will follow all the -m magic tests. */ for (filep = mlist1; filep < mlist1p; filep++) { if (f_mkmtab(*filep, cflg, 1) == -1) { exit(2); } } default_magic(); if (f_mkmtab(dfile, cflg, 0) == -1) { exit(2); } #else /* !XPG4 */ /* * Retain Solaris file behavior for -m before SUSv3, * when the new -d and -M options are not specified. * Use the -m file specified in place of the default * /etc/magic file. Solaris file will * now allow more than one magic file to be specified * with multiple -m options, for consistency with * other behavior. * * Put the magic table(s) specified by -m into * the second magic table instead of the first * (as indicated by the last argument to f_mkmtab()), * since they replace the /etc/magic tests and * must be executed alongside the default * position-sensitive tests. */ for (filep = mlist1; filep < mlist1p; filep++) { if (f_mkmtab(*filep, cflg, 0) == -1) { exit(2); } } #endif /* XPG4 */ } else { /* * For any other combination of -d, -m, and -M, * use the magic files in command-line order. * Store the entries from the two separate lists of magic * files, if any, into two separate magic file tables. * mlist1: magic tests executed before default magic tests * mlist2: default magic tests and after */ for (filep = mlist1; filep && (filep < mlist1p); filep++) { if (f_mkmtab(*filep, cflg, 1) == -1) { exit(2); } } for (filep = mlist2; filep && (filep < mlist2p); filep++) { if (f_mkmtab(*filep, cflg, 0) == -1) { exit(2); } } } /* Initialize the magic file variables; check both magic tables */ tmpmax = f_getmaxoffset(1); maxmagicoffset = f_getmaxoffset(0); if (maxmagicoffset < tmpmax) { maxmagicoffset = tmpmax; } if (maxmagicoffset < (intmax_t)FBSZ) maxmagicoffset = (intmax_t)FBSZ; if ((magicbuf = (char *)malloc(maxmagicoffset)) == NULL) { (void) fprintf(stderr, gettext("malloc failed\n")); exit(2); } if (cflg) { f_prtmtab(); if (ferror(stdout) != 0) { (void) fprintf(stderr, gettext("file: error writing to " "stdout\n")); exit(1); } if (fclose(stdout) != 0) { perror(gettext("file: fclose failed")); exit(1); } exit(0); } for (; fflg || optind < argc; optind += !fflg) { register int l; if (fflg) { if ((p = fgets(ap, pathlen, fl)) == NULL) { fflg = 0; optind--; continue; } l = strlen(p); if (l > 0) p[l - 1] = '\0'; } else p = argv[optind]; prf(p); /* print "file_name:" */ if (type(p)) tret = 1; } if (ap != NULL) free(ap); if (tret != 0) { exit(tret); } if (ferror(stdout) != 0) { (void) fprintf(stderr, gettext("file: error writing to " "stdout\n")); exit(1); } if (fclose(stdout) != 0) { perror(gettext("file: fclose failed")); exit(1); } return (0); } static int type(char *file) { int cc; char buf[BUFSIZ]; int (*statf)() = hflg ? lstat64 : stat64; i = 0; /* reset index to beginning of file */ ifd = -1; if ((*statf)(file, &mbuf) < 0) { if (statf == lstat64 || lstat64(file, &mbuf) < 0) { (void) printf(gettext("cannot open: %s\n"), strerror(errno)); return (0); /* POSIX.2 */ } } switch (mbuf.st_mode & S_IFMT) { case S_IFREG: if (iflg) { (void) printf(gettext("regular file\n")); return (0); } break; case S_IFCHR: (void) printf(gettext("character")); goto spcl; case S_IFDIR: (void) printf(gettext("directory\n")); return (0); case S_IFIFO: (void) printf(gettext("fifo\n")); return (0); case S_IFLNK: if ((cc = readlink(file, buf, BUFSIZ)) < 0) { (void) printf(gettext("readlink error: %s\n"), strerror(errno)); return (1); } buf[cc] = '\0'; (void) printf(gettext("symbolic link to %s\n"), buf); return (0); case S_IFBLK: (void) printf(gettext("block")); /* major and minor, see sys/mkdev.h */ spcl: (void) printf(gettext(" special (%d/%d)\n"), major(mbuf.st_rdev), minor(mbuf.st_rdev)); return (0); case S_IFSOCK: (void) printf("socket\n"); /* FIXME, should open and try to getsockname. */ return (0); case S_IFDOOR: if (get_door_target(file, buf, sizeof (buf)) == 0) (void) printf(gettext("door to %s\n"), buf); else (void) printf(gettext("door\n")); return (0); } if (elf_version(EV_CURRENT) == EV_NONE) { (void) printf(gettext("libelf is out of date\n")); return (1); } ifd = open64(file, O_RDONLY); if (ifd < 0) { (void) printf(gettext("cannot open: %s\n"), strerror(errno)); return (0); /* POSIX.2 */ } /* need another fd for elf, since we might want to read the file too */ elffd = open64(file, O_RDONLY); if (elffd < 0) { (void) printf(gettext("cannot open: %s\n"), strerror(errno)); (void) close(ifd); ifd = -1; return (0); /* POSIX.2 */ } if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) { (void) printf(gettext("cannot read: %s\n"), strerror(errno)); (void) close(ifd); ifd = -1; return (0); /* POSIX.2 */ } if (fbsz == 0) { (void) printf(gettext("empty file\n")); fd_cleanup(); return (0); } /* * First try user-specified position-dependent magic tests, if any, * which need to execute before the default tests. */ if ((mread = pread(ifd, (void*)magicbuf, (size_t)maxmagicoffset, (off_t)0)) == -1) { (void) printf(gettext("cannot read: %s\n"), strerror(errno)); fd_cleanup(); return (0); } /* * ChecK against Magic Table entries. * Check first magic table for magic tests to be applied * before default tests. * If no default tests are to be applied, all magic tests * should occur in this magic table. */ switch (f_ckmtab(magicbuf, mread, 1)) { case -1: /* Error */ exit(2); break; case 0: /* Not magic */ break; default: /* Switch is magic index */ (void) putchar('\n'); fd_cleanup(); return (0); /* NOTREACHED */ break; } if (dflg || !M_flg) { /* * default position-dependent tests, * plus non-default magic tests, if any */ switch (def_position_tests()) { case -1: /* error */ fd_cleanup(); return (1); case 1: /* matching type found */ fd_cleanup(); return (0); /* NOTREACHED */ break; case 0: /* no matching type found */ break; } /* default context-sensitive tests */ def_context_tests(); } else { /* no more tests to apply; no match was found */ (void) printf(gettext("data\n")); } fd_cleanup(); return (0); } /* * def_position_tests() - applies default position-sensitive tests, * looking for values in specific positions in the file. * These are followed by default (followed by possibly some * non-default) magic file tests. * * All position-sensitive tests, default or otherwise, must * be applied before context-sensitive tests, to avoid * false context-sensitive matches. * * Returns -1 on error which should result in error (non-zero) * exit status for the file utility. * Returns 0 if no matching file type found. * Returns 1 if matching file type found. */ static int def_position_tests(void) { Elf *elf; if (sccs()) { /* look for "1hddddd" where d is a digit */ (void) printf("sccs \n"); return (1); } if (fbuf[0] == '#' && fbuf[1] == '!' && shellscript(fbuf+2, &mbuf)) return (1); if ((elf = is_elf_file(elffd)) != NULL) { (void) elf_check(elf); (void) elf_end(elf); (void) putchar('\n'); return (1); /* LINTED: pointer cast may result in improper alignment */ } else if (*(int *)fbuf == CORE_MAGIC) { /* LINTED: pointer cast may result in improper alignment */ struct core *corep = (struct core *)fbuf; (void) printf("a.out core file"); if (*(corep->c_cmdname) != '\0') (void) printf(" from '%s'", corep->c_cmdname); (void) putchar('\n'); return (1); } /* * Runtime linker (ld.so.1) configuration file. */ if (is_rtld_config()) return (1); /* * ZIP files, JAR files, and Java executables */ if (zipfile(fbuf, ifd)) return (1); if (is_crash_dump(fbuf, ifd)) return (1); /* * ChecK against Magic Table entries. * The magic entries checked here always start with default * magic tests and may be followed by other, non-default magic * tests. If no default tests are to be executed, all the * magic tests should have been in the first magic table. */ switch (f_ckmtab(magicbuf, mread, 0)) { case -1: /* Error */ exit(2); break; case 0: /* Not magic */ return (0); /* NOTREACHED */ break; default: /* Switch is magic index */ /* * f_ckmtab recognizes file type, * check if it is PostScript. * if not, check if elf or a.out */ if (magicbuf[0] == '%' && magicbuf[1] == '!') { (void) putchar('\n'); } else { /* * Check that the file is executable (dynamic * objects must be executable to be exec'ed, * shared objects need not be, but by convention * should be executable). * * Note that we should already have processed * the file if it was an ELF file. */ ar_coff_or_aout(elffd); (void) putchar('\n'); } return (1); /* NOTREACHED */ break; } return (0); /* file was not identified */ } /* * def_context_tests() - default context-sensitive tests. * These are the last tests to be applied. * If no match is found, prints out "data". */ static void def_context_tests(void) { int j; int nl; char ch; int len; if (ccom() == 0) goto notc; while (fbuf[i] == '#') { j = i; while (fbuf[i++] != '\n') { if (i - j > 255) { (void) printf(gettext("data\n")); return; } if (i >= fbsz) goto notc; } if (ccom() == 0) goto notc; } check: if (lookup(c) == 1) { while ((ch = fbuf[i]) != ';' && ch != '{') { if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0) len = 1; i += len; if (i >= fbsz) goto notc; } (void) printf(gettext("c program text")); goto outa; } nl = 0; while (fbuf[i] != '(') { if (fbuf[i] <= 0) goto notas; if (fbuf[i] == ';') { i++; goto check; } if (fbuf[i++] == '\n') if (nl++ > 6) goto notc; if (i >= fbsz) goto notc; } while (fbuf[i] != ')') { if (fbuf[i++] == '\n') if (nl++ > 6) goto notc; if (i >= fbsz) goto notc; } while (fbuf[i] != '{') { if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0) len = 1; if (fbuf[i] == '\n') if (nl++ > 6) goto notc; i += len; if (i >= fbsz) goto notc; } (void) printf(gettext("c program text")); goto outa; notc: i = 0; /* reset to begining of file again */ while (fbuf[i] == 'c' || fbuf[i] == 'C'|| fbuf[i] == '!' || fbuf[i] == '*' || fbuf[i] == '\n') { while (fbuf[i++] != '\n') if (i >= fbsz) goto notfort; } if (lookup(fort) == 1) { (void) printf(gettext("fortran program text")); goto outa; } notfort: /* looking for assembler program */ i = 0; /* reset to beginning of file again */ if (ccom() == 0) /* assembler programs may contain */ /* c-style comments */ goto notas; if (ascom() == 0) goto notas; j = i - 1; if (fbuf[i] == '.') { i++; if (lookup(as) == 1) { (void) printf(gettext("assembler program text")); goto outa; } else if (j != -1 && fbuf[j] == '\n' && isalpha(fbuf[j + 2])) { (void) printf( gettext("[nt]roff, tbl, or eqn input text")); goto outa; } } while (lookup(asc) == 0) { if (ccom() == 0) goto notas; if (ascom() == 0) goto notas; while (fbuf[i] != '\n' && fbuf[i++] != ':') { if (i >= fbsz) goto notas; } while (fbuf[i] == '\n' || fbuf[i] == ' ' || fbuf[i] == '\t') if (i++ >= fbsz) goto notas; j = i - 1; if (fbuf[i] == '.') { i++; if (lookup(as) == 1) { (void) printf( gettext("assembler program text")); goto outa; } else if (fbuf[j] == '\n' && isalpha(fbuf[j+2])) { (void) printf( gettext("[nt]roff, tbl, or eqn input " "text")); goto outa; } } } (void) printf(gettext("assembler program text")); goto outa; notas: /* start modification for multibyte env */ IS_ascii = 1; if (fbsz < FBSZ) Max = fbsz; else Max = FBSZ - MB_LEN_MAX; /* prevent cut of wchar read */ /* end modification for multibyte env */ for (i = 0; i < Max; /* null */) if (fbuf[i] & 0200) { IS_ascii = 0; if (fbuf[0] == '\100' && fbuf[1] == '\357') { (void) printf(gettext("troff output\n")); return; } /* start modification for multibyte env */ if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX)) <= 0 || !iswprint(wchar)) { (void) printf(gettext("data\n")); return; } i += length; } else i++; i = fbsz; /* end modification for multibyte env */ if (mbuf.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH)) (void) printf(gettext("commands text")); else if (troffint(fbuf, fbsz)) (void) printf(gettext("troff intermediate output text")); else if (english(fbuf, fbsz)) (void) printf(gettext("English text")); else if (IS_ascii) (void) printf(gettext("ascii text")); else (void) printf(gettext("text")); /* for multibyte env */ outa: /* * This code is to make sure that no MB char is cut in half * while still being used. */ fbsz = (fbsz < FBSZ ? fbsz : fbsz - MB_CUR_MAX + 1); while (i < fbsz) { if (isascii(fbuf[i])) { i++; continue; } else { if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX)) <= 0 || !iswprint(wchar)) { (void) printf(gettext(" with garbage\n")); return; } i = i + length; } } (void) printf("\n"); } static int troffint(char *bp, int n) { int k; i = 0; for (k = 0; k < 6; k++) { if (lookup(troff) == 0) return (0); if (lookup(troff) == 0) return (0); while (i < n && bp[i] != '\n') i++; if (i++ >= n) return (0); } return (1); } /* * Determine if the passed descriptor describes an ELF file. * If so, return the Elf handle. */ static Elf * is_elf_file(int elffd) { Elf *elf; elf = elf_begin(elffd, ELF_C_READ, (Elf *)0); switch (elf_kind(elf)) { case ELF_K_ELF: break; default: (void) elf_end(elf); elf = NULL; break; } return (elf); } static void ar_coff_or_aout(int elffd) { Elf *elf; /* * Get the files elf descriptor and process it as an elf or * a.out (4.x) file. */ elf = elf_begin(elffd, ELF_C_READ, (Elf *)0); switch (elf_kind(elf)) { case ELF_K_AR : (void) printf(gettext(", not a dynamic executable " "or shared object")); break; case ELF_K_COFF: (void) printf(gettext(", unsupported or unknown " "file type")); break; default: /* * This is either an unknown file or an aout format * At this time, we don't print dynamic/stripped * info. on a.out or non-Elf binaries. */ break; } (void) elf_end(elf); } static void print_elf_type(Elf *elf, GElf_Ehdr *ehdr, int format) { switch (ehdr->e_type) { case ET_NONE: (void) printf(" %s", gettext("unknown type")); break; case ET_REL: (void) printf(" %s", gettext("relocatable")); break; case ET_EXEC: (void) printf(" %s", gettext("executable")); break; case ET_DYN: (void) printf(" %s", gettext("dynamic lib")); break; case ET_CORE: if (old_core(elf, ehdr, format)) (void) printf(" %s", gettext("pre-2.6 core file")); else (void) printf(" %s", gettext("core file")); break; default: break; } } static void print_elf_machine(int machine) { switch (machine) { case EM_NONE: (void) printf(" %s", gettext("unknown machine")); break; case EM_M32: (void) printf(" %s", gettext("WE32100")); break; case EM_SPARC: (void) printf(" %s", gettext("SPARC")); break; case EM_386: (void) printf(" %s", gettext("80386")); break; case EM_68K: (void) printf(" %s", gettext("M68000")); break; case EM_88K: (void) printf(" %s", gettext("M88000")); break; case EM_486: (void) printf(" %s", gettext("80486")); break; case EM_860: (void) printf(" %s", gettext("i860")); break; case EM_MIPS: (void) printf(" %s", gettext("MIPS RS3000 Big-Endian")); break; case EM_MIPS_RS3_LE: (void) printf(" %s", gettext("MIPS RS3000 Little-Endian")); break; case EM_RS6000: (void) printf(" %s", gettext("MIPS RS6000")); break; case EM_PA_RISC: (void) printf(" %s", gettext("PA-RISC")); break; case EM_nCUBE: (void) printf(" %s", gettext("nCUBE")); break; case EM_VPP500: (void) printf(" %s", gettext("VPP500")); break; case EM_SPARC32PLUS: (void) printf(" %s", gettext("SPARC32PLUS")); break; case EM_PPC: (void) printf(" %s", gettext("PowerPC")); break; case EM_SPARCV9: (void) printf(" %s", gettext("SPARCV9")); break; case EM_IA_64: (void) printf(" %s", gettext("IA64")); break; case EM_AMD64: (void) printf(" %s", gettext("AMD64")); break; default: break; } } static void print_elf_datatype(int datatype) { switch (datatype) { case ELFDATA2LSB: (void) printf(" %s", gettext("LSB")); break; case ELFDATA2MSB: (void) printf(" %s", gettext("MSB")); break; default: break; } } static void print_elf_class(int class) { switch (class) { case ELFCLASS32: (void) printf(" %s", gettext("32-bit")); break; case ELFCLASS64: (void) printf(" %s", gettext("64-bit")); break; default: break; } } static void print_elf_flags(int machine, unsigned int flags) { switch (machine) { case EM_SPARCV9: if (flags & EF_SPARC_EXT_MASK) { if (flags & EF_SPARC_SUN_US3) { (void) printf("%s", gettext( ", UltraSPARC3 Extensions Required")); } else if (flags & EF_SPARC_SUN_US1) { (void) printf("%s", gettext( ", UltraSPARC1 Extensions Required")); } if (flags & EF_SPARC_HAL_R1) (void) printf("%s", gettext( ", HaL R1 Extensions Required")); } break; case EM_SPARC32PLUS: if (flags & EF_SPARC_32PLUS) (void) printf("%s", gettext(", V8+ Required")); if (flags & EF_SPARC_SUN_US3) { (void) printf("%s", gettext(", UltraSPARC3 Extensions Required")); } else if (flags & EF_SPARC_SUN_US1) { (void) printf("%s", gettext(", UltraSPARC1 Extensions Required")); } if (flags & EF_SPARC_HAL_R1) (void) printf("%s", gettext(", HaL R1 Extensions Required")); break; default: break; } } static int print_cap(Elf *elf, GElf_Ehdr *ehdr) { Elf_Scn *scn = 0; /* * Traverse the files sections to see if any software/hardware * capabilities are available. */ while ((scn = elf_nextscn(elf, scn)) != 0) { GElf_Word ndx, capn; GElf_Shdr shdr; Elf_Data *data; if (gelf_getshdr(scn, &shdr) == 0) { (void) fprintf(stderr, gettext("can't read ELF section header\n")); return (1); } if (shdr.sh_type != SHT_SUNW_cap) continue; /* * Get the data associated with the .cap section. */ if ((data = elf_getdata(scn, 0)) == 0) { (void) fprintf(stderr, gettext("can't read ELF section data\n")); return (1); } capn = (GElf_Word)(shdr.sh_size / shdr.sh_entsize); for (ndx = 0; ndx < capn; ndx++) { char str[100]; GElf_Cap cap; if (gelf_getcap(data, ndx, &cap) == NULL) { (void) fprintf(stderr, gettext("can't read capabilities data\n")); return (1); } if (cap.c_tag != CA_SUNW_NULL) { (void) cap_val2str(cap.c_tag, cap.c_un.c_val, str, sizeof (str), 0, ehdr->e_machine); (void) printf(" [%s]", str); } } } return (0); } static int elf_check(Elf *elf) { GElf_Ehdr ehdr; GElf_Phdr phdr; int dynamic, cnt; char *ident; size_t size; /* * verify information in file header */ if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)0) { (void) fprintf(stderr, gettext("can't read ELF header\n")); return (1); } ident = elf_getident(elf, &size); (void) printf("%s", gettext("ELF")); print_elf_class(ident[EI_CLASS]); print_elf_datatype(ident[EI_DATA]); print_elf_type(elf, &ehdr, ident[EI_DATA]); print_elf_machine(ehdr.e_machine); if (ehdr.e_version == 1) (void) printf(" %s %d", gettext("Version"), (int)ehdr.e_version); print_elf_flags(ehdr.e_machine, ehdr.e_flags); if (core(elf, &ehdr, ident[EI_DATA])) /* check for core file */ return (0); if (print_cap(elf, &ehdr)) return (1); /* * check type */ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) return (1); /* * read program header and check for dynamic section */ if (ehdr.e_phnum == 0) { (void) fprintf(stderr, gettext("can't read program header\n")); return (1); } for (dynamic = 0, cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) { if (gelf_getphdr(elf, cnt, &phdr) == NULL) { (void) fprintf(stderr, gettext("can't read program header\n")); return (1); } if (phdr.p_type == PT_DYNAMIC) { dynamic = 1; break; } } if (dynamic) (void) printf(gettext(", dynamically linked")); else (void) printf(gettext(", statically linked")); is_stripped(elf); return (0); } /* * is_stripped prints information on whether the executable has * been stripped. */ static void is_stripped(Elf *elf) { GElf_Shdr shdr; GElf_Ehdr ehdr; Elf_Scn *scn, *nextscn; char *section_name; int symtab = 0; int debuginfo = 0; if (gelf_getehdr(elf, &ehdr) == NULL) { return; } /* * Definition time: * - "not stripped" means that an executable file * contains a Symbol Table (.symtab) * - "stripped" means that an executable file * does not contain a Symbol Table. * When strip -l or strip -x is run, it strips the * debugging information (.line section name (strip -l), * .line, .debug*, .stabs*, .dwarf* section names * and SHT_SUNW_DEBUGSTR and SHT_SUNW_DEBUG * section types (strip -x), however the Symbol * Table will still be present. * Therefore, if * - No Symbol Table present, then report * "stripped" * - Symbol Table present with debugging * information (line number or debug section names, * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section * types) then report: * "not stripped" * - Symbol Table present with no debugging * information (line number or debug section names, * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section * types) then report: * "not stripped, no debugging information * available" */ scn = NULL; while ((nextscn = elf_nextscn(elf, scn)) != NULL) { if (symtab && debuginfo) { break; } scn = nextscn; if (gelf_getshdr(scn, &shdr) == NULL) { continue; } if (!symtab && (shdr.sh_type == SHT_SYMTAB)) { symtab++; continue; } if (!debuginfo && ((shdr.sh_type == SHT_SUNW_DEBUG) || (shdr.sh_type == SHT_SUNW_DEBUGSTR) || (((section_name = elf_strptr(elf, ehdr.e_shstrndx, (size_t)shdr.sh_name)) != NULL) && (is_in_list(debug_sections, section_name))))) { debuginfo++; } } /* * Now that we've scanned all sections, print out the appropriate * diagnostic. */ if (symtab) { (void) printf(gettext(", not stripped")); if (!debuginfo) { (void) printf(gettext( ", no debugging information available")); } } else { (void) printf(gettext(", stripped")); } } /* * is_rtld_config - If file is a runtime linker config file, prints * the description and returns True (1). Otherwise, silently returns * False (0). */ int is_rtld_config(void) { Rtc_id *id; if ((fbsz >= sizeof (*id)) && RTC_ID_TEST(fbuf)) { (void) printf(gettext("Runtime Linking Configuration")); /* LINTED: pointer cast may result in improper alignment */ id = (Rtc_id *) fbuf; print_elf_class(id->id_class); print_elf_datatype(id->id_data); print_elf_machine(id->id_machine); (void) printf("\n"); return (1); } return (0); } /* * lookup - * Attempts to match one of the strings from a list, 'tab', * with what is in the file, starting at the current index position 'i'. * Looks past any initial whitespace and expects whitespace or other * delimiting characters to follow the matched string. * A match identifies the file as being 'assembler', 'fortran', 'c', etc. * Returns 1 for a successful match, 0 otherwise. */ static int lookup(char **tab) { register char r; register int k, j, l; while (fbuf[i] == ' ' || fbuf[i] == '\t' || fbuf[i] == '\n') i++; for (j = 0; tab[j] != 0; j++) { l = 0; for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++); if (r == '\0') if (fbuf[k] == ' ' || fbuf[k] == '\n' || fbuf[k] == '\t' || fbuf[k] == '{' || fbuf[k] == '/') { i = k; return (1); } } return (0); } /* * ccom - * Increments the current index 'i' into the file buffer 'fbuf' past any * whitespace lines and C-style comments found, starting at the current * position of 'i'. Returns 1 as long as we don't increment i past the * size of fbuf (fbsz). Otherwise, returns 0. */ static int ccom(void) { register char cc; int len; while ((cc = fbuf[i]) == ' ' || cc == '\t' || cc == '\n') if (i++ >= fbsz) return (0); if (fbuf[i] == '/' && fbuf[i+1] == '*') { i += 2; while (fbuf[i] != '*' || fbuf[i+1] != '/') { if (fbuf[i] == '\\') i++; if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0) len = 1; i += len; if (i >= fbsz) return (0); } if ((i += 2) >= fbsz) return (0); } if (fbuf[i] == '\n') if (ccom() == 0) return (0); return (1); } /* * ascom - * Increments the current index 'i' into the file buffer 'fbuf' past * consecutive assembler program comment lines starting with ASCOMCHAR, * starting at the current position of 'i'. * Returns 1 as long as we don't increment i past the * size of fbuf (fbsz). Otherwise returns 0. */ static int ascom(void) { while (fbuf[i] == ASCOMCHAR) { i++; while (fbuf[i++] != '\n') if (i >= fbsz) return (0); while (fbuf[i] == '\n') if (i++ >= fbsz) return (0); } return (1); } static int sccs(void) { /* look for "1hddddd" where d is a digit */ register int j; if (fbuf[0] == 1 && fbuf[1] == 'h') { for (j = 2; j <= 6; j++) { if (isdigit(fbuf[j])) continue; else return (0); } } else { return (0); } return (1); } static int english(char *bp, int n) { #define NASC 128 /* number of ascii char ?? */ register int j, vow, freq, rare, len; register int badpun = 0, punct = 0; int ct[NASC]; if (n < 50) return (0); /* no point in statistics on squibs */ for (j = 0; j < NASC; j++) ct[j] = 0; for (j = 0; j < n; j += len) { if ((unsigned char)bp[j] < NASC) ct[bp[j]|040]++; switch (bp[j]) { case '.': case ',': case ')': case '%': case ';': case ':': case '?': punct++; if (j < n-1 && bp[j+1] != ' ' && bp[j+1] != '\n') badpun++; } if ((len = mblen(&bp[j], MB_CUR_MAX)) <= 0) len = 1; } if (badpun*5 > punct) return (0); vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u']; freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n']; rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z']; if (2*ct[';'] > ct['e']) return (0); if ((ct['>'] + ct['<'] + ct['/']) > ct['e']) return (0); /* shell file test */ return (vow * 5 >= n - ct[' '] && freq >= 10 * rare); } /* * Convert a word from an elf file to native format. * This is needed because there's no elf routine to * get and decode a Note section header. */ static void convert_gelf_word(Elf *elf, GElf_Word *data, int version, int format) { Elf_Data src, dst; dst.d_buf = data; dst.d_version = version; dst.d_size = sizeof (GElf_Word); dst.d_type = ELF_T_WORD; src.d_buf = data; src.d_version = version; src.d_size = sizeof (GElf_Word); src.d_type = ELF_T_WORD; (void) gelf_xlatetom(elf, &dst, &src, format); } static void convert_gelf_nhdr(Elf *elf, GElf_Nhdr *nhdr, GElf_Word version, int format) { convert_gelf_word(elf, &nhdr->n_namesz, version, format); convert_gelf_word(elf, &nhdr->n_descsz, version, format); convert_gelf_word(elf, &nhdr->n_type, version, format); } /* * Return true if it is an old (pre-restructured /proc) core file. */ static int old_core(Elf *elf, GElf_Ehdr *ehdr, int format) { register int inx; GElf_Phdr phdr; GElf_Phdr nphdr; GElf_Nhdr nhdr; off_t offset; if (ehdr->e_type != ET_CORE) return (0); for (inx = 0; inx < (int)ehdr->e_phnum; inx++) { if (gelf_getphdr(elf, inx, &phdr) == NULL) { return (0); } if (phdr.p_type == PT_NOTE) { /* * If the next segment is also a note, use it instead. */ if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) { return (0); } if (nphdr.p_type == PT_NOTE) phdr = nphdr; offset = (off_t)phdr.p_offset; (void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset); convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format); /* * Old core files have type NT_PRPSINFO. */ if (nhdr.n_type == NT_PRPSINFO) return (1); return (0); } } return (0); } /* * If it's a core file, print out the name of the file that dumped core. */ static int core(Elf *elf, GElf_Ehdr *ehdr, int format) { register int inx; char *psinfo; GElf_Phdr phdr; GElf_Phdr nphdr; GElf_Nhdr nhdr; off_t offset; if (ehdr->e_type != ET_CORE) return (0); for (inx = 0; inx < (int)ehdr->e_phnum; inx++) { if (gelf_getphdr(elf, inx, &phdr) == NULL) { (void) fprintf(stderr, gettext("can't read program header\n")); return (0); } if (phdr.p_type == PT_NOTE) { char *fname; size_t size; /* * If the next segment is also a note, use it instead. */ if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) { (void) fprintf(stderr, gettext("can't read program header\n")); return (0); } if (nphdr.p_type == PT_NOTE) phdr = nphdr; offset = (off_t)phdr.p_offset; (void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset); convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format); /* * Note: the ABI states that n_namesz must * be rounded up to a 4 byte boundary. */ offset += sizeof (GElf_Nhdr) + ((nhdr.n_namesz + 0x03) & ~0x3); size = nhdr.n_descsz; psinfo = malloc(size); (void) pread(ifd, psinfo, size, offset); /* * We want to print the string contained * in psinfo->pr_fname[], where 'psinfo' * is either an old NT_PRPSINFO structure * or a new NT_PSINFO structure. * * Old core files have only type NT_PRPSINFO. * New core files have type NT_PSINFO. * * These structures are also different by * virtue of being contained in a core file * of either 32-bit or 64-bit type. * * To further complicate matters, we ourself * might be compiled either 32-bit or 64-bit. * * For these reason, we just *know* the offsets of * pr_fname[] into the four different structures * here, regardless of how we are compiled. */ if (gelf_getclass(elf) == ELFCLASS32) { /* 32-bit core file, 32-bit structures */ if (nhdr.n_type == NT_PSINFO) fname = psinfo + 88; else /* old: NT_PRPSINFO */ fname = psinfo + 84; } else if (gelf_getclass(elf) == ELFCLASS64) { /* 64-bit core file, 64-bit structures */ if (nhdr.n_type == NT_PSINFO) fname = psinfo + 136; else /* old: NT_PRPSINFO */ fname = psinfo + 120; } else { free(psinfo); break; } (void) printf(gettext(", from '%s'"), fname); free(psinfo); break; } } return (1); } static int shellscript(char buf[], struct stat64 *sb) { char *tp, *cp, *xp, *up, *gp; cp = strchr(buf, '\n'); if (cp == NULL || cp - fbuf > fbsz) return (0); for (tp = buf; tp != cp && isspace((unsigned char)*tp); tp++) if (!isascii(*tp)) return (0); for (xp = tp; tp != cp && !isspace((unsigned char)*tp); tp++) if (!isascii(*tp)) return (0); if (tp == xp) return (0); if (sb->st_mode & S_ISUID) up = gettext("set-uid "); else up = ""; if (sb->st_mode & S_ISGID) gp = gettext("set-gid "); else gp = ""; if (strncmp(xp, "/bin/sh", tp - xp) == 0) xp = gettext("shell"); else if (strncmp(xp, "/bin/csh", tp - xp) == 0) xp = gettext("c-shell"); else if (strncmp(xp, "/usr/sbin/dtrace", tp - xp) == 0) xp = gettext("DTrace"); else *tp = '\0'; /* * TRANSLATION_NOTE * This message is printed by file command for shell scripts. * The first %s is for the translation for "set-uid " (if the script * has the set-uid bit set), or is for an empty string (if the * script does not have the set-uid bit set). * Similarly, the second %s is for the translation for "set-gid ", * or is for an empty string. * The third %s is for the translation for either: "shell", "c-shell", * or "DTrace", or is for the pathname of the program the script * executes. */ (void) printf(gettext("%s%sexecutable %s script\n"), up, gp, xp); return (1); } static int get_door_target(char *file, char *buf, size_t bufsize) { int fd; door_info_t di; psinfo_t psinfo; if ((fd = open64(file, O_RDONLY)) < 0 || door_info(fd, &di) != 0) { if (fd >= 0) (void) close(fd); return (-1); } (void) close(fd); (void) sprintf(buf, "/proc/%ld/psinfo", di.di_target); if ((fd = open64(buf, O_RDONLY)) < 0 || read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) { if (fd >= 0) (void) close(fd); return (-1); } (void) close(fd); (void) snprintf(buf, bufsize, "%s[%ld]", psinfo.pr_fname, di.di_target); return (0); } /* * ZIP file header information */ #define SIGSIZ 4 #define LOCSIG "PK\003\004" #define LOCHDRSIZ 30 #define CH(b, n) (((unsigned char *)(b))[n]) #define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8)) #define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16)) #define LOCNAM(b) (SH(b, 26)) /* filename size */ #define LOCEXT(b) (SH(b, 28)) /* extra field size */ #define XFHSIZ 4 /* header id, data size */ #define XFHID(b) (SH(b, 0)) /* extract field header id */ #define XFDATASIZ(b) (SH(b, 2)) /* extract field data size */ #define XFJAVASIG 0xcafe /* java executables */ static int zipfile(char *fbuf, int fd) { off_t xoff, xoff_end; if (strncmp(fbuf, LOCSIG, SIGSIZ) != 0) return (0); xoff = LOCHDRSIZ + LOCNAM(fbuf); xoff_end = xoff + LOCEXT(fbuf); while (xoff < xoff_end) { char xfhdr[XFHSIZ]; if (pread(fd, xfhdr, XFHSIZ, xoff) != XFHSIZ) break; if (XFHID(xfhdr) == XFJAVASIG) { (void) printf("%s\n", gettext("java program")); return (1); } xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr); } /* * We could just print "ZIP archive" here. * * However, customers may be using their own entries in * /etc/magic to distinguish one kind of ZIP file from another, so * let's defer the printing of "ZIP archive" to there. */ return (0); } static int is_crash_dump(const char *buf, int fd) { /* LINTED: pointer cast may result in improper alignment */ const dumphdr_t *dhp = (const dumphdr_t *)buf; /* * The current DUMP_MAGIC string covers Solaris 7 and later releases. * The utsname struct is only present in dumphdr_t's with dump_version * greater than or equal to 9. */ if (dhp->dump_magic == DUMP_MAGIC) { print_dumphdr(fd, dhp, return_uint32, NATIVE_ISA); } else if (dhp->dump_magic == swap_uint32(DUMP_MAGIC)) { print_dumphdr(fd, dhp, swap_uint32, OTHER_ISA); } else if (dhp->dump_magic == OLD_DUMP_MAGIC || dhp->dump_magic == swap_uint32(OLD_DUMP_MAGIC)) { char *isa = (dhp->dump_magic == OLD_DUMP_MAGIC ? NATIVE_ISA : OTHER_ISA); (void) printf(gettext("SunOS 32-bit %s crash dump\n"), isa); } else { return (0); } return (1); } static void print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t), const char *isa) { dumphdr_t dh; /* * A dumphdr_t is bigger than FBSZ, so we have to manually read the * rest of it. */ if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t), (off_t)0) == sizeof (dumphdr_t)) { (void) printf(gettext( "%s %s %s %u-bit %s crash dump from '%s'\n"), dh.dump_utsname.sysname, dh.dump_utsname.release, dh.dump_utsname.version, swap(dh.dump_wordsize), isa, dh.dump_utsname.nodename); } else { (void) printf(gettext("SunOS %u-bit %s crash dump\n"), swap(dhp->dump_wordsize), isa); } } static void usage(void) { (void) fprintf(stderr, gettext( "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n" " file [-dh] [-M mfile] [-m mfile] -f ffile\n" " file -i [-h] [-f ffile] file ...\n" " file -i [-h] -f ffile\n" " file -c [-d] [-M mfile] [-m mfile]\n")); exit(2); } static uint32_t swap_uint32(uint32_t in) { uint32_t out; out = (in & 0x000000ff) << 24; out |= (in & 0x0000ff00) << 8; /* >> 8 << 16 */ out |= (in & 0x00ff0000) >> 8; /* >> 16 << 8 */ out |= (in & 0xff000000) >> 24; return (out); } static uint32_t return_uint32(uint32_t in) { return (in); } /* * Check if str is in the string list str_list. */ static int is_in_list(char *str_list[], char *str) { int i; /* * Only need to compare the strlen(str_list[i]) bytes. * That way .stab will match on .stab* sections, and * .debug will match on .debug* sections. */ for (i = 0; str_list[i] != NULL; i++) { if (strncmp(str_list[i], str, strlen(str_list[i])) == 0) { return (1); } } return (0); } /* * default_magic - * allocate space for and create the default magic file * name string. */ static void default_magic(void) { const char *msg_locale = setlocale(LC_MESSAGES, NULL); struct stat statbuf; if ((dfile = (char *)malloc(strlen(msg_locale) + 35)) == NULL) { perror("file"); exit(2); } (void) snprintf(dfile, strlen(msg_locale) + 35, "/usr/lib/locale/%s/LC_MESSAGES/magic", msg_locale); if (stat(dfile, &statbuf) != 0) { (void) strcpy(dfile, "/etc/magic"); } } /* * add_to_mlist - * Add the given magic_file filename string to the list of magic * files (mlist). This list of files will later be examined, and * each magic file's entries will be added in order to * the mtab table. * * The first flag is set to 1 to add to the first list, mlist1. * The first flag is set to 0 to add to the second list, mlist2. */ static void add_to_mlist(char *magic_file, int first) { char **mlist; /* ordered list of magic files */ size_t mlist_sz; /* number of pointers allocated for mlist */ char **mlistp; /* next entry in mlist */ size_t mlistp_off; if (first) { mlist = mlist1; mlist_sz = mlist1_sz; mlistp = mlist1p; } else { mlist = mlist2; mlist_sz = mlist2_sz; mlistp = mlist2p; } if (mlist == NULL) { /* initial mlist allocation */ if ((mlist = (char **)calloc(MLIST_SZ, sizeof (char *))) == NULL) { perror("file"); exit(2); } mlist_sz = MLIST_SZ; mlistp = mlist; } if ((mlistp - mlist) >= mlist_sz) { mlistp_off = mlistp - mlist; mlist_sz *= 2; if ((mlist = (char **)realloc(mlist, mlist_sz * sizeof (char *))) == NULL) { perror("file"); exit(2); } mlistp = mlist + mlistp_off; } /* * now allocate memory for and copy the * magic file name string */ if ((*mlistp = malloc(strlen(magic_file) + 1)) == NULL) { perror("file"); exit(2); } (void) strlcpy(*mlistp, magic_file, strlen(magic_file) + 1); mlistp++; if (first) { mlist1 = mlist; mlist1_sz = mlist_sz; mlist1p = mlistp; } else { mlist2 = mlist; mlist2_sz = mlist_sz; mlist2p = mlistp; } } static void fd_cleanup(void) { if (ifd != -1) { (void) close(ifd); ifd = -1; } if (elffd != -1) { (void) close(elffd); elffd = -1; } }