xref: /dragonfly/usr.sbin/asf/asf.c (revision 2ee85085)
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.6 2003/11/04 06:38:37 green Exp $ */
27 /* $FreeBSD: src/usr.sbin/asf/asf.c,v 1.6 2003/11/04 06:38:37 green Exp $ */
28 /* $DragonFly: src/usr.sbin/asf/asf.c,v 1.1 2004/06/18 21:20:55 hmp Exp $ */
29 
30 #define MAXLINE 1024
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/file.h>
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <sys/types.h>
41 #include <fts.h>
42 #include <unistd.h>
43 
44 #define MAXTOKEN 10
45 const char *modules_path;		/* path relative to kernel
46 					 * build directory */
47 const char *outfile;			/* and where to write the output */
48 
49 /*
50  * Take a blank separated list of tokens and turn it into a list of
51  * individual nul-delimited strings.  Build a list of pointers at
52  * token, which must have enough space for the tokens.  Return the
53  * number of tokens, or -1 on error (typically a missing string
54  * delimiter).
55  */
56 static int
57 tokenize(char *cptr, char *token[], int maxtoken)
58 {
59     char delim;				/* delimiter to search for */
60     int tokennr;			/* index of this token */
61 
62     for (tokennr = 0; tokennr < maxtoken;) {
63 	while (isspace(*cptr))
64 	    cptr++;			/* skip initial white space */
65 	if ((*cptr == '\0') || (*cptr == '\n')
66 	    || (*cptr == '#'))		/* end of line */
67 	    return tokennr;		/* return number of tokens found */
68 	delim = *cptr;
69 	token[tokennr] = cptr;		/* point to it */
70 	tokennr++;			/* one more */
71 	if (tokennr == maxtoken)	/* run off the end? */
72 	    return tokennr;
73 	if ((delim == '\'') || (delim == '"')) { /* delimitered */
74 	    for (;;) {
75 		cptr++;
76 		if ((*cptr == delim)
77 		    && (cptr[-1] != '\\')) { /* found the partner */
78 		    cptr++;		/* move on past */
79 		    if (!isspace(*cptr)) /* no space after closing quote */
80 			return -1;
81 		    *cptr++ = '\0';	/* delimit */
82 		} else if ((*cptr == '\0')
83 		    || (*cptr == '\n'))	/* end of line */
84 		    return -1;
85 	    }
86 	} else {			/* not quoted */
87 	    while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
88 		cptr++;
89 	    if (*cptr != '\0')		/* not end of the line, */
90 		*cptr++ = '\0';		/* delimit and move to the next */
91 	}
92     }
93     return maxtoken;			/* can't get here */
94 }
95 
96 static char *
97 findmodule(char *modules_path, const char *module_name)
98 {
99     char *const path_argv[2] = { modules_path, NULL };
100     char *module_path = NULL;
101     int module_name_len = strlen(module_name);
102     FTS *fts;
103     FTSENT *ftsent;
104 
105     if (modules_path == NULL) {
106 	fprintf(stderr,
107 	    "Can't allocate memory to traverse a path: %s (%d)\n",
108 	    strerror(errno),
109 	    errno);
110 	exit(1);
111     }
112     fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
113     if (fts == NULL) {
114 	fprintf(stderr,
115 	    "Can't begin traversing path %s: %s (%d)\n",
116 	    modules_path,
117 	    strerror(errno),
118 	    errno);
119 	exit(1);
120     }
121     while ((ftsent = fts_read(fts)) != NULL) {
122 	if (ftsent->fts_info == FTS_DNR ||
123 	    ftsent->fts_info == FTS_ERR ||
124 	    ftsent->fts_info == FTS_NS) {
125 	    fprintf(stderr,
126 		"Error while traversing path %s: %s (%d)\n",
127 		modules_path,
128 		strerror(errno),
129 		errno);
130 	    exit(1);
131 	}
132 	if (ftsent->fts_info != FTS_F ||
133 	    ftsent->fts_namelen != module_name_len ||
134 	    memcmp(module_name, ftsent->fts_name, module_name_len) != 0)
135 		continue;
136 	if (asprintf(&module_path,
137 	    "%.*s",
138 	    ftsent->fts_pathlen,
139 	    ftsent->fts_path) == -1) {
140 	    fprintf(stderr,
141 		"Can't allocate memory traversing path %s: %s (%d)\n",
142 		modules_path,
143 		strerror(errno),
144 		errno);
145 	    exit(1);
146 	}
147 	break;
148     }
149     if (ftsent == NULL && errno != 0) {
150 	fprintf(stderr,
151 	    "Couldn't complete traversing path %s: %s (%d)\n",
152 	    modules_path,
153 	    strerror(errno),
154 	    errno);
155 	exit(1);
156     }
157     fts_close(fts);
158     free(modules_path);
159     return (module_path);
160 }
161 
162 static void
163 usage(const char *myname)
164 {
165     fprintf(stderr,
166 	"Usage:\n"
167 	"%s [-a] [-f] [-k] [-s] [-x] [modules-path [outfile]]\n\n"
168 	"\t-a\tappend to outfile)\n"
169 	"\t-f\tfind the module in any subdirectory of module-path\n"
170 	"\t-k\ttake input from kldstat(8)\n"
171 	"\t-s\tdon't prepend subdir for module path\n"
172 	"\t-x\tdon't append \".debug\" to module name\n",
173 	myname);
174 }
175 
176 int
177 main(int argc, char *argv[])
178 {
179     char buf[MAXLINE];
180     FILE *kldstat;
181     FILE *objcopy;
182     FILE *out;				/* output file */
183     char ocbuf[MAXLINE];
184     int tokens;				/* number of tokens on line */
185     char basetoken[MAXLINE];
186     int i;
187     const char *filemode = "w";		/* mode for outfile */
188     char cwd[MAXPATHLEN];		/* current directory */
189     const char *debugname = ".debug";	/* some file names end in this */
190     char *token[MAXTOKEN];
191     int nosubdir = 0;
192     int dofind = 0;
193 
194     getcwd(cwd, MAXPATHLEN);		/* find where we are */
195     kldstat = stdin;
196     for (i = 1; i < argc; i++) {
197 	if (argv[i][0] == '-') {
198 	    if (strcmp(argv[i], "-k") == 0) { /* get input from kldstat(8) */
199 		if (!(kldstat = popen("kldstat", "r"))) {
200 		    perror("Can't start kldstat");
201 		    return 1;
202 		}
203 	    } else if (strcmp(argv[i], "-a") == 0) /* append to outfile */
204 		filemode = "a";
205 	    else if (strcmp(argv[i], "-x") == 0) /* no .debug extension */
206 		debugname = "";		/* nothing */
207 	    else if (strcmp(argv[i], "-s") == 0) /* no subdir */
208 		nosubdir = 1;		/* nothing */
209 	    else if (strcmp(argv[i], "-f") == 0) /* find .ko (recursively) */
210 		dofind = 1;
211 	    else {
212 		fprintf(stderr,
213 		    "Invalid option: %s, aborting\n",
214 		    argv[i]);
215 		usage(argv[0]);
216 		return 1;
217 	    }
218 	} else if (modules_path == NULL)
219 	    modules_path = argv[i];
220 	else if (outfile == NULL)
221 	    outfile = argv[i];
222 	else {
223 	    fprintf(stderr,
224 		"Extraneous startup information: \"%s\", aborting\n",
225 		argv[i]);
226 	    usage(argv[0]);
227 	    return 1;
228 	}
229     }
230     if (modules_path == NULL)
231 	modules_path = "modules";
232     if (outfile == NULL)
233 	outfile = ".asf";
234     if ((out = fopen(outfile, filemode)) == NULL) {
235 	fprintf(stderr,
236 	    "Can't open output file %s: %s (%d)\n",
237 	    outfile,
238 	    strerror(errno),
239 	    errno);
240 	return 1;
241     }
242     while (fgets(buf, MAXLINE, kldstat)) {
243 	if ((!(strstr(buf, "kernel")))
244 	    && buf[0] != 'I') {
245 	    quad_t base;
246 	    quad_t textaddr;
247 	    quad_t dataaddr;
248 	    quad_t bssaddr;
249 
250 	    tokens = tokenize(buf, token, MAXTOKEN);
251 	    base = strtoll(token[2], NULL, 16);
252 	    if (!dofind) {
253 		strcpy(basetoken, token[4]);
254 		basetoken[strlen(basetoken) - 3] = '/';
255 		basetoken[strlen(basetoken) - 2] = '\0'; /* cut off the .ko */
256 		snprintf(ocbuf,
257 		    MAXLINE,
258 		    "/usr/bin/objdump --section-headers %s/%s%s%s",
259 		    modules_path,
260 		    nosubdir ? "" : basetoken,
261 		    token[4],
262 		    debugname);
263 	    } else {
264 		char *modpath;
265 
266 		modpath = findmodule(strdup(modules_path), token[4]);
267 		if (modpath == NULL)
268 		    continue;
269 		snprintf(ocbuf,
270 		    MAXLINE,
271 		    "/usr/bin/objdump --section-headers %s%s",
272 		    modpath,
273 		    debugname);
274 		free(modpath);
275 	    }
276 	    if (!(objcopy = popen(ocbuf, "r"))) {
277 		fprintf(stderr,
278 		    "Can't start %s: %s (%d)\n",
279 		    ocbuf,
280 		    strerror(errno),
281 		    errno);
282 		return 1;
283 	    }
284 	    while (fgets(ocbuf, MAXLINE, objcopy)) {
285 		int octokens;
286 		char *octoken[MAXTOKEN];
287 
288 		octokens = tokenize(ocbuf, octoken, MAXTOKEN);
289 		if (octokens > 1) {
290 		    if (!strcmp(octoken[1], ".text"))
291 			textaddr = strtoll(octoken[3], NULL, 16) + base;
292 		    else if (!strcmp(octoken[1], ".data"))
293 			dataaddr = strtoll(octoken[3], NULL, 16) + base;
294 		    else if (!strcmp(octoken[1], ".bss"))
295 			bssaddr = strtoll(octoken[3], NULL, 16) + base;
296 		}
297 	    }
298 	    if (textaddr) {		/* we must have a text address */
299 		if (!dofind) {
300 		    fprintf(out,
301 			"add-symbol-file %s/%s/%s%s%s 0x%llx",
302 			cwd,
303 			modules_path,
304 			nosubdir ? "" : basetoken,
305 			token[4],
306 			debugname,
307 			textaddr);
308 		} else {
309 		    char *modpath;
310 
311 		    modpath = findmodule(strdup(modules_path), token[4]);
312 		    if (modpath == NULL)
313 			continue;
314 		    fprintf(out,
315 			"add-symbol-file %s%s 0x%llx",
316 			modpath,
317 			debugname,
318 			textaddr);
319 		    free(modpath);
320 		}
321 		if (dataaddr)
322 		    fprintf(out, " -s .data 0x%llx", dataaddr);
323 		if (bssaddr)
324 		    fprintf(out, " -s .bss 0x%llx", bssaddr);
325 		fprintf(out, "\n");
326 	    }
327 	}
328     }
329     return 0;
330 }
331