diff options
Diffstat (limited to 'usr.sbin/asf/asf.c')
-rw-r--r-- | usr.sbin/asf/asf.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/usr.sbin/asf/asf.c b/usr.sbin/asf/asf.c new file mode 100644 index 0000000..c913e3d --- /dev/null +++ b/usr.sbin/asf/asf.c @@ -0,0 +1,427 @@ +/*- + * Copyright (c) 2002, 2003 Greg Lehey + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * This software is provided by the author ``as is'' and any express + * or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose + * are disclaimed. In no event shall the author be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, + * whether in contract, strict liability, or tort (including + * negligence or otherwise) arising in any way out of the use of this + * software, even if advised of the possibility of such damage. + */ +/* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asf.h" + +struct kfile { + char *name; + caddr_t addr; + int seen; + STAILQ_ENTRY(kfile) link; +}; + +static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head); + +void +kfile_add(const char *name, caddr_t addr) +{ + struct kfile *kfp; + + if ((kfp = malloc(sizeof(*kfp))) == NULL || + (kfp->name = strdup(name)) == NULL) + errx(2, "out of memory"); + kfp->addr = addr; + kfp->seen = 0; + STAILQ_INSERT_TAIL(&kfile_head, kfp, link); +} + +static struct kfile * +kfile_find(const char *name) +{ + struct kfile *kfp; + + STAILQ_FOREACH(kfp, &kfile_head, link) + if (strcmp(kfp->name, name) == 0) + return (kfp); /* found */ + + return (NULL); /* not found */ +} + +static int +kfile_allseen(void) +{ + struct kfile *kfp; + + STAILQ_FOREACH(kfp, &kfile_head, link) + if (!kfp->seen) + return (0); /* at least one unseen */ + + return (1); /* all seen */ +} + +static int +kfile_empty(void) +{ + return (STAILQ_EMPTY(&kfile_head)); +} + +/* + * Take a blank separated list of tokens and turn it into a list of + * individual nul-delimited strings. Build a list of pointers at + * token, which must have enough space for the tokens. Return the + * number of tokens, or -1 on error (typically a missing string + * delimiter). + */ +int +tokenize(char *cptr, char *token[], int maxtoken) +{ + char delim; /* delimiter to search for */ + int tokennr; /* index of this token */ + + for (tokennr = 0; tokennr < maxtoken;) { + while (isspace(*cptr)) + cptr++; /* skip initial white space */ + if ((*cptr == '\0') || (*cptr == '\n') + || (*cptr == '#')) /* end of line */ + return tokennr; /* return number of tokens found */ + delim = *cptr; + token[tokennr] = cptr; /* point to it */ + tokennr++; /* one more */ + if (tokennr == maxtoken) /* run off the end? */ + return tokennr; + if ((delim == '\'') || (delim == '"')) { /* delimitered */ + for (;;) { + cptr++; + if ((*cptr == delim) + && (cptr[-1] != '\\')) { /* found the partner */ + cptr++; /* move on past */ + if (!isspace(*cptr)) /* no space after closing quote */ + return -1; + *cptr++ = '\0'; /* delimit */ + } else if ((*cptr == '\0') + || (*cptr == '\n')) /* end of line */ + return -1; + } + } else { /* not quoted */ + while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n')) + cptr++; + if (*cptr != '\0') /* not end of the line, */ + *cptr++ = '\0'; /* delimit and move to the next */ + } + } + return maxtoken; /* can't get here */ +} + +static void +doobj(const char *path, caddr_t addr, FILE *out) +{ + uintmax_t base = (uintptr_t)addr; + uintmax_t textaddr = 0; + uintmax_t dataaddr = 0; + uintmax_t bssaddr = 0; + uintmax_t *up; + int octokens; + char *octoken[MAXTOKEN]; + char ocbuf[LINE_MAX + PATH_MAX]; + FILE *objcopy; + + snprintf(ocbuf, sizeof(ocbuf), + "/usr/bin/objdump --section-headers %s", path); + if ((objcopy = popen(ocbuf, "r")) == NULL) + err(2, "can't start %s", ocbuf); + while (fgets(ocbuf, sizeof(ocbuf), objcopy)) { + octokens = tokenize(ocbuf, octoken, MAXTOKEN); + if (octokens <= 1) + continue; + up = NULL; + if (strcmp(octoken[1], ".text") == 0) + up = &textaddr; + else if (strcmp(octoken[1], ".data") == 0) + up = &dataaddr; + else if (strcmp(octoken[1], ".bss") == 0) + up = &bssaddr; + if (up == NULL) + continue; + *up = strtoumax(octoken[3], NULL, 16) + base; + } + if (textaddr) { /* we must have a text address */ + fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr); + if (dataaddr) + fprintf(out, " -s .data 0x%jx", dataaddr); + if (bssaddr) + fprintf(out, " -s .bss 0x%jx", bssaddr); + fprintf(out, "\n"); + } +} + +static void +findmodules(char *path_argv[], const char *sfx[], FILE *out) +{ + char *p; + FTS *fts; + FTSENT *ftsent; + struct kfile *kfp; + int i; + int sl; + + /* Have to fts once per suffix to find preferred suffixes first */ + do { + sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */ + fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); + if (fts == NULL) + err(2, "can't begin traversing module path"); + while ((ftsent = fts_read(fts)) != NULL) { + if (ftsent->fts_info == FTS_DNR || + ftsent->fts_info == FTS_ERR || + ftsent->fts_info == FTS_NS) { + errno = ftsent->fts_errno; + err(2, "error while traversing path %s", ftsent->fts_path); + } + if (ftsent->fts_info != FTS_F) + continue; /* not a plain file */ + + if (sl > 0) { + /* non-blank suffix; see if file name has it */ + i = ftsent->fts_namelen - sl; + if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0) + continue; /* no such suffix */ + if ((p = strdup(ftsent->fts_name)) == NULL) + errx(2, "out of memory"); + p[i] = '\0'; /* remove suffix in the copy */ + kfp = kfile_find(p); + free(p); + } else + kfp = kfile_find(ftsent->fts_name); + + if (kfp && !kfp->seen) { + doobj(ftsent->fts_path, kfp->addr, out); + kfp->seen = 1; + /* Optimization: stop fts as soon as seen all loaded modules */ + if (kfile_allseen()) { + fts_close(fts); + return; + } + } + } + if (ftsent == NULL && errno != 0) + err(2, "couldn't complete traversing module path"); + fts_close(fts); + } while (*sfx++); +} + +static void +usage(const char *myname) +{ + fprintf(stderr, + "Usage:\n" + "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n" + "%*s [modules-path [outfile]]\n\n" + "\t-a\tappend to outfile\n" + "\t-f\tfind the module in any subdirectory of modules-path\n" + "\t-K\tuse kld(2) to get the list of modules\n" + "\t-k\ttake input from kldstat(8)\n" + "\t-M\tspecify core name for kvm(3)\n" + "\t-N\tspecify system name for kvm(3)\n" + "\t-o\tuse outfile instead of \".asf\"\n" + "\t-s\tdon't prepend subdir for module path\n" + "\t-V\tuse kvm(3) to get the list of modules\n" + "\t-X\tappend suffix to list of possible module file name suffixes\n" + "\t-x\tclear list of possible module file name suffixes\n", + myname, (int)strlen(myname), ""); + exit(2); +} + +#define MAXPATHS 15 +#define MAXSUFFIXES 15 + +/* KLD file names end in this */ +static int nsuffixes = 2; +static const char *suffixes[MAXSUFFIXES + 1] = { + ".debug", + ".symbols", + NULL +}; + +int +main(int argc, char *argv[]) +{ + char basename[PATH_MAX]; + char path[PATH_MAX]; + char *modules_argv[MAXPATHS + 1]; + char *copy, *p; + char **ap; + const char *filemode = "w"; /* mode for outfile */ + const char *modules_path = "modules"; /* path to kernel build directory */ + const char *outfile = ".asf"; /* and where to write the output */ + const char *corefile = NULL; /* for kvm(3) */ + const char *sysfile = NULL; /* for kvm(3) */ + const char **sfx; + struct kfile *kfp; + struct stat st; + FILE *out; /* output file */ + int dofind = 0; + int dokld = 0; + int dokvm = 0; + int nosubdir = 0; + int runprog = 0; + int i; + const int sl = strlen(KLDSUFFIX); + + while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1) + switch (i) { + case 'a': + filemode = "a"; /* append to outfile */ + break; + case 'f': + dofind = 1; /* find .ko (recursively) */ + break; + case 'K': + dokld = 1; /* use kld(2) interface */ + break; + case 'k': + runprog = 1; /* get input from kldstat(8) */ + break; + case 'M': + corefile = optarg; /* core file for kvm(3) */ + break; + case 'N': + sysfile = optarg; /* system file (kernel) for kvm(3) */ + break; + case 'o': + outfile = optarg; /* output file name */ + break; + case 's': + nosubdir = 1; /* don't descend into subdirs */ + break; + case 'V': + dokvm = 1; /* use kvm(3) interface */ + break; + case 'X': + if (nsuffixes >= MAXSUFFIXES) + errx(2, "only %d suffixes can be specified", MAXSUFFIXES); + suffixes[nsuffixes++] = optarg; + suffixes[nsuffixes] = NULL; + break; + case 'x': + nsuffixes = 0; + suffixes[0] = NULL; + break; + default: + usage(argv[0]); + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + modules_path = argv[0]; + argc--, argv++; + } + if (argc > 0) { + outfile = argv[0]; + argc--, argv++; + } + if (argc > 0) + usage(argv[0]); + + if (strcmp(outfile, "-") == 0) + out = stdout; + else + if ((out = fopen(outfile, filemode)) == NULL) + err(2, "can't open output file %s", outfile); + + if (dokvm || corefile || sysfile) { + if (dokld || runprog) + warnx("using kvm(3) instead"); + asf_kvm(sysfile, corefile); + } else if (dokld) { + if (runprog) + warnx("using kld(2) instead"); + asf_kld(); + } else + asf_prog(runprog); + + /* Avoid long operations like module tree traversal when nothing to do */ + if (kfile_empty()) { + warnx("no kernel modules loaded"); + return (0); + } + + if ((copy = strdup(modules_path)) == NULL) + errx(2, "out of memory"); + for ( + ap = modules_argv, p = copy; + (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS]; + ap++ + ); + if (*ap) + errx(2, "only %d module path elements can be specified", MAXPATHS); + + if (!dofind) + STAILQ_FOREACH(kfp, &kfile_head, link) { + for (ap = modules_argv; *ap; ap++) { + if (!nosubdir) { + /* prepare basename of KLD, w/o suffix */ + strlcpy(basename, kfp->name, sizeof(basename) - 1); + i = strlen(basename); + if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0) + i -= sl; + basename[i] = '/'; + basename[i + 1] = '\0'; + } + for (sfx = suffixes;; sfx++) { + snprintf(path, sizeof(path), + "%s/%s%s%s", + *ap, + nosubdir ? "" : basename, + kfp->name, + *sfx ? *sfx : ""); + if (stat(path, &st) == 0) { + doobj(path, kfp->addr, out); + goto found; + } + if (*sfx == NULL) + break; + } + } + warnx("module %s not found in search path", kfp->name); +found: + ; + } + else + findmodules(modules_argv, suffixes, out); + + free(copy); + return (0); +} |