| /* |
| * 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 <ctype.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <libelf.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <locale.h> |
| #include <wctype.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <door.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <sys/mkdev.h> |
| #include <sys/stat.h> |
| #include <sys/elf.h> |
| #include <procfs.h> |
| #include <sys/core.h> |
| #include <sys/dumphdr.h> |
| #include <netinet/in.h> |
| #include <gelf.h> |
| #include <elfcap.h> |
| #include <sgsrtcid.h> |
| #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 <space> 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:<tab>" */ |
| |
| 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); |
| /* 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); |
| } |
| |
| 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); |
| |
| /* 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); |
| } |
| |
| 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); |
| } |
| |
| |
| 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; |
| } |
| } |