asf.c revision 165372
1/*- 2 * Copyright (c) 2002, 2003 Greg Lehey 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * This software is provided by the author ``as is'' and any express 15 * or implied warranties, including, but not limited to, the implied 16 * warranties of merchantability and fitness for a particular purpose 17 * are disclaimed. In no event shall the author be liable for any 18 * direct, indirect, incidental, special, exemplary, or consequential 19 * damages (including, but not limited to, procurement of substitute 20 * goods or services; loss of use, data, or profits; or business 21 * interruption) however caused and on any theory of liability, 22 * whether in contract, strict liability, or tort (including 23 * negligence or otherwise) arising in any way out of the use of this 24 * software, even if advised of the possibility of such damage. 25 */ 26/* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/usr.sbin/asf/asf.c 165372 2006-12-20 06:20:04Z yar $"); 30 31#include <sys/types.h> 32#include <sys/queue.h> 33#include <sys/stat.h> 34#include <ctype.h> 35#include <err.h> 36#include <errno.h> 37#include <fts.h> 38#include <inttypes.h> 39#include <limits.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44 45#include "asf.h" 46 47struct kfile { 48 char *name; 49 caddr_t addr; 50 int seen; 51 STAILQ_ENTRY(kfile) link; 52}; 53 54static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head); 55 56void 57kfile_add(const char *name, caddr_t addr) 58{ 59 struct kfile *kfp; 60 61 if ((kfp = malloc(sizeof(*kfp))) == NULL || 62 (kfp->name = strdup(name)) == NULL) 63 errx(2, "out of memory"); 64 kfp->addr = addr; 65 kfp->seen = 0; 66 STAILQ_INSERT_TAIL(&kfile_head, kfp, link); 67} 68 69static struct kfile * 70kfile_find(const char *name) 71{ 72 struct kfile *kfp; 73 74 STAILQ_FOREACH(kfp, &kfile_head, link) 75 if (strcmp(kfp->name, name) == 0) 76 return (kfp); /* found */ 77 78 return (NULL); /* not found */ 79} 80 81static int 82kfile_allseen(void) 83{ 84 struct kfile *kfp; 85 86 STAILQ_FOREACH(kfp, &kfile_head, link) 87 if (!kfp->seen) 88 return (0); /* at least one unseen */ 89 90 return (1); /* all seen */ 91} 92 93static int 94kfile_empty(void) 95{ 96 return (STAILQ_EMPTY(&kfile_head)); 97} 98 99/* 100 * Take a blank separated list of tokens and turn it into a list of 101 * individual nul-delimited strings. Build a list of pointers at 102 * token, which must have enough space for the tokens. Return the 103 * number of tokens, or -1 on error (typically a missing string 104 * delimiter). 105 */ 106int 107tokenize(char *cptr, char *token[], int maxtoken) 108{ 109 char delim; /* delimiter to search for */ 110 int tokennr; /* index of this token */ 111 112 for (tokennr = 0; tokennr < maxtoken;) { 113 while (isspace(*cptr)) 114 cptr++; /* skip initial white space */ 115 if ((*cptr == '\0') || (*cptr == '\n') 116 || (*cptr == '#')) /* end of line */ 117 return tokennr; /* return number of tokens found */ 118 delim = *cptr; 119 token[tokennr] = cptr; /* point to it */ 120 tokennr++; /* one more */ 121 if (tokennr == maxtoken) /* run off the end? */ 122 return tokennr; 123 if ((delim == '\'') || (delim == '"')) { /* delimitered */ 124 for (;;) { 125 cptr++; 126 if ((*cptr == delim) 127 && (cptr[-1] != '\\')) { /* found the partner */ 128 cptr++; /* move on past */ 129 if (!isspace(*cptr)) /* no space after closing quote */ 130 return -1; 131 *cptr++ = '\0'; /* delimit */ 132 } else if ((*cptr == '\0') 133 || (*cptr == '\n')) /* end of line */ 134 return -1; 135 } 136 } else { /* not quoted */ 137 while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n')) 138 cptr++; 139 if (*cptr != '\0') /* not end of the line, */ 140 *cptr++ = '\0'; /* delimit and move to the next */ 141 } 142 } 143 return maxtoken; /* can't get here */ 144} 145 146static void 147doobj(const char *path, caddr_t addr, FILE *out) 148{ 149 uintmax_t base = (uintptr_t)addr; 150 uintmax_t textaddr = 0; 151 uintmax_t dataaddr = 0; 152 uintmax_t bssaddr = 0; 153 uintmax_t *up; 154 int octokens; 155 char *octoken[MAXTOKEN]; 156 char ocbuf[LINE_MAX + PATH_MAX]; 157 FILE *objcopy; 158 159 snprintf(ocbuf, sizeof(ocbuf), 160 "/usr/bin/objdump --section-headers %s", path); 161 if ((objcopy = popen(ocbuf, "r")) == NULL) 162 err(2, "can't start %s", ocbuf); 163 while (fgets(ocbuf, sizeof(ocbuf), objcopy)) { 164 octokens = tokenize(ocbuf, octoken, MAXTOKEN); 165 if (octokens <= 1) 166 continue; 167 up = NULL; 168 if (strcmp(octoken[1], ".text") == 0) 169 up = &textaddr; 170 else if (strcmp(octoken[1], ".data") == 0) 171 up = &dataaddr; 172 else if (strcmp(octoken[1], ".bss") == 0) 173 up = &bssaddr; 174 if (up == NULL) 175 continue; 176 *up = strtoumax(octoken[3], NULL, 16) + base; 177 } 178 if (textaddr) { /* we must have a text address */ 179 fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr); 180 if (dataaddr) 181 fprintf(out, " -s .data 0x%jx", dataaddr); 182 if (bssaddr) 183 fprintf(out, " -s .bss 0x%jx", bssaddr); 184 fprintf(out, "\n"); 185 } 186} 187 188static void 189findmodules(char *path_argv[], const char *sfx[], FILE *out) 190{ 191 char *p; 192 FTS *fts; 193 FTSENT *ftsent; 194 struct kfile *kfp; 195 int i; 196 int sl; 197 198 /* Have to fts once per suffix to find preferred suffixes first */ 199 do { 200 sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */ 201 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 202 if (fts == NULL) 203 err(2, "can't begin traversing module path"); 204 while ((ftsent = fts_read(fts)) != NULL) { 205 if (ftsent->fts_info == FTS_DNR || 206 ftsent->fts_info == FTS_ERR || 207 ftsent->fts_info == FTS_NS) { 208 errno = ftsent->fts_errno; 209 err(2, "error while traversing path %s", ftsent->fts_path); 210 } 211 if (ftsent->fts_info != FTS_F) 212 continue; /* not a plain file */ 213 214 if (sl > 0) { 215 /* non-blank suffix; see if file name has it */ 216 i = ftsent->fts_namelen - sl; 217 if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0) 218 continue; /* no such suffix */ 219 if ((p = strdup(ftsent->fts_name)) == NULL) 220 errx(2, "out of memory"); 221 p[i] = '\0'; /* remove suffix in the copy */ 222 kfp = kfile_find(p); 223 free(p); 224 } else 225 kfp = kfile_find(ftsent->fts_name); 226 227 if (kfp && !kfp->seen) { 228 doobj(ftsent->fts_path, kfp->addr, out); 229 kfp->seen = 1; 230 /* Optimization: stop fts as soon as seen all loaded modules */ 231 if (kfile_allseen()) { 232 fts_close(fts); 233 return; 234 } 235 } 236 } 237 if (ftsent == NULL && errno != 0) 238 err(2, "couldn't complete traversing module path"); 239 fts_close(fts); 240 } while (*sfx++); 241} 242 243static void 244usage(const char *myname) 245{ 246 fprintf(stderr, 247 "Usage:\n" 248 "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n" 249 "%*s [modules-path [outfile]]\n\n" 250 "\t-a\tappend to outfile\n" 251 "\t-f\tfind the module in any subdirectory of modules-path\n" 252 "\t-K\tuse kld(2) to get the list of modules\n" 253 "\t-k\ttake input from kldstat(8)\n" 254 "\t-M\tspecify core name for kvm(3)\n" 255 "\t-N\tspecify system name for kvm(3)\n" 256 "\t-o\tuse outfile instead of \".asf\"\n" 257 "\t-s\tdon't prepend subdir for module path\n" 258 "\t-V\tuse kvm(3) to get the list of modules\n" 259 "\t-X\tappend suffix to list of possible module file name suffixes\n" 260 "\t-x\tclear list of possible module file name suffixes\n", 261 myname, (int)strlen(myname), ""); 262 exit(2); 263} 264 265#define MAXPATHS 15 266#define MAXSUFFIXES 15 267 268/* KLD file names end in this */ 269static int nsuffixes = 2; 270static const char *suffixes[MAXSUFFIXES + 1] = { 271 ".debug", 272 ".symbols", 273 NULL 274}; 275 276int 277main(int argc, char *argv[]) 278{ 279 char basename[PATH_MAX]; 280 char path[PATH_MAX]; 281 char *modules_argv[MAXPATHS + 1]; 282 char *copy, *p; 283 char **ap; 284 const char *filemode = "w"; /* mode for outfile */ 285 const char *modules_path = "modules"; /* path to kernel build directory */ 286 const char *outfile = ".asf"; /* and where to write the output */ 287 const char *corefile = NULL; /* for kvm(3) */ 288 const char *sysfile = NULL; /* for kvm(3) */ 289 const char **sfx; 290 struct kfile *kfp; 291 struct stat st; 292 FILE *out; /* output file */ 293 int dofind = 0; 294 int dokld = 0; 295 int dokvm = 0; 296 int nosubdir = 0; 297 int runprog = 0; 298 int i; 299 const int sl = strlen(KLDSUFFIX); 300 301 while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1) 302 switch (i) { 303 case 'a': 304 filemode = "a"; /* append to outfile */ 305 break; 306 case 'f': 307 dofind = 1; /* find .ko (recursively) */ 308 break; 309 case 'K': 310 dokld = 1; /* use kld(2) interface */ 311 break; 312 case 'k': 313 runprog = 1; /* get input from kldstat(8) */ 314 break; 315 case 'M': 316 corefile = optarg; /* core file for kvm(3) */ 317 break; 318 case 'N': 319 sysfile = optarg; /* system file (kernel) for kvm(3) */ 320 break; 321 case 'o': 322 outfile = optarg; /* output file name */ 323 break; 324 case 's': 325 nosubdir = 1; /* don't descend into subdirs */ 326 break; 327 case 'V': 328 dokvm = 1; /* use kvm(3) interface */ 329 break; 330 case 'X': 331 if (nsuffixes >= MAXSUFFIXES) 332 errx(2, "only %d suffixes can be specified", MAXSUFFIXES); 333 suffixes[nsuffixes++] = optarg; 334 suffixes[nsuffixes] = NULL; 335 break; 336 case 'x': 337 nsuffixes = 0; 338 suffixes[0] = NULL; 339 break; 340 default: 341 usage(argv[0]); 342 } 343 344 argc -= optind; 345 argv += optind; 346 347 if (argc > 0) { 348 modules_path = argv[0]; 349 argc--, argv++; 350 } 351 if (argc > 0) { 352 outfile = argv[0]; 353 argc--, argv++; 354 } 355 if (argc > 0) 356 usage(argv[0]); 357 358 if (strcmp(outfile, "-") == 0) 359 out = stdout; 360 else 361 if ((out = fopen(outfile, filemode)) == NULL) 362 err(2, "can't open output file %s", outfile); 363 364 if (dokvm || corefile || sysfile) { 365 if (dokld || runprog) 366 warnx("using kvm(3) instead"); 367 asf_kvm(sysfile, corefile); 368 } else if (dokld) { 369 if (runprog) 370 warnx("using kld(2) instead"); 371 asf_kld(); 372 } else 373 asf_prog(runprog); 374 375 /* Avoid long operations like module tree traversal when nothing to do */ 376 if (kfile_empty()) { 377 warnx("no kernel modules loaded"); 378 return (0); 379 } 380 381 if ((copy = strdup(modules_path)) == NULL) 382 errx(2, "out of memory"); 383 for ( 384 ap = modules_argv, p = copy; 385 (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS]; 386 ap++ 387 ); 388 if (*ap) 389 errx(2, "only %d module path elements can be specified", MAXPATHS); 390 391 if (!dofind) 392 STAILQ_FOREACH(kfp, &kfile_head, link) { 393 for (ap = modules_argv; *ap; ap++) { 394 if (!nosubdir) { 395 /* prepare basename of KLD, w/o suffix */ 396 strlcpy(basename, kfp->name, sizeof(basename) - 1); 397 i = strlen(basename); 398 if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0) 399 i -= sl; 400 basename[i] = '/'; 401 basename[i + 1] = '\0'; 402 } 403 for (sfx = suffixes;; sfx++) { 404 snprintf(path, sizeof(path), 405 "%s/%s%s%s", 406 *ap, 407 nosubdir ? "" : basename, 408 kfp->name, 409 *sfx ? *sfx : ""); 410 if (stat(path, &st) == 0) { 411 doobj(path, kfp->addr, out); 412 goto found; 413 } 414 if (*sfx == NULL) 415 break; 416 } 417 } 418 warnx("module %s not found in search path", kfp->name); 419found: 420 ; 421 } 422 else 423 findmodules(modules_argv, suffixes, out); 424 425 free(copy); 426 return (0); 427} 428