asf.c revision 150410
1116014Sgrog/*
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/* $FreeBSD: head/usr.sbin/asf/asf.c 150410 2005-09-21 05:20:03Z grog $ */
28116009Sgrog
29116009Sgrog#define MAXLINE 1024
30116009Sgrog#include <ctype.h>
31116009Sgrog#include <errno.h>
32116009Sgrog#include <stdio.h>
33116009Sgrog#include <stdlib.h>
34116009Sgrog#include <string.h>
35116009Sgrog#include <sys/file.h>
36116009Sgrog#include <sys/param.h>
37116009Sgrog#include <sys/stat.h>
38116009Sgrog#include <sys/wait.h>
39116009Sgrog#include <sys/types.h>
40122033Sgreen#include <fts.h>
41116009Sgrog#include <unistd.h>
42116009Sgrog
43116009Sgrog#define MAXTOKEN 10
44116017Sjmallettconst char *modules_path;		/* path relative to kernel
45116009Sgrog					 * build directory */
46116017Sjmallettconst char *outfile;			/* and where to write the output */
47116009Sgrog
48116009Sgrog/*
49116009Sgrog * Take a blank separated list of tokens and turn it into a list of
50116009Sgrog * individual nul-delimited strings.  Build a list of pointers at
51116009Sgrog * token, which must have enough space for the tokens.  Return the
52116009Sgrog * number of tokens, or -1 on error (typically a missing string
53116009Sgrog * delimiter).
54116009Sgrog */
55116017Sjmallettstatic int
56116009Sgrogtokenize(char *cptr, char *token[], int maxtoken)
57116009Sgrog{
58116009Sgrog    char delim;				/* delimiter to search for */
59116009Sgrog    int tokennr;			/* index of this token */
60116009Sgrog
61116009Sgrog    for (tokennr = 0; tokennr < maxtoken;) {
62116009Sgrog	while (isspace(*cptr))
63116009Sgrog	    cptr++;			/* skip initial white space */
64116009Sgrog	if ((*cptr == '\0') || (*cptr == '\n')
65116009Sgrog	    || (*cptr == '#'))		/* end of line */
66116009Sgrog	    return tokennr;		/* return number of tokens found */
67116009Sgrog	delim = *cptr;
68116009Sgrog	token[tokennr] = cptr;		/* point to it */
69116009Sgrog	tokennr++;			/* one more */
70116009Sgrog	if (tokennr == maxtoken)	/* run off the end? */
71116009Sgrog	    return tokennr;
72116009Sgrog	if ((delim == '\'') || (delim == '"')) { /* delimitered */
73116009Sgrog	    for (;;) {
74116009Sgrog		cptr++;
75116009Sgrog		if ((*cptr == delim)
76116009Sgrog		    && (cptr[-1] != '\\')) { /* found the partner */
77116009Sgrog		    cptr++;		/* move on past */
78116009Sgrog		    if (!isspace(*cptr)) /* no space after closing quote */
79116009Sgrog			return -1;
80116009Sgrog		    *cptr++ = '\0';	/* delimit */
81116009Sgrog		} else if ((*cptr == '\0')
82116009Sgrog		    || (*cptr == '\n'))	/* end of line */
83116009Sgrog		    return -1;
84116009Sgrog	    }
85116009Sgrog	} else {			/* not quoted */
86116009Sgrog	    while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
87116009Sgrog		cptr++;
88116009Sgrog	    if (*cptr != '\0')		/* not end of the line, */
89116009Sgrog		*cptr++ = '\0';		/* delimit and move to the next */
90116009Sgrog	}
91116009Sgrog    }
92116009Sgrog    return maxtoken;			/* can't get here */
93116009Sgrog}
94116009Sgrog
95122033Sgreenstatic char *
96122033Sgreenfindmodule(char *modules_path, const char *module_name)
97122033Sgreen{
98122033Sgreen    char *const path_argv[2] = { modules_path, NULL };
99122033Sgreen    char *module_path = NULL;
100122033Sgreen    int module_name_len = strlen(module_name);
101122033Sgreen    FTS *fts;
102122033Sgreen    FTSENT *ftsent;
103122033Sgreen
104122033Sgreen    if (modules_path == NULL) {
105122033Sgreen	fprintf(stderr,
106122033Sgreen	    "Can't allocate memory to traverse a path: %s (%d)\n",
107122033Sgreen	    strerror(errno),
108122033Sgreen	    errno);
109122033Sgreen	exit(1);
110122033Sgreen    }
111122033Sgreen    fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
112122033Sgreen    if (fts == NULL) {
113122033Sgreen	fprintf(stderr,
114122033Sgreen	    "Can't begin traversing path %s: %s (%d)\n",
115122033Sgreen	    modules_path,
116122033Sgreen	    strerror(errno),
117122033Sgreen	    errno);
118122033Sgreen	exit(1);
119122033Sgreen    }
120122033Sgreen    while ((ftsent = fts_read(fts)) != NULL) {
121122033Sgreen	if (ftsent->fts_info == FTS_DNR ||
122122033Sgreen	    ftsent->fts_info == FTS_ERR ||
123122033Sgreen	    ftsent->fts_info == FTS_NS) {
124122033Sgreen	    fprintf(stderr,
125122033Sgreen		"Error while traversing path %s: %s (%d)\n",
126122033Sgreen		modules_path,
127122033Sgreen		strerror(errno),
128122033Sgreen		errno);
129122033Sgreen	    exit(1);
130122033Sgreen	}
131122033Sgreen	if (ftsent->fts_info != FTS_F ||
132122033Sgreen	    ftsent->fts_namelen != module_name_len ||
133122033Sgreen	    memcmp(module_name, ftsent->fts_name, module_name_len) != 0)
134122033Sgreen		continue;
135122033Sgreen	if (asprintf(&module_path,
136122033Sgreen	    "%.*s",
137122033Sgreen	    ftsent->fts_pathlen,
138122033Sgreen	    ftsent->fts_path) == -1) {
139122033Sgreen	    fprintf(stderr,
140122033Sgreen		"Can't allocate memory traversing path %s: %s (%d)\n",
141122033Sgreen		modules_path,
142122033Sgreen		strerror(errno),
143122033Sgreen		errno);
144122033Sgreen	    exit(1);
145122033Sgreen	}
146122033Sgreen	break;
147122033Sgreen    }
148122033Sgreen    if (ftsent == NULL && errno != 0) {
149122033Sgreen	fprintf(stderr,
150122033Sgreen	    "Couldn't complete traversing path %s: %s (%d)\n",
151122033Sgreen	    modules_path,
152122033Sgreen	    strerror(errno),
153122033Sgreen	    errno);
154122033Sgreen	exit(1);
155122033Sgreen    }
156122033Sgreen    fts_close(fts);
157122033Sgreen    free(modules_path);
158122033Sgreen    return (module_path);
159122033Sgreen}
160122033Sgreen
161116017Sjmallettstatic void
162116017Sjmallettusage(const char *myname)
163116009Sgrog{
164116009Sgrog    fprintf(stderr,
165116009Sgrog	"Usage:\n"
166122033Sgreen	"%s [-a] [-f] [-k] [-s] [-x] [modules-path [outfile]]\n\n"
167116009Sgrog	"\t-a\tappend to outfile)\n"
168122033Sgreen	"\t-f\tfind the module in any subdirectory of module-path\n"
169116016Sjmallett	"\t-k\ttake input from kldstat(8)\n"
170122033Sgreen	"\t-s\tdon't prepend subdir for module path\n"
171116009Sgrog	"\t-x\tdon't append \".debug\" to module name\n",
172116009Sgrog	myname);
173116009Sgrog}
174116009Sgrog
175116009Sgrogint
176116009Sgrogmain(int argc, char *argv[])
177116009Sgrog{
178116009Sgrog    char buf[MAXLINE];
179116009Sgrog    FILE *kldstat;
180116009Sgrog    FILE *objcopy;
181116009Sgrog    FILE *out;				/* output file */
182116009Sgrog    char ocbuf[MAXLINE];
183116009Sgrog    int tokens;				/* number of tokens on line */
184116009Sgrog    char basetoken[MAXLINE];
185116009Sgrog    int i;
186116017Sjmallett    const char *filemode = "w";		/* mode for outfile */
187116009Sgrog    char cwd[MAXPATHLEN];		/* current directory */
188116017Sjmallett    const char *debugname = ".debug";	/* some file names end in this */
189116017Sjmallett    char *token[MAXTOKEN];
190120430Ssimokawa    int nosubdir = 0;
191122033Sgreen    int dofind = 0;
192116009Sgrog
193116009Sgrog    getcwd(cwd, MAXPATHLEN);		/* find where we are */
194116009Sgrog    kldstat = stdin;
195116009Sgrog    for (i = 1; i < argc; i++) {
196116009Sgrog	if (argv[i][0] == '-') {
197116009Sgrog	    if (strcmp(argv[i], "-k") == 0) { /* get input from kldstat(8) */
198116009Sgrog		if (!(kldstat = popen("kldstat", "r"))) {
199116009Sgrog		    perror("Can't start kldstat");
200116009Sgrog		    return 1;
201116009Sgrog		}
202116009Sgrog	    } else if (strcmp(argv[i], "-a") == 0) /* append to outfile */
203116009Sgrog		filemode = "a";
204116009Sgrog	    else if (strcmp(argv[i], "-x") == 0) /* no .debug extension */
205116009Sgrog		debugname = "";		/* nothing */
206120430Ssimokawa	    else if (strcmp(argv[i], "-s") == 0) /* no subdir */
207120430Ssimokawa		nosubdir = 1;		/* nothing */
208122033Sgreen	    else if (strcmp(argv[i], "-f") == 0) /* find .ko (recursively) */
209122033Sgreen		dofind = 1;
210116009Sgrog	    else {
211116009Sgrog		fprintf(stderr,
212116009Sgrog		    "Invalid option: %s, aborting\n",
213116009Sgrog		    argv[i]);
214116009Sgrog		usage(argv[0]);
215116009Sgrog		return 1;
216116009Sgrog	    }
217116009Sgrog	} else if (modules_path == NULL)
218116009Sgrog	    modules_path = argv[i];
219116009Sgrog	else if (outfile == NULL)
220116009Sgrog	    outfile = argv[i];
221116009Sgrog	else {
222116009Sgrog	    fprintf(stderr,
223116009Sgrog		"Extraneous startup information: \"%s\", aborting\n",
224116009Sgrog		argv[i]);
225116009Sgrog	    usage(argv[0]);
226116009Sgrog	    return 1;
227116009Sgrog	}
228116009Sgrog    }
229116009Sgrog    if (modules_path == NULL)
230116009Sgrog	modules_path = "modules";
231116009Sgrog    if (outfile == NULL)
232116009Sgrog	outfile = ".asf";
233116009Sgrog    if ((out = fopen(outfile, filemode)) == NULL) {
234116009Sgrog	fprintf(stderr,
235116009Sgrog	    "Can't open output file %s: %s (%d)\n",
236116009Sgrog	    outfile,
237116009Sgrog	    strerror(errno),
238116009Sgrog	    errno);
239116009Sgrog	return 1;
240116009Sgrog    }
241116009Sgrog    while (fgets(buf, MAXLINE, kldstat)) {
242116009Sgrog	if ((!(strstr(buf, "kernel")))
243116009Sgrog	    && buf[0] != 'I') {
244116009Sgrog	    quad_t base;
245116009Sgrog	    quad_t textaddr;
246116009Sgrog	    quad_t dataaddr;
247116009Sgrog	    quad_t bssaddr;
248116009Sgrog
249116009Sgrog	    tokens = tokenize(buf, token, MAXTOKEN);
250150410Sgrog           if (tokens < 4)
251150410Sgrog		continue;
252116009Sgrog	    base = strtoll(token[2], NULL, 16);
253122033Sgreen	    if (!dofind) {
254122033Sgreen		strcpy(basetoken, token[4]);
255122033Sgreen		basetoken[strlen(basetoken) - 3] = '/';
256122033Sgreen		basetoken[strlen(basetoken) - 2] = '\0'; /* cut off the .ko */
257122033Sgreen		snprintf(ocbuf,
258122033Sgreen		    MAXLINE,
259122033Sgreen		    "/usr/bin/objdump --section-headers %s/%s%s%s",
260122033Sgreen		    modules_path,
261122033Sgreen		    nosubdir ? "" : basetoken,
262122033Sgreen		    token[4],
263122033Sgreen		    debugname);
264122033Sgreen	    } else {
265122033Sgreen		char *modpath;
266150410Sgrog
267122033Sgreen		modpath = findmodule(strdup(modules_path), token[4]);
268122033Sgreen		if (modpath == NULL)
269122033Sgreen		    continue;
270122033Sgreen		snprintf(ocbuf,
271122033Sgreen		    MAXLINE,
272122033Sgreen		    "/usr/bin/objdump --section-headers %s%s",
273122033Sgreen		    modpath,
274122033Sgreen		    debugname);
275122033Sgreen		free(modpath);
276122033Sgreen	    }
277116009Sgrog	    if (!(objcopy = popen(ocbuf, "r"))) {
278116009Sgrog		fprintf(stderr,
279116009Sgrog		    "Can't start %s: %s (%d)\n",
280116009Sgrog		    ocbuf,
281116009Sgrog		    strerror(errno),
282116009Sgrog		    errno);
283116009Sgrog		return 1;
284116009Sgrog	    }
285116009Sgrog	    while (fgets(ocbuf, MAXLINE, objcopy)) {
286116009Sgrog		int octokens;
287116009Sgrog		char *octoken[MAXTOKEN];
288116009Sgrog
289116009Sgrog		octokens = tokenize(ocbuf, octoken, MAXTOKEN);
290116009Sgrog		if (octokens > 1) {
291116009Sgrog		    if (!strcmp(octoken[1], ".text"))
292116009Sgrog			textaddr = strtoll(octoken[3], NULL, 16) + base;
293116009Sgrog		    else if (!strcmp(octoken[1], ".data"))
294116009Sgrog			dataaddr = strtoll(octoken[3], NULL, 16) + base;
295116009Sgrog		    else if (!strcmp(octoken[1], ".bss"))
296116009Sgrog			bssaddr = strtoll(octoken[3], NULL, 16) + base;
297116009Sgrog		}
298116009Sgrog	    }
299116009Sgrog	    if (textaddr) {		/* we must have a text address */
300122033Sgreen		if (!dofind) {
301122033Sgreen		    fprintf(out,
302122033Sgreen			"add-symbol-file %s/%s/%s%s%s 0x%llx",
303122033Sgreen			cwd,
304122033Sgreen			modules_path,
305122033Sgreen			nosubdir ? "" : basetoken,
306122033Sgreen			token[4],
307122033Sgreen			debugname,
308122033Sgreen			textaddr);
309122033Sgreen		} else {
310122033Sgreen		    char *modpath;
311150410Sgrog
312122033Sgreen		    modpath = findmodule(strdup(modules_path), token[4]);
313122033Sgreen		    if (modpath == NULL)
314122033Sgreen			continue;
315122033Sgreen		    fprintf(out,
316122033Sgreen			"add-symbol-file %s%s 0x%llx",
317122033Sgreen			modpath,
318122033Sgreen			debugname,
319122033Sgreen			textaddr);
320122033Sgreen		    free(modpath);
321122033Sgreen		}
322116009Sgrog		if (dataaddr)
323116009Sgrog		    fprintf(out, " -s .data 0x%llx", dataaddr);
324116009Sgrog		if (bssaddr)
325116009Sgrog		    fprintf(out, " -s .bss 0x%llx", bssaddr);
326116009Sgrog		fprintf(out, "\n");
327116009Sgrog	    }
328116009Sgrog	}
329116009Sgrog    }
330116009Sgrog    return 0;
331116009Sgrog}
332