asf.c revision 159720
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 159720 2006-06-18 11:14:40Z 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(const char *modules_path, const char *sfx[], FILE *out) 190{ 191 char *path_argv[2]; 192 char *p; 193 FTS *fts; 194 FTSENT *ftsent; 195 struct kfile *kfp; 196 int i; 197 int sl; 198 199 /* Have to copy modules_path here because it's const */ 200 if ((path_argv[0] = strdup(modules_path)) == NULL) 201 errx(2, "out of memory"); 202 path_argv[1] = NULL; 203 204 /* Have to fts once per suffix to find preferred suffixes first */ 205 do { 206 sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */ 207 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 208 if (fts == NULL) 209 err(2, "can't begin traversing path %s", modules_path); 210 while ((ftsent = fts_read(fts)) != NULL) { 211 if (ftsent->fts_info == FTS_DNR || 212 ftsent->fts_info == FTS_ERR || 213 ftsent->fts_info == FTS_NS) { 214 errno = ftsent->fts_errno; 215 err(2, "error while traversing path %s", ftsent->fts_path); 216 } 217 if (ftsent->fts_info != FTS_F) 218 continue; /* not a plain file */ 219 220 if (sl > 0) { 221 /* non-blank suffix; see if file name has it */ 222 i = ftsent->fts_namelen - sl; 223 if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0) 224 continue; /* no such suffix */ 225 if ((p = strdup(ftsent->fts_name)) == NULL) 226 errx(2, "out of memory"); 227 p[i] = '\0'; /* remove suffix in the copy */ 228 kfp = kfile_find(p); 229 free(p); 230 } else 231 kfp = kfile_find(ftsent->fts_name); 232 233 if (kfp && !kfp->seen) { 234 doobj(ftsent->fts_path, kfp->addr, out); 235 kfp->seen = 1; 236 /* Optimization: stop fts as soon as seen all loaded modules */ 237 if (kfile_allseen()) { 238 fts_close(fts); 239 goto done; 240 } 241 } 242 } 243 if (ftsent == NULL && errno != 0) 244 err(2, "couldn't complete traversing path %s", modules_path); 245 fts_close(fts); 246 } while (*sfx++); 247done: 248 free(path_argv[0]); 249} 250 251static void 252usage(const char *myname) 253{ 254 fprintf(stderr, 255 "Usage:\n" 256 "%s [-afKksVx] [-M core] [-N system ] [-o outfile] [-X suffix]\n" 257 "%*s [modules-path [outfile]]\n\n" 258 "\t-a\tappend to outfile\n" 259 "\t-f\tfind the module in any subdirectory of modules-path\n" 260 "\t-K\tuse kld(2) to get the list of modules\n" 261 "\t-k\ttake input from kldstat(8)\n" 262 "\t-M\tspecify core name for kvm(3)\n" 263 "\t-N\tspecify system name for kvm(3)\n" 264 "\t-o\tuse outfile instead of \".asf\"\n" 265 "\t-s\tdon't prepend subdir for module path\n" 266 "\t-V\tuse kvm(3) to get the list of modules\n" 267 "\t-X\tappend suffix to list of possible module file name suffixes\n" 268 "\t-x\tclear list of possible module file name suffixes\n", 269 myname, strlen(myname), ""); 270 exit(2); 271} 272 273#define MAXSUFFIXES 15 274 275/* KLD file names end in this */ 276static int nsuffixes = 2; 277static const char *suffixes[MAXSUFFIXES + 1] = { 278 ".debug", 279 ".symbols", 280 NULL 281}; 282 283int 284main(int argc, char *argv[]) 285{ 286 char basename[PATH_MAX]; 287 char path[PATH_MAX]; 288 const char *filemode = "w"; /* mode for outfile */ 289 const char *modules_path = "modules"; /* path to kernel build directory */ 290 const char *outfile = ".asf"; /* and where to write the output */ 291 const char *corefile = NULL; /* for kvm(3) */ 292 const char *sysfile = NULL; /* for kvm(3) */ 293 const char **sfx; 294 struct kfile *kfp; 295 struct stat st; 296 FILE *out; /* output file */ 297 int dofind = 0; 298 int dokld = 0; 299 int dokvm = 0; 300 int nosubdir = 0; 301 int runprog = 0; 302 int i; 303 const int sl = strlen(KLDSUFFIX); 304 305 while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1) 306 switch (i) { 307 case 'a': 308 filemode = "a"; /* append to outfile */ 309 break; 310 case 'f': 311 dofind = 1; /* find .ko (recursively) */ 312 break; 313 case 'K': 314 dokld = 1; /* use kld(2) interface */ 315 break; 316 case 'k': 317 runprog = 1; /* get input from kldstat(8) */ 318 break; 319 case 'M': 320 corefile = optarg; /* core file for kvm(3) */ 321 break; 322 case 'N': 323 sysfile = optarg; /* system file (kernel) for kvm(3) */ 324 break; 325 case 'o': 326 outfile = optarg; /* output file name */ 327 break; 328 case 's': 329 nosubdir = 1; /* don't descend into subdirs */ 330 break; 331 case 'V': 332 dokvm = 1; /* use kvm(3) interface */ 333 break; 334 case 'X': 335 if (nsuffixes >= MAXSUFFIXES) 336 errx(2, "only %d suffixes can be specified", MAXSUFFIXES); 337 suffixes[nsuffixes++] = optarg; 338 suffixes[nsuffixes] = NULL; 339 break; 340 case 'x': 341 nsuffixes = 0; 342 suffixes[0] = NULL; 343 break; 344 default: 345 usage(argv[0]); 346 } 347 348 argc -= optind; 349 argv += optind; 350 351 if (argc > 0) { 352 modules_path = argv[0]; 353 argc--, argv++; 354 } 355 if (argc > 0) { 356 outfile = argv[0]; 357 argc--, argv++; 358 } 359 if (argc > 0) 360 usage(argv[0]); 361 362 if (strcmp(outfile, "-") == 0) 363 out = stdout; 364 else 365 if ((out = fopen(outfile, filemode)) == NULL) 366 err(2, "can't open output file %s", outfile); 367 368 if (dokvm || corefile || sysfile) { 369 if (dokld || runprog) 370 warnx("using kvm(3) instead"); 371 asf_kvm(sysfile, corefile); 372 } else if (dokld) { 373 if (runprog) 374 warnx("using kld(2) instead"); 375 asf_kld(); 376 } else 377 asf_prog(runprog); 378 379 /* Avoid long operations like module tree traversal when nothing to do */ 380 if (kfile_empty()) { 381 warnx("no kernel modules loaded"); 382 return (0); 383 } 384 385 if (!dofind) 386 STAILQ_FOREACH(kfp, &kfile_head, link) { 387 if (!nosubdir) { 388 /* prepare basename of KLD, w/o suffix */ 389 strlcpy(basename, kfp->name, sizeof(basename) - 1); 390 i = strlen(basename); 391 if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0) 392 i -= sl; 393 basename[i] = '/'; 394 basename[i + 1] = '\0'; 395 } 396 for (sfx = suffixes;; sfx++) { 397 snprintf(path, sizeof(path), 398 "%s/%s%s%s", 399 modules_path, 400 nosubdir ? "" : basename, 401 kfp->name, 402 *sfx ? *sfx : ""); 403 if (*sfx == NULL || stat(path, &st) == 0) { 404 doobj(path, kfp->addr, out); 405 break; 406 } 407 } 408 } 409 else 410 findmodules(modules_path, suffixes, out); 411 412 return (0); 413} 414