asf.c revision 159720
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 159720 2006-06-18 11:14:40Z 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
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"
256159720Syar	"%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",
269159720Syar	myname, 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