]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/asf/asf.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / asf / asf.c
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$");
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
47 struct kfile {
48     char               *name;
49     caddr_t             addr;
50     int                 seen;
51     STAILQ_ENTRY(kfile) link;
52 };
53
54 static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head);
55
56 void
57 kfile_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
69 static struct kfile *
70 kfile_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
81 static int
82 kfile_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
93 static int
94 kfile_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  */
106 int
107 tokenize(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
146 static void
147 doobj(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
188 static void
189 findmodules(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
243 static void
244 usage(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 */
269 static int         nsuffixes = 2;
270 static const char *suffixes[MAXSUFFIXES + 1] = {
271     ".debug",
272     ".symbols",
273     NULL
274 };
275
276 int
277 main(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);
419 found:
420             ;
421         }
422     else
423         findmodules(modules_argv, suffixes, out);
424
425     free(copy);
426     return (0);
427 }