asf.c revision 162796
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: head/usr.sbin/asf/asf.c 162796 2006-09-29 16:10:28Z ru $"); 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 189159720Syarfindmodules(const char *modules_path, const char *sfx[], FILE *out) 190159720Syar{ 191159720Syar char *path_argv[2]; 192159720Syar char *p; 193159720Syar FTS *fts; 194159720Syar FTSENT *ftsent; 195159720Syar struct kfile *kfp; 196159720Syar int i; 197159720Syar int sl; 198159720Syar 199159720Syar /* Have to copy modules_path here because it's const */ 200159720Syar if ((path_argv[0] = strdup(modules_path)) == NULL) 201159720Syar errx(2, "out of memory"); 202159720Syar path_argv[1] = NULL; 203159720Syar 204159720Syar /* Have to fts once per suffix to find preferred suffixes first */ 205159720Syar do { 206159720Syar sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */ 207159720Syar fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 208159720Syar if (fts == NULL) 209159720Syar err(2, "can't begin traversing path %s", modules_path); 210159720Syar while ((ftsent = fts_read(fts)) != NULL) { 211159720Syar if (ftsent->fts_info == FTS_DNR || 212159720Syar ftsent->fts_info == FTS_ERR || 213159720Syar ftsent->fts_info == FTS_NS) { 214159720Syar errno = ftsent->fts_errno; 215159720Syar err(2, "error while traversing path %s", ftsent->fts_path); 216159720Syar } 217159720Syar if (ftsent->fts_info != FTS_F) 218159720Syar continue; /* not a plain file */ 219159720Syar 220159720Syar if (sl > 0) { 221159720Syar /* non-blank suffix; see if file name has it */ 222159720Syar i = ftsent->fts_namelen - sl; 223159720Syar if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0) 224159720Syar continue; /* no such suffix */ 225159720Syar if ((p = strdup(ftsent->fts_name)) == NULL) 226159720Syar errx(2, "out of memory"); 227159720Syar p[i] = '\0'; /* remove suffix in the copy */ 228159720Syar kfp = kfile_find(p); 229159720Syar free(p); 230159720Syar } else 231159720Syar kfp = kfile_find(ftsent->fts_name); 232159720Syar 233159720Syar if (kfp && !kfp->seen) { 234159720Syar doobj(ftsent->fts_path, kfp->addr, out); 235159720Syar kfp->seen = 1; 236159720Syar /* Optimization: stop fts as soon as seen all loaded modules */ 237159720Syar if (kfile_allseen()) { 238159720Syar fts_close(fts); 239159720Syar goto done; 240159720Syar } 241159720Syar } 242122033Sgreen } 243159720Syar if (ftsent == NULL && errno != 0) 244159720Syar err(2, "couldn't complete traversing path %s", modules_path); 245159720Syar fts_close(fts); 246159720Syar } while (*sfx++); 247159720Syardone: 248159720Syar free(path_argv[0]); 249122033Sgreen} 250122033Sgreen 251116017Sjmallettstatic void 252116017Sjmallettusage(const char *myname) 253116009Sgrog{ 254116009Sgrog fprintf(stderr, 255116009Sgrog "Usage:\n" 256162796Sru "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n" 257159720Syar "%*s [modules-path [outfile]]\n\n" 258159720Syar "\t-a\tappend to outfile\n" 259159720Syar "\t-f\tfind the module in any subdirectory of modules-path\n" 260159720Syar "\t-K\tuse kld(2) to get the list of modules\n" 261116016Sjmallett "\t-k\ttake input from kldstat(8)\n" 262159720Syar "\t-M\tspecify core name for kvm(3)\n" 263159720Syar "\t-N\tspecify system name for kvm(3)\n" 264159720Syar "\t-o\tuse outfile instead of \".asf\"\n" 265122033Sgreen "\t-s\tdon't prepend subdir for module path\n" 266159720Syar "\t-V\tuse kvm(3) to get the list of modules\n" 267159720Syar "\t-X\tappend suffix to list of possible module file name suffixes\n" 268159720Syar "\t-x\tclear list of possible module file name suffixes\n", 269159776Syar myname, (int)strlen(myname), ""); 270159720Syar exit(2); 271116009Sgrog} 272116009Sgrog 273159720Syar#define MAXSUFFIXES 15 274159720Syar 275159720Syar/* KLD file names end in this */ 276159720Syarstatic int nsuffixes = 2; 277159720Syarstatic const char *suffixes[MAXSUFFIXES + 1] = { 278159720Syar ".debug", 279159720Syar ".symbols", 280159720Syar NULL 281159720Syar}; 282159720Syar 283116009Sgrogint 284116009Sgrogmain(int argc, char *argv[]) 285116009Sgrog{ 286159720Syar char basename[PATH_MAX]; 287159720Syar char path[PATH_MAX]; 288159720Syar const char *filemode = "w"; /* mode for outfile */ 289159720Syar const char *modules_path = "modules"; /* path to kernel build directory */ 290159720Syar const char *outfile = ".asf"; /* and where to write the output */ 291159720Syar const char *corefile = NULL; /* for kvm(3) */ 292159720Syar const char *sysfile = NULL; /* for kvm(3) */ 293159720Syar const char **sfx; 294159720Syar struct kfile *kfp; 295159720Syar struct stat st; 296116009Sgrog FILE *out; /* output file */ 297159720Syar int dofind = 0; 298159720Syar int dokld = 0; 299159720Syar int dokvm = 0; 300159720Syar int nosubdir = 0; 301159720Syar int runprog = 0; 302116009Sgrog int i; 303159720Syar const int sl = strlen(KLDSUFFIX); 304116009Sgrog 305159720Syar while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1) 306159720Syar switch (i) { 307159720Syar case 'a': 308159720Syar filemode = "a"; /* append to outfile */ 309159720Syar break; 310159720Syar case 'f': 311159720Syar dofind = 1; /* find .ko (recursively) */ 312159720Syar break; 313159720Syar case 'K': 314159720Syar dokld = 1; /* use kld(2) interface */ 315159720Syar break; 316159720Syar case 'k': 317159720Syar runprog = 1; /* get input from kldstat(8) */ 318159720Syar break; 319159720Syar case 'M': 320159720Syar corefile = optarg; /* core file for kvm(3) */ 321159720Syar break; 322159720Syar case 'N': 323159720Syar sysfile = optarg; /* system file (kernel) for kvm(3) */ 324159720Syar break; 325159720Syar case 'o': 326159720Syar outfile = optarg; /* output file name */ 327159720Syar break; 328159720Syar case 's': 329159720Syar nosubdir = 1; /* don't descend into subdirs */ 330159720Syar break; 331159720Syar case 'V': 332159720Syar dokvm = 1; /* use kvm(3) interface */ 333159720Syar break; 334159720Syar case 'X': 335159720Syar if (nsuffixes >= MAXSUFFIXES) 336159720Syar errx(2, "only %d suffixes can be specified", MAXSUFFIXES); 337159720Syar suffixes[nsuffixes++] = optarg; 338159720Syar suffixes[nsuffixes] = NULL; 339159720Syar break; 340159720Syar case 'x': 341159720Syar nsuffixes = 0; 342159720Syar suffixes[0] = NULL; 343159720Syar break; 344159720Syar default: 345116009Sgrog usage(argv[0]); 346116009Sgrog } 347159720Syar 348159720Syar argc -= optind; 349159720Syar argv += optind; 350159720Syar 351159720Syar if (argc > 0) { 352159720Syar modules_path = argv[0]; 353159720Syar argc--, argv++; 354116009Sgrog } 355159720Syar if (argc > 0) { 356159720Syar outfile = argv[0]; 357159720Syar argc--, argv++; 358116009Sgrog } 359159720Syar if (argc > 0) 360159720Syar usage(argv[0]); 361116009Sgrog 362159720Syar if (strcmp(outfile, "-") == 0) 363159720Syar out = stdout; 364159720Syar else 365159720Syar if ((out = fopen(outfile, filemode)) == NULL) 366159720Syar err(2, "can't open output file %s", outfile); 367150410Sgrog 368159720Syar if (dokvm || corefile || sysfile) { 369159720Syar if (dokld || runprog) 370159720Syar warnx("using kvm(3) instead"); 371159720Syar asf_kvm(sysfile, corefile); 372159720Syar } else if (dokld) { 373159720Syar if (runprog) 374159720Syar warnx("using kld(2) instead"); 375159720Syar asf_kld(); 376159720Syar } else 377159720Syar asf_prog(runprog); 378116009Sgrog 379159720Syar /* Avoid long operations like module tree traversal when nothing to do */ 380159720Syar if (kfile_empty()) { 381159720Syar warnx("no kernel modules loaded"); 382159720Syar return (0); 383159720Syar } 384159720Syar 385159720Syar if (!dofind) 386159720Syar STAILQ_FOREACH(kfp, &kfile_head, link) { 387159720Syar if (!nosubdir) { 388159720Syar /* prepare basename of KLD, w/o suffix */ 389159720Syar strlcpy(basename, kfp->name, sizeof(basename) - 1); 390159720Syar i = strlen(basename); 391159720Syar if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0) 392159720Syar i -= sl; 393159720Syar basename[i] = '/'; 394159720Syar basename[i + 1] = '\0'; 395116009Sgrog } 396159720Syar for (sfx = suffixes;; sfx++) { 397159720Syar snprintf(path, sizeof(path), 398159720Syar "%s/%s%s%s", 399159720Syar modules_path, 400159720Syar nosubdir ? "" : basename, 401159720Syar kfp->name, 402159720Syar *sfx ? *sfx : ""); 403159720Syar if (*sfx == NULL || stat(path, &st) == 0) { 404159720Syar doobj(path, kfp->addr, out); 405159720Syar break; 406122033Sgreen } 407116009Sgrog } 408116009Sgrog } 409159720Syar else 410159720Syar findmodules(modules_path, suffixes, out); 411159720Syar 412159720Syar return (0); 413116009Sgrog} 414