/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018, Joyent, Inc. */ #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" #include "elf_read.h" /* * 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 ar_coff_or_aout(int ifd); static int type(char *file); static int def_position_tests(char *file); 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 shellscript(char buf[], struct stat64 *sb); static int elf_check(char *file); 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 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); /* from elf_read.c */ int elf_read32(int elffd, Elf_Info *EInfo); int elf_read64(int elffd, Elf_Info *EInfo); #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 */ /* * Static program identifier - used to prevent localization of the name "file" * within individual error messages. */ const char *File = "file"; int main(int argc, char **argv) { char *p; int ch; FILE *fl; int bflg = 0; 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:bcdf:him:")) != EOF) { switch (ch) { case 'M': add_to_mlist(optarg, !dflg); M_flg++; break; case 'b': bflg++; break; case 'c': cflg++; break; case 'd': if (!dflg) { default_magic(); add_to_mlist(dfile, 0); dflg++; } break; case 'f': fflg++; errno = 0; if ((fl = fopen(optarg, "r")) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: cannot " "open file %s: %s\n"), File, optarg, err ? strerror(err) : ""); usage(); } pathlen = pathconf("/", _PC_PATH_MAX); if (pathlen == -1) { int err = errno; (void) fprintf(stderr, gettext("%s: cannot " "determine maximum path length: %s\n"), File, strerror(err)); exit(1); } pathlen += 2; /* for null and newline in fgets */ if ((ap = malloc(pathlen * sizeof (char))) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc " "failed: %s\n"), File, strerror(err)); exit(2); } 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) || (cflg && bflg)) { 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 = malloc(maxmagicoffset)) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"), File, strerror(err)); exit(2); } if (cflg) { f_prtmtab(); if (ferror(stdout) != 0) { (void) fprintf(stderr, gettext("%s: error writing to " "stdout\n"), File); exit(1); } if (fclose(stdout) != 0) { int err = errno; (void) fprintf(stderr, gettext("%s: fclose " "failed: %s\n"), File, strerror(err)); 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]; if (!bflg) 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("%s: error writing to " "stdout\n"), File); exit(1); } if (fclose(stdout) != 0) { int err = errno; (void) fprintf(stderr, gettext("%s: fclose failed: %s\n"), File, strerror(err)); 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) { int err = errno; (void) printf(gettext("cannot open: %s\n"), strerror(err)); 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) { int err = errno; (void) printf(gettext("readlink error: %s\n"), strerror(err)); 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) { int err = errno; (void) printf(gettext("cannot open: %s\n"), strerror(err)); 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) { int err = errno; (void) printf(gettext("cannot open: %s\n"), strerror(err)); (void) close(ifd); ifd = -1; return (0); /* POSIX.2 */ } if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) { int err = errno; (void) printf(gettext("cannot read: %s\n"), strerror(err)); (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) { int err = errno; (void) printf(gettext("cannot read: %s\n"), strerror(err)); 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(file)) { 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(char *file) { 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_check(file) == 0) { (void) putchar('\n'); return (1); } else if (*(int *)fbuf == CORE_MAGIC) { #if !defined(_LP64) struct core *corep = (struct core *)fbuf; #endif (void) printf("a.out core file"); #if !defined(_LP64) if (*(corep->c_cmdname) != '\0') (void) printf(" from '%s'", corep->c_cmdname); #endif (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') && ((uchar_t)fbuf[1] == (uchar_t)'\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); } 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_Info EI) { switch (EI.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; default: break; } } static void print_elf_machine(int machine) { /* * This table must be kept in sync with the EM_ constants * in /usr/include/sys/elf.h. */ static const char *mach_str[EM_NUM] = { [EM_NONE] = "unknown machine", [EM_M32] = "WE32100", [EM_SPARC] = "SPARC", [EM_386] = "80386", [EM_68K] = "M68000", [EM_88K] = "M88000", [EM_486] = "80486", [EM_860] = "i860", [EM_MIPS] = "MIPS RS3000 Big-Endian", [EM_S370] = "S/370", [EM_MIPS_RS3_LE] = "MIPS RS3000 Little-Endian", [EM_RS6000] = "MIPS RS6000", [EM_PA_RISC] = "PA-RISC", [EM_nCUBE] = "nCUBE", [EM_VPP500] = "VPP500", [EM_SPARC32PLUS] = "SPARC32PLUS", [EM_960] = "i960", [EM_PPC] = "PowerPC", [EM_PPC64] = "PowerPC64", [EM_S390] = "S/390", [EM_V800] = "V800", [EM_FR20] = "FR20", [EM_RH32] = "RH32", [EM_RCE] = "RCE", [EM_ARM] = "ARM", [EM_ALPHA] = "Alpha", [EM_SH] = "S/390", [EM_SPARCV9] = "SPARCV9", [EM_TRICORE] = "Tricore", [EM_ARC] = "ARC", [EM_H8_300] = "H8/300", [EM_H8_300H] = "H8/300H", [EM_H8S] = "H8S", [EM_H8_500] = "H8/500", [EM_IA_64] = "IA64", [EM_MIPS_X] = "MIPS-X", [EM_COLDFIRE] = "Coldfire", [EM_68HC12] = "M68HC12", [EM_MMA] = "MMA", [EM_PCP] = "PCP", [EM_NCPU] = "nCPU", [EM_NDR1] = "NDR1", [EM_STARCORE] = "Starcore", [EM_ME16] = "ME16", [EM_ST100] = "ST100", [EM_TINYJ] = "TINYJ", [EM_AMD64] = "AMD64", [EM_PDSP] = "PDSP", [EM_FX66] = "FX66", [EM_ST9PLUS] = "ST9 PLUS", [EM_ST7] = "ST7", [EM_68HC16] = "68HC16", [EM_68HC11] = "68HC11", [EM_68HC08] = "68H08", [EM_68HC05] = "68HC05", [EM_SVX] = "SVX", [EM_ST19] = "ST19", [EM_VAX] = "VAX", [EM_CRIS] = "CRIS", [EM_JAVELIN] = "Javelin", [EM_FIREPATH] = "Firepath", [EM_ZSP] = "ZSP", [EM_MMIX] = "MMIX", [EM_HUANY] = "HUANY", [EM_PRISM] = "Prism", [EM_AVR] = "AVR", [EM_FR30] = "FR30", [EM_D10V] = "D10V", [EM_D30V] = "D30V", [EM_V850] = "V850", [EM_M32R] = "M32R", [EM_MN10300] = "MN10300", [EM_MN10200] = "MN10200", [EM_PJ] = "picoJava", [EM_OPENRISC] = "OpenRISC", [EM_ARC_A5] = "Tangent-A5", [EM_XTENSA] = "Xtensa", [EM_VIDEOCORE] = "Videocore", [EM_TMM_GPP] = "TMM_GPP", [EM_NS32K] = "NS32K", [EM_TPC] = "TPC", [EM_SNP1K] = "SNP1K", [EM_ST200] = "ST200", [EM_IP2K] = "IP2K", [EM_MAX] = "MAX", [EM_CR] = "CompactRISC", [EM_F2MC16] = "F2MC16", [EM_MSP430] = "MSP430", [EM_BLACKFIN] = "Blackfin", [EM_SE_C33] = "S1C33", [EM_SEP] = "SEP", [EM_ARCA] = "Arca", [EM_UNICORE] = "Unicore", [EM_EXCESS] = "eXcess", [EM_DXP] = "DXP", [EM_ALTERA_NIOS2] = "Nios 2", [EM_CRX] = "CompactRISC CRX", [EM_XGATE] = "XGATE", [EM_C166] = "C16x/XC16x", [EM_M16C] = "M16C", [EM_DSPIC30F] = "dsPIC30F", [EM_CE] = "CE RISC", [EM_M32C] = "M32C", [EM_TSK3000] = "TSK3000", [EM_RS08] = "RS08", [EM_SHARC] = "SHARC", [EM_ECOG2] = "eCOG2", [EM_SCORE7] = "SCORE7", [EM_DSP24] = "DSP24", [EM_VIDEOCORE3] = "Videocore III", [EM_LATTICEMICO32] = "LATTICEMICO32", [EM_SE_C17] = "SE_C17", [EM_TI_C6000] = "TMS320C6000", [EM_TI_C2000] = "TMS320C2000", [EM_TI_C5500] = "TMS320C55x", [EM_TI_ARP32] = "ASRP32", [EM_TI_PRU] = "TI_PRU", [EM_MMDSP_PLUS] = "MMDSP_PLUS", [EM_CYPRESS_M8C] = "M8C", [EM_R32C] = "R32C", [EM_TRIMEDIA] = "TriMedia", [EM_QDSP6] = "QDSP6", [EM_8051] = "8051", [EM_STXP7X] = "STxP7x", [EM_NDS32] = "NDS32", [EM_ECOG1] = "eCOG1X", [EM_MAXQ30] = "MAXQ30", [EM_XIMO16] = "XIMO16", [EM_MANIK] = "M2000", [EM_CRAYNV2] = "CRAYNV2", [EM_RX] = "RX", [EM_METAG] = "METAG", [EM_MCST_ELBRUS] = "Elbrus", [EM_ECOG16] = "eCOG16", [EM_CR16] = "CR16", [EM_ETPU] = "ETPU", [EM_SLE9X] = "SLE9X", [EM_L10M] = "L10M", [EM_K10M] = "K10M", [EM_AARCH64] = "aarch64", [EM_AVR32] = "AVR32", [EM_STM8] = "STM8", [EM_TILE64] = "TILE64", [EM_TILEPRO] = "TILEPRO", [EM_MICROBLAZE] = "MicroBlaze", [EM_CUDA] = "CUDA", [EM_TILEGX] = "TILE-Gx", [EM_CLOUDSHIELD] = "CloudShield", [EM_COREA_1ST] = "CORE-A 1st", [EM_COREA_2ND] = "CORE-A 2nd", [EM_ARC_COMPACT2] = "ARCompact V2", [EM_OPEN8] = "Open8", [EM_RL78] = "RL78", [EM_VIDEOCORE5] = "VideoCore V", [EM_78KOR] = "78KOR", [EM_56800EX] = "56800EX", [EM_BA1] = "BA1", [EM_BA2] = "BA2", [EM_XCORE] = "xCORE", [EM_MCHP_PIC] = "MCHP_PIC", [EM_KM32] = "KM32", [EM_KMX32] = "KMX32", [EM_KMX16] = "KMX16", [EM_KMX8] = "KMX8", [EM_KVARC] = "KVARC", [EM_CDP] = "CDP", [EM_COGE] = "COGE", [EM_COOL] = "CoolEngine", [EM_NORC] = "NORC", [EM_CSR_KALIMBA] = "Kalimba", [EM_Z80] = "Zilog Z80", [EM_VISIUM] = "VISIUMcore", [EM_FT32] = "FT32", [EM_MOXIE] = "Moxie", [EM_AMDGPU] = "AMD GPU", [EM_RISCV] = "RISC-V" }; /* If new machine is added, refuse to compile until we're updated */ #if EM_NUM != 244 #error "Number of known ELF machine constants has changed" #endif const char *str; if ((machine < EM_NONE) || (machine >= EM_NUM)) machine = EM_NONE; str = mach_str[machine]; if (str) (void) printf(" %s", str); } static void print_elf_datatype(int datatype) { switch (datatype) { case ELFDATA2LSB: (void) printf(" LSB"); break; case ELFDATA2MSB: (void) printf(" 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(Elf_Info EI) { unsigned int flags; flags = EI.flags; switch (EI.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; } } /* * check_ident: checks the ident field of the presumeably * elf file. If check fails, this is not an * elf file. */ static int check_ident(unsigned char *ident, int fd) { int class; if (pread64(fd, ident, EI_NIDENT, 0) != EI_NIDENT) return (ELF_READ_FAIL); class = ident[EI_CLASS]; if (class != ELFCLASS32 && class != ELFCLASS64) return (ELF_READ_FAIL); if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) return (ELF_READ_FAIL); return (ELF_READ_OKAY); } static int elf_check(char *file) { Elf_Info EInfo; int class, version, format; unsigned char ident[EI_NIDENT]; (void) memset(&EInfo, 0, sizeof (Elf_Info)); EInfo.file = file; /* * Verify information in file indentifier. * Return quietly if not elf; Different type of file. */ if (check_ident(ident, elffd) == ELF_READ_FAIL) return (1); /* * Read the elf headers for processing and get the * get the needed information in Elf_Info struct. */ class = ident[EI_CLASS]; if (class == ELFCLASS32) { if (elf_read32(elffd, &EInfo) == ELF_READ_FAIL) { (void) fprintf(stderr, gettext("%s: %s: can't " "read ELF header\n"), File, file); return (1); } } else if (class == ELFCLASS64) { if (elf_read64(elffd, &EInfo) == ELF_READ_FAIL) { (void) fprintf(stderr, gettext("%s: %s: can't " "read ELF header\n"), File, file); return (1); } } else { /* something wrong */ return (1); } /* version not in ident then 1 */ version = ident[EI_VERSION] ? ident[EI_VERSION] : 1; format = ident[EI_DATA]; (void) printf("%s", gettext("ELF")); print_elf_class(class); print_elf_datatype(format); print_elf_type(EInfo); if (EInfo.core_type != EC_NOTCORE) { /* Print what kind of core is this */ if (EInfo.core_type == EC_OLDCORE) (void) printf(" %s", gettext("pre-2.6 core file")); else (void) printf(" %s", gettext("core file")); } /* Print machine info */ print_elf_machine(EInfo.machine); /* Print Version */ if (version == 1) (void) printf(" %s %d", gettext("Version"), version); if (EInfo.kmod) { (void) printf(", %s", gettext("kernel module")); } /* Print Flags */ print_elf_flags(EInfo); /* Last bit, if it is a core */ if (EInfo.core_type != EC_NOTCORE) { /* Print the program name that dumped this core */ (void) printf(gettext(", from '%s'"), EInfo.fname); return (0); } /* Print Capabilities */ if (EInfo.cap_str[0] != '\0') (void) printf(" [%s]", EInfo.cap_str); if ((EInfo.type != ET_EXEC) && (EInfo.type != ET_DYN)) return (0); /* Print if it is dynamically linked */ if (EInfo.dynamic) (void) printf(gettext(", dynamically linked")); else (void) printf(gettext(", statically linked")); /* Printf it it is stripped */ if (EInfo.stripped & E_SYMTAB) { (void) printf(gettext(", not stripped")); if (!(EInfo.stripped & E_DBGINF)) { (void) printf(gettext( ", no debugging information available")); } } else { (void) printf(gettext(", stripped")); } return (0); } /* * 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")); 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); } /* look for "1hddddd" where d is a digit */ static int sccs(void) { 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); } 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 archive file")); 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)) { const char *c = swap(dh.dump_flags) & DF_COMPRESSED ? "compressed " : ""; const char *l = swap(dh.dump_flags) & DF_LIVE ? "live" : "crash"; (void) printf(gettext( "%s %s %s %u-bit %s %s%s dump from '%s'\n"), dh.dump_utsname.sysname, dh.dump_utsname.release, dh.dump_utsname.version, swap(dh.dump_wordsize), isa, c, l, 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 [-bdh] [-M mfile] [-m mfile] [-f ffile] file ...\n" " file [-bdh] [-M mfile] [-m mfile] -f ffile\n" " file -i [-bh] [-f ffile] file ...\n" " file -i [-bh] -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. */ int is_in_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; debug_sections[i] != NULL; i++) { if (strncmp(debug_sections[i], str, strlen(debug_sections[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 = malloc(strlen(msg_locale) + 35)) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"), File, strerror(err)); 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 = calloc(MLIST_SZ, sizeof (char *))) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc " "failed: %s\n"), File, strerror(err)); exit(2); } mlist_sz = MLIST_SZ; mlistp = mlist; } if ((mlistp - mlist) >= mlist_sz) { mlistp_off = mlistp - mlist; mlist_sz *= 2; if ((mlist = realloc(mlist, mlist_sz * sizeof (char *))) == NULL) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc " "failed: %s\n"), File, strerror(err)); 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) { int err = errno; (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"), File, strerror(err)); 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; } }