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: releng/11.0/usr.sbin/asf/asf.c 165372 2006-12-20 06:20:04Z 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
189165372Syarfindmodules(char *path_argv[], const char *sfx[], FILE *out)
190159720Syar{
191159720Syar    char	       *p;
192159720Syar    FTS		       *fts;
193159720Syar    FTSENT	       *ftsent;
194159720Syar    struct kfile       *kfp;
195159720Syar    int			i;
196159720Syar    int			sl;
197159720Syar
198159720Syar    /* Have to fts once per suffix to find preferred suffixes first */
199159720Syar    do {
200159720Syar	sl = *sfx ? strlen(*sfx) : 0;	/* current suffix length */
201159720Syar	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
202159720Syar	if (fts == NULL)
203165372Syar	    err(2, "can't begin traversing module path");
204159720Syar	while ((ftsent = fts_read(fts)) != NULL) {
205159720Syar	    if (ftsent->fts_info == FTS_DNR ||
206159720Syar		ftsent->fts_info == FTS_ERR ||
207159720Syar		ftsent->fts_info == FTS_NS) {
208159720Syar		    errno = ftsent->fts_errno;
209159720Syar		    err(2, "error while traversing path %s", ftsent->fts_path);
210159720Syar	    }
211159720Syar	    if (ftsent->fts_info != FTS_F)
212159720Syar		continue;			/* not a plain file */
213159720Syar
214159720Syar	    if (sl > 0) {
215159720Syar		/* non-blank suffix; see if file name has it */
216159720Syar		i = ftsent->fts_namelen - sl;
217159720Syar		if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0)
218159720Syar		    continue;		/* no such suffix */
219159720Syar		if ((p = strdup(ftsent->fts_name)) == NULL)
220159720Syar		    errx(2, "out of memory");
221159720Syar		p[i] = '\0';		/* remove suffix in the copy */
222159720Syar		kfp = kfile_find(p);
223159720Syar		free(p);
224159720Syar	    } else
225159720Syar		kfp = kfile_find(ftsent->fts_name);
226159720Syar
227159720Syar	    if (kfp && !kfp->seen) {
228159720Syar		doobj(ftsent->fts_path, kfp->addr, out);
229159720Syar		kfp->seen = 1;
230159720Syar		/* Optimization: stop fts as soon as seen all loaded modules */
231159720Syar		if (kfile_allseen()) {
232159720Syar		    fts_close(fts);
233165372Syar		    return;
234159720Syar		}
235159720Syar	    }
236122033Sgreen	}
237159720Syar	if (ftsent == NULL && errno != 0)
238165372Syar	    err(2, "couldn't complete traversing module path");
239159720Syar	fts_close(fts);
240159720Syar    } while (*sfx++);
241122033Sgreen}
242122033Sgreen
243116017Sjmallettstatic void
244116017Sjmallettusage(const char *myname)
245116009Sgrog{
246116009Sgrog    fprintf(stderr,
247116009Sgrog	"Usage:\n"
248162796Sru	"%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n"
249159720Syar	"%*s [modules-path [outfile]]\n\n"
250159720Syar	"\t-a\tappend to outfile\n"
251159720Syar	"\t-f\tfind the module in any subdirectory of modules-path\n"
252159720Syar	"\t-K\tuse kld(2) to get the list of modules\n"
253116016Sjmallett	"\t-k\ttake input from kldstat(8)\n"
254159720Syar	"\t-M\tspecify core name for kvm(3)\n"
255159720Syar	"\t-N\tspecify system name for kvm(3)\n"
256159720Syar	"\t-o\tuse outfile instead of \".asf\"\n"
257122033Sgreen	"\t-s\tdon't prepend subdir for module path\n"
258159720Syar	"\t-V\tuse kvm(3) to get the list of modules\n"
259159720Syar	"\t-X\tappend suffix to list of possible module file name suffixes\n"
260159720Syar	"\t-x\tclear list of possible module file name suffixes\n",
261159776Syar	myname, (int)strlen(myname), "");
262159720Syar    exit(2);
263116009Sgrog}
264116009Sgrog
265165372Syar#define	MAXPATHS	15
266159720Syar#define	MAXSUFFIXES	15
267159720Syar
268159720Syar/* KLD file names end in this */
269159720Syarstatic int	   nsuffixes = 2;
270159720Syarstatic const char *suffixes[MAXSUFFIXES + 1] = {
271159720Syar    ".debug",
272159720Syar    ".symbols",
273159720Syar    NULL
274159720Syar};
275159720Syar
276116009Sgrogint
277116009Sgrogmain(int argc, char *argv[])
278116009Sgrog{
279159720Syar    char basename[PATH_MAX];
280159720Syar    char path[PATH_MAX];
281165372Syar    char *modules_argv[MAXPATHS + 1];
282165372Syar    char *copy, *p;
283165372Syar    char **ap;
284159720Syar    const char *filemode = "w";		/* mode for outfile */
285159720Syar    const char *modules_path = "modules"; /* path to kernel build directory */
286159720Syar    const char *outfile = ".asf";	/* and where to write the output */
287159720Syar    const char *corefile = NULL;	/* for kvm(3) */
288159720Syar    const char *sysfile = NULL;		/* for kvm(3) */
289159720Syar    const char **sfx;
290159720Syar    struct kfile *kfp;
291159720Syar    struct stat st;
292116009Sgrog    FILE *out;				/* output file */
293159720Syar    int dofind = 0;
294159720Syar    int dokld = 0;
295159720Syar    int dokvm = 0;
296159720Syar    int nosubdir = 0;
297159720Syar    int runprog = 0;
298116009Sgrog    int i;
299159720Syar    const int sl = strlen(KLDSUFFIX);
300116009Sgrog
301159720Syar    while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1)
302159720Syar	switch (i) {
303159720Syar	case 'a':
304159720Syar	    filemode = "a";	/* append to outfile */
305159720Syar	    break;
306159720Syar	case 'f':
307159720Syar	    dofind = 1;		/* find .ko (recursively) */
308159720Syar	    break;
309159720Syar	case 'K':
310159720Syar	    dokld = 1;		/* use kld(2) interface */
311159720Syar	    break;
312159720Syar	case 'k':
313159720Syar	    runprog = 1;	/* get input from kldstat(8) */
314159720Syar	    break;
315159720Syar	case 'M':
316159720Syar	    corefile = optarg;	/* core file for kvm(3) */
317159720Syar	    break;
318159720Syar	case 'N':
319159720Syar	    sysfile = optarg;	/* system file (kernel) for kvm(3) */
320159720Syar	    break;
321159720Syar	case 'o':
322159720Syar	    outfile = optarg;	/* output file name */
323159720Syar	    break;
324159720Syar	case 's':
325159720Syar	    nosubdir = 1;	/* don't descend into subdirs */
326159720Syar	    break;
327159720Syar	case 'V':
328159720Syar	    dokvm = 1;		/* use kvm(3) interface */
329159720Syar	    break;
330159720Syar	case 'X':
331159720Syar	    if (nsuffixes >= MAXSUFFIXES)
332159720Syar		errx(2, "only %d suffixes can be specified", MAXSUFFIXES);
333159720Syar	    suffixes[nsuffixes++] = optarg;
334159720Syar	    suffixes[nsuffixes] = NULL;
335159720Syar	    break;
336159720Syar	case 'x':
337159720Syar	    nsuffixes = 0;
338159720Syar	    suffixes[0] = NULL;
339159720Syar	    break;
340159720Syar	default:
341116009Sgrog	    usage(argv[0]);
342116009Sgrog	}
343159720Syar
344159720Syar    argc -= optind;
345159720Syar    argv += optind;
346159720Syar
347159720Syar    if (argc > 0) {
348159720Syar	modules_path = argv[0];
349159720Syar	argc--, argv++;
350116009Sgrog    }
351159720Syar    if (argc > 0) {
352159720Syar	outfile = argv[0];
353159720Syar	argc--, argv++;
354116009Sgrog    }
355159720Syar    if (argc > 0)
356159720Syar	usage(argv[0]);
357116009Sgrog
358159720Syar    if (strcmp(outfile, "-") == 0)
359159720Syar	out = stdout;
360159720Syar    else
361159720Syar	if ((out = fopen(outfile, filemode)) == NULL)
362159720Syar	    err(2, "can't open output file %s", outfile);
363150410Sgrog
364159720Syar    if (dokvm || corefile || sysfile) {
365159720Syar	if (dokld || runprog)
366159720Syar	    warnx("using kvm(3) instead");
367159720Syar	asf_kvm(sysfile, corefile);
368159720Syar    } else if (dokld) {
369159720Syar	if (runprog)
370159720Syar	    warnx("using kld(2) instead");
371159720Syar	asf_kld();
372159720Syar    } else
373159720Syar	asf_prog(runprog);
374116009Sgrog
375159720Syar    /* Avoid long operations like module tree traversal when nothing to do */
376159720Syar    if (kfile_empty()) {
377159720Syar	warnx("no kernel modules loaded");
378159720Syar	return (0);
379159720Syar    }
380159720Syar
381165372Syar    if ((copy = strdup(modules_path)) == NULL)
382165372Syar	errx(2, "out of memory");
383165372Syar    for (
384165372Syar	ap = modules_argv, p = copy;
385165372Syar	(*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS];
386165372Syar	ap++
387165372Syar    );
388165372Syar    if (*ap)
389165372Syar	errx(2, "only %d module path elements can be specified", MAXPATHS);
390165372Syar
391159720Syar    if (!dofind)
392159720Syar	STAILQ_FOREACH(kfp, &kfile_head, link) {
393165372Syar	    for (ap = modules_argv; *ap; ap++) {
394165372Syar		if (!nosubdir) {
395165372Syar		    /* prepare basename of KLD, w/o suffix */
396165372Syar		    strlcpy(basename, kfp->name, sizeof(basename) - 1);
397165372Syar		    i = strlen(basename);
398165372Syar		    if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0)
399165372Syar			i -= sl;
400165372Syar		    basename[i] = '/';
401165372Syar		    basename[i + 1] = '\0';
402122033Sgreen		}
403165372Syar		for (sfx = suffixes;; sfx++) {
404165372Syar		    snprintf(path, sizeof(path),
405165372Syar			     "%s/%s%s%s",
406165372Syar			     *ap,
407165372Syar			     nosubdir ? "" : basename,
408165372Syar			     kfp->name,
409165372Syar			     *sfx ? *sfx : "");
410165372Syar		    if (stat(path, &st) == 0) {
411165372Syar			doobj(path, kfp->addr, out);
412165372Syar			goto found;
413165372Syar		    }
414165372Syar		    if (*sfx == NULL)
415165372Syar			break;
416165372Syar		}
417116009Sgrog	    }
418165372Syar	    warnx("module %s not found in search path", kfp->name);
419165372Syarfound:
420165372Syar	    ;
421116009Sgrog	}
422159720Syar    else
423165372Syar    	findmodules(modules_argv, suffixes, out);
424159720Syar
425165372Syar    free(copy);
426159720Syar    return (0);
427116009Sgrog}
428