1159720Syar/*- 2116014Sgrog * Copyright (c) 2002, 2003 Greg Lehey 3116014Sgrog * All rights reserved. 4116014Sgrog * 5116014Sgrog * Redistribution and use in source and binary forms, with or without 6116014Sgrog * modification, are permitted provided that the following conditions 7116014Sgrog * are met: 8116014Sgrog * 1. Redistributions of source code must retain the above copyright 9116014Sgrog * notice, this list of conditions and the following disclaimer. 10116014Sgrog * 2. Redistributions in binary form must reproduce the above copyright 11116014Sgrog * notice, this list of conditions and the following disclaimer in the 12116014Sgrog * documentation and/or other materials provided with the distribution. 13116014Sgrog * 14116014Sgrog * This software is provided by the author ``as is'' and any express 15116014Sgrog * or implied warranties, including, but not limited to, the implied 16116014Sgrog * warranties of merchantability and fitness for a particular purpose 17116014Sgrog * are disclaimed. In no event shall the author be liable for any 18116014Sgrog * direct, indirect, incidental, special, exemplary, or consequential 19116014Sgrog * damages (including, but not limited to, procurement of substitute 20116014Sgrog * goods or services; loss of use, data, or profits; or business 21116014Sgrog * interruption) however caused and on any theory of liability, 22116014Sgrog * whether in contract, strict liability, or tort (including 23116014Sgrog * negligence or otherwise) arising in any way out of the use of this 24116014Sgrog * software, even if advised of the possibility of such damage. 25116014Sgrog */ 26116009Sgrog/* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */ 27116009Sgrog 28159720Syar#include <sys/cdefs.h> 29159720Syar__FBSDID("$FreeBSD: releng/11.0/usr.sbin/asf/asf.c 165372 2006-12-20 06:20:04Z yar $"); 30159720Syar 31159720Syar#include <sys/types.h> 32159720Syar#include <sys/queue.h> 33159720Syar#include <sys/stat.h> 34116009Sgrog#include <ctype.h> 35159720Syar#include <err.h> 36116009Sgrog#include <errno.h> 37159720Syar#include <fts.h> 38159720Syar#include <inttypes.h> 39159720Syar#include <limits.h> 40116009Sgrog#include <stdio.h> 41116009Sgrog#include <stdlib.h> 42116009Sgrog#include <string.h> 43116009Sgrog#include <unistd.h> 44116009Sgrog 45159720Syar#include "asf.h" 46116009Sgrog 47159720Syarstruct kfile { 48159720Syar char *name; 49159720Syar caddr_t addr; 50159720Syar int seen; 51159720Syar STAILQ_ENTRY(kfile) link; 52159720Syar}; 53159720Syar 54159720Syarstatic STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head); 55159720Syar 56159720Syarvoid 57159720Syarkfile_add(const char *name, caddr_t addr) 58159720Syar{ 59159720Syar struct kfile *kfp; 60159720Syar 61159720Syar if ((kfp = malloc(sizeof(*kfp))) == NULL || 62159720Syar (kfp->name = strdup(name)) == NULL) 63159720Syar errx(2, "out of memory"); 64159720Syar kfp->addr = addr; 65159720Syar kfp->seen = 0; 66159720Syar STAILQ_INSERT_TAIL(&kfile_head, kfp, link); 67159720Syar} 68159720Syar 69159720Syarstatic struct kfile * 70159720Syarkfile_find(const char *name) 71159720Syar{ 72159720Syar struct kfile *kfp; 73159720Syar 74159720Syar STAILQ_FOREACH(kfp, &kfile_head, link) 75159720Syar if (strcmp(kfp->name, name) == 0) 76159720Syar return (kfp); /* found */ 77159720Syar 78159720Syar return (NULL); /* not found */ 79159720Syar} 80159720Syar 81159720Syarstatic int 82159720Syarkfile_allseen(void) 83159720Syar{ 84159720Syar struct kfile *kfp; 85159720Syar 86159720Syar STAILQ_FOREACH(kfp, &kfile_head, link) 87159720Syar if (!kfp->seen) 88159720Syar return (0); /* at least one unseen */ 89159720Syar 90159720Syar return (1); /* all seen */ 91159720Syar} 92159720Syar 93159720Syarstatic int 94159720Syarkfile_empty(void) 95159720Syar{ 96159720Syar return (STAILQ_EMPTY(&kfile_head)); 97159720Syar} 98159720Syar 99116009Sgrog/* 100116009Sgrog * Take a blank separated list of tokens and turn it into a list of 101116009Sgrog * individual nul-delimited strings. Build a list of pointers at 102116009Sgrog * token, which must have enough space for the tokens. Return the 103116009Sgrog * number of tokens, or -1 on error (typically a missing string 104116009Sgrog * delimiter). 105116009Sgrog */ 106159720Syarint 107116009Sgrogtokenize(char *cptr, char *token[], int maxtoken) 108116009Sgrog{ 109116009Sgrog char delim; /* delimiter to search for */ 110116009Sgrog int tokennr; /* index of this token */ 111116009Sgrog 112116009Sgrog for (tokennr = 0; tokennr < maxtoken;) { 113116009Sgrog while (isspace(*cptr)) 114116009Sgrog cptr++; /* skip initial white space */ 115116009Sgrog if ((*cptr == '\0') || (*cptr == '\n') 116116009Sgrog || (*cptr == '#')) /* end of line */ 117116009Sgrog return tokennr; /* return number of tokens found */ 118116009Sgrog delim = *cptr; 119116009Sgrog token[tokennr] = cptr; /* point to it */ 120116009Sgrog tokennr++; /* one more */ 121116009Sgrog if (tokennr == maxtoken) /* run off the end? */ 122116009Sgrog return tokennr; 123116009Sgrog if ((delim == '\'') || (delim == '"')) { /* delimitered */ 124116009Sgrog for (;;) { 125116009Sgrog cptr++; 126116009Sgrog if ((*cptr == delim) 127116009Sgrog && (cptr[-1] != '\\')) { /* found the partner */ 128116009Sgrog cptr++; /* move on past */ 129116009Sgrog if (!isspace(*cptr)) /* no space after closing quote */ 130116009Sgrog return -1; 131116009Sgrog *cptr++ = '\0'; /* delimit */ 132116009Sgrog } else if ((*cptr == '\0') 133116009Sgrog || (*cptr == '\n')) /* end of line */ 134116009Sgrog return -1; 135116009Sgrog } 136116009Sgrog } else { /* not quoted */ 137116009Sgrog while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n')) 138116009Sgrog cptr++; 139116009Sgrog if (*cptr != '\0') /* not end of the line, */ 140116009Sgrog *cptr++ = '\0'; /* delimit and move to the next */ 141116009Sgrog } 142116009Sgrog } 143116009Sgrog return maxtoken; /* can't get here */ 144116009Sgrog} 145116009Sgrog 146159720Syarstatic void 147159720Syardoobj(const char *path, caddr_t addr, FILE *out) 148122033Sgreen{ 149159720Syar uintmax_t base = (uintptr_t)addr; 150159720Syar uintmax_t textaddr = 0; 151159720Syar uintmax_t dataaddr = 0; 152159720Syar uintmax_t bssaddr = 0; 153159720Syar uintmax_t *up; 154159720Syar int octokens; 155159720Syar char *octoken[MAXTOKEN]; 156159720Syar char ocbuf[LINE_MAX + PATH_MAX]; 157159720Syar FILE *objcopy; 158122033Sgreen 159159720Syar snprintf(ocbuf, sizeof(ocbuf), 160159720Syar "/usr/bin/objdump --section-headers %s", path); 161159720Syar if ((objcopy = popen(ocbuf, "r")) == NULL) 162159720Syar err(2, "can't start %s", ocbuf); 163159720Syar while (fgets(ocbuf, sizeof(ocbuf), objcopy)) { 164159720Syar octokens = tokenize(ocbuf, octoken, MAXTOKEN); 165159720Syar if (octokens <= 1) 166159720Syar continue; 167159720Syar up = NULL; 168159720Syar if (strcmp(octoken[1], ".text") == 0) 169159720Syar up = &textaddr; 170159720Syar else if (strcmp(octoken[1], ".data") == 0) 171159720Syar up = &dataaddr; 172159720Syar else if (strcmp(octoken[1], ".bss") == 0) 173159720Syar up = &bssaddr; 174159720Syar if (up == NULL) 175159720Syar continue; 176159720Syar *up = strtoumax(octoken[3], NULL, 16) + base; 177122033Sgreen } 178159720Syar if (textaddr) { /* we must have a text address */ 179159720Syar fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr); 180159720Syar if (dataaddr) 181159720Syar fprintf(out, " -s .data 0x%jx", dataaddr); 182159720Syar if (bssaddr) 183159720Syar fprintf(out, " -s .bss 0x%jx", bssaddr); 184159720Syar fprintf(out, "\n"); 185122033Sgreen } 186159720Syar} 187159720Syar 188159720Syarstatic void 189165372Syarfindmodules(char *path_argv[], const char *sfx[], FILE *out) 190159720Syar{ 191159720Syar char *p; 192159720Syar FTS *fts; 193159720Syar FTSENT *ftsent; 194159720Syar struct kfile *kfp; 195159720Syar int i; 196159720Syar int sl; 197159720Syar 198159720Syar /* Have to fts once per suffix to find preferred suffixes first */ 199159720Syar do { 200159720Syar sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */ 201159720Syar fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 202159720Syar if (fts == NULL) 203165372Syar err(2, "can't begin traversing module path"); 204159720Syar while ((ftsent = fts_read(fts)) != NULL) { 205159720Syar if (ftsent->fts_info == FTS_DNR || 206159720Syar ftsent->fts_info == FTS_ERR || 207159720Syar ftsent->fts_info == FTS_NS) { 208159720Syar errno = ftsent->fts_errno; 209159720Syar err(2, "error while traversing path %s", ftsent->fts_path); 210159720Syar } 211159720Syar if (ftsent->fts_info != FTS_F) 212159720Syar continue; /* not a plain file */ 213159720Syar 214159720Syar if (sl > 0) { 215159720Syar /* non-blank suffix; see if file name has it */ 216159720Syar i = ftsent->fts_namelen - sl; 217159720Syar if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0) 218159720Syar continue; /* no such suffix */ 219159720Syar if ((p = strdup(ftsent->fts_name)) == NULL) 220159720Syar errx(2, "out of memory"); 221159720Syar p[i] = '\0'; /* remove suffix in the copy */ 222159720Syar kfp = kfile_find(p); 223159720Syar free(p); 224159720Syar } else 225159720Syar kfp = kfile_find(ftsent->fts_name); 226159720Syar 227159720Syar if (kfp && !kfp->seen) { 228159720Syar doobj(ftsent->fts_path, kfp->addr, out); 229159720Syar kfp->seen = 1; 230159720Syar /* Optimization: stop fts as soon as seen all loaded modules */ 231159720Syar if (kfile_allseen()) { 232159720Syar fts_close(fts); 233165372Syar return; 234159720Syar } 235159720Syar } 236122033Sgreen } 237159720Syar if (ftsent == NULL && errno != 0) 238165372Syar err(2, "couldn't complete traversing module path"); 239159720Syar fts_close(fts); 240159720Syar } while (*sfx++); 241122033Sgreen} 242122033Sgreen 243116017Sjmallettstatic void 244116017Sjmallettusage(const char *myname) 245116009Sgrog{ 246116009Sgrog fprintf(stderr, 247116009Sgrog "Usage:\n" 248162796Sru "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n" 249159720Syar "%*s [modules-path [outfile]]\n\n" 250159720Syar "\t-a\tappend to outfile\n" 251159720Syar "\t-f\tfind the module in any subdirectory of modules-path\n" 252159720Syar "\t-K\tuse kld(2) to get the list of modules\n" 253116016Sjmallett "\t-k\ttake input from kldstat(8)\n" 254159720Syar "\t-M\tspecify core name for kvm(3)\n" 255159720Syar "\t-N\tspecify system name for kvm(3)\n" 256159720Syar "\t-o\tuse outfile instead of \".asf\"\n" 257122033Sgreen "\t-s\tdon't prepend subdir for module path\n" 258159720Syar "\t-V\tuse kvm(3) to get the list of modules\n" 259159720Syar "\t-X\tappend suffix to list of possible module file name suffixes\n" 260159720Syar "\t-x\tclear list of possible module file name suffixes\n", 261159776Syar myname, (int)strlen(myname), ""); 262159720Syar exit(2); 263116009Sgrog} 264116009Sgrog 265165372Syar#define MAXPATHS 15 266159720Syar#define MAXSUFFIXES 15 267159720Syar 268159720Syar/* KLD file names end in this */ 269159720Syarstatic int nsuffixes = 2; 270159720Syarstatic const char *suffixes[MAXSUFFIXES + 1] = { 271159720Syar ".debug", 272159720Syar ".symbols", 273159720Syar NULL 274159720Syar}; 275159720Syar 276116009Sgrogint 277116009Sgrogmain(int argc, char *argv[]) 278116009Sgrog{ 279159720Syar char basename[PATH_MAX]; 280159720Syar char path[PATH_MAX]; 281165372Syar char *modules_argv[MAXPATHS + 1]; 282165372Syar char *copy, *p; 283165372Syar char **ap; 284159720Syar const char *filemode = "w"; /* mode for outfile */ 285159720Syar const char *modules_path = "modules"; /* path to kernel build directory */ 286159720Syar const char *outfile = ".asf"; /* and where to write the output */ 287159720Syar const char *corefile = NULL; /* for kvm(3) */ 288159720Syar const char *sysfile = NULL; /* for kvm(3) */ 289159720Syar const char **sfx; 290159720Syar struct kfile *kfp; 291159720Syar struct stat st; 292116009Sgrog FILE *out; /* output file */ 293159720Syar int dofind = 0; 294159720Syar int dokld = 0; 295159720Syar int dokvm = 0; 296159720Syar int nosubdir = 0; 297159720Syar int runprog = 0; 298116009Sgrog int i; 299159720Syar const int sl = strlen(KLDSUFFIX); 300116009Sgrog 301159720Syar while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1) 302159720Syar switch (i) { 303159720Syar case 'a': 304159720Syar filemode = "a"; /* append to outfile */ 305159720Syar break; 306159720Syar case 'f': 307159720Syar dofind = 1; /* find .ko (recursively) */ 308159720Syar break; 309159720Syar case 'K': 310159720Syar dokld = 1; /* use kld(2) interface */ 311159720Syar break; 312159720Syar case 'k': 313159720Syar runprog = 1; /* get input from kldstat(8) */ 314159720Syar break; 315159720Syar case 'M': 316159720Syar corefile = optarg; /* core file for kvm(3) */ 317159720Syar break; 318159720Syar case 'N': 319159720Syar sysfile = optarg; /* system file (kernel) for kvm(3) */ 320159720Syar break; 321159720Syar case 'o': 322159720Syar outfile = optarg; /* output file name */ 323159720Syar break; 324159720Syar case 's': 325159720Syar nosubdir = 1; /* don't descend into subdirs */ 326159720Syar break; 327159720Syar case 'V': 328159720Syar dokvm = 1; /* use kvm(3) interface */ 329159720Syar break; 330159720Syar case 'X': 331159720Syar if (nsuffixes >= MAXSUFFIXES) 332159720Syar errx(2, "only %d suffixes can be specified", MAXSUFFIXES); 333159720Syar suffixes[nsuffixes++] = optarg; 334159720Syar suffixes[nsuffixes] = NULL; 335159720Syar break; 336159720Syar case 'x': 337159720Syar nsuffixes = 0; 338159720Syar suffixes[0] = NULL; 339159720Syar break; 340159720Syar default: 341116009Sgrog usage(argv[0]); 342116009Sgrog } 343159720Syar 344159720Syar argc -= optind; 345159720Syar argv += optind; 346159720Syar 347159720Syar if (argc > 0) { 348159720Syar modules_path = argv[0]; 349159720Syar argc--, argv++; 350116009Sgrog } 351159720Syar if (argc > 0) { 352159720Syar outfile = argv[0]; 353159720Syar argc--, argv++; 354116009Sgrog } 355159720Syar if (argc > 0) 356159720Syar usage(argv[0]); 357116009Sgrog 358159720Syar if (strcmp(outfile, "-") == 0) 359159720Syar out = stdout; 360159720Syar else 361159720Syar if ((out = fopen(outfile, filemode)) == NULL) 362159720Syar err(2, "can't open output file %s", outfile); 363150410Sgrog 364159720Syar if (dokvm || corefile || sysfile) { 365159720Syar if (dokld || runprog) 366159720Syar warnx("using kvm(3) instead"); 367159720Syar asf_kvm(sysfile, corefile); 368159720Syar } else if (dokld) { 369159720Syar if (runprog) 370159720Syar warnx("using kld(2) instead"); 371159720Syar asf_kld(); 372159720Syar } else 373159720Syar asf_prog(runprog); 374116009Sgrog 375159720Syar /* Avoid long operations like module tree traversal when nothing to do */ 376159720Syar if (kfile_empty()) { 377159720Syar warnx("no kernel modules loaded"); 378159720Syar return (0); 379159720Syar } 380159720Syar 381165372Syar if ((copy = strdup(modules_path)) == NULL) 382165372Syar errx(2, "out of memory"); 383165372Syar for ( 384165372Syar ap = modules_argv, p = copy; 385165372Syar (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS]; 386165372Syar ap++ 387165372Syar ); 388165372Syar if (*ap) 389165372Syar errx(2, "only %d module path elements can be specified", MAXPATHS); 390165372Syar 391159720Syar if (!dofind) 392159720Syar STAILQ_FOREACH(kfp, &kfile_head, link) { 393165372Syar for (ap = modules_argv; *ap; ap++) { 394165372Syar if (!nosubdir) { 395165372Syar /* prepare basename of KLD, w/o suffix */ 396165372Syar strlcpy(basename, kfp->name, sizeof(basename) - 1); 397165372Syar i = strlen(basename); 398165372Syar if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0) 399165372Syar i -= sl; 400165372Syar basename[i] = '/'; 401165372Syar basename[i + 1] = '\0'; 402122033Sgreen } 403165372Syar for (sfx = suffixes;; sfx++) { 404165372Syar snprintf(path, sizeof(path), 405165372Syar "%s/%s%s%s", 406165372Syar *ap, 407165372Syar nosubdir ? "" : basename, 408165372Syar kfp->name, 409165372Syar *sfx ? *sfx : ""); 410165372Syar if (stat(path, &st) == 0) { 411165372Syar doobj(path, kfp->addr, out); 412165372Syar goto found; 413165372Syar } 414165372Syar if (*sfx == NULL) 415165372Syar break; 416165372Syar } 417116009Sgrog } 418165372Syar warnx("module %s not found in search path", kfp->name); 419165372Syarfound: 420165372Syar ; 421116009Sgrog } 422159720Syar else 423165372Syar findmodules(modules_argv, suffixes, out); 424159720Syar 425165372Syar free(copy); 426159720Syar return (0); 427116009Sgrog} 428