asf.c revision 116017
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/* $FreeBSD: head/usr.sbin/asf/asf.c 116017 2003-06-08 08:37:11Z jmallett $ */
28
29#define MAXLINE 1024
30#include <ctype.h>
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/file.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/wait.h>
39#include <sys/types.h>
40#include <unistd.h>
41
42#define MAXTOKEN 10
43const char *modules_path;		/* path relative to kernel
44					 * build directory */
45const char *outfile;			/* and where to write the output */
46
47/*
48 * Take a blank separated list of tokens and turn it into a list of
49 * individual nul-delimited strings.  Build a list of pointers at
50 * token, which must have enough space for the tokens.  Return the
51 * number of tokens, or -1 on error (typically a missing string
52 * delimiter).
53 */
54static int
55tokenize(char *cptr, char *token[], int maxtoken)
56{
57    char delim;				/* delimiter to search for */
58    int tokennr;			/* index of this token */
59
60    for (tokennr = 0; tokennr < maxtoken;) {
61	while (isspace(*cptr))
62	    cptr++;			/* skip initial white space */
63	if ((*cptr == '\0') || (*cptr == '\n')
64	    || (*cptr == '#'))		/* end of line */
65	    return tokennr;		/* return number of tokens found */
66	delim = *cptr;
67	token[tokennr] = cptr;		/* point to it */
68	tokennr++;			/* one more */
69	if (tokennr == maxtoken)	/* run off the end? */
70	    return tokennr;
71	if ((delim == '\'') || (delim == '"')) { /* delimitered */
72	    for (;;) {
73		cptr++;
74		if ((*cptr == delim)
75		    && (cptr[-1] != '\\')) { /* found the partner */
76		    cptr++;		/* move on past */
77		    if (!isspace(*cptr)) /* no space after closing quote */
78			return -1;
79		    *cptr++ = '\0';	/* delimit */
80		} else if ((*cptr == '\0')
81		    || (*cptr == '\n'))	/* end of line */
82		    return -1;
83	    }
84	} else {			/* not quoted */
85	    while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
86		cptr++;
87	    if (*cptr != '\0')		/* not end of the line, */
88		*cptr++ = '\0';		/* delimit and move to the next */
89	}
90    }
91    return maxtoken;			/* can't get here */
92}
93
94static void
95usage(const char *myname)
96{
97    fprintf(stderr,
98	"Usage:\n"
99	"%s [-a] [-k] [-t] [modules-path [outfile]]\n\n"
100	"\t-a\tappend to outfile)\n"
101	"\t-k\ttake input from kldstat(8)\n"
102	"\t-x\tdon't append \".debug\" to module name\n",
103	myname);
104}
105
106int
107main(int argc, char *argv[])
108{
109    char buf[MAXLINE];
110    FILE *kldstat;
111    FILE *objcopy;
112    FILE *out;				/* output file */
113    char ocbuf[MAXLINE];
114    int tokens;				/* number of tokens on line */
115    char basetoken[MAXLINE];
116    int i;
117    const char *filemode = "w";		/* mode for outfile */
118    char cwd[MAXPATHLEN];		/* current directory */
119    const char *debugname = ".debug";	/* some file names end in this */
120    char *token[MAXTOKEN];
121
122    getcwd(cwd, MAXPATHLEN);		/* find where we are */
123    kldstat = stdin;
124    for (i = 1; i < argc; i++) {
125	if (argv[i][0] == '-') {
126	    if (strcmp(argv[i], "-k") == 0) { /* get input from kldstat(8) */
127		if (!(kldstat = popen("kldstat", "r"))) {
128		    perror("Can't start kldstat");
129		    return 1;
130		}
131	    } else if (strcmp(argv[i], "-a") == 0) /* append to outfile */
132		filemode = "a";
133	    else if (strcmp(argv[i], "-x") == 0) /* no .debug extension */
134		debugname = "";		/* nothing */
135	    else {
136		fprintf(stderr,
137		    "Invalid option: %s, aborting\n",
138		    argv[i]);
139		usage(argv[0]);
140		return 1;
141	    }
142	} else if (modules_path == NULL)
143	    modules_path = argv[i];
144	else if (outfile == NULL)
145	    outfile = argv[i];
146	else {
147	    fprintf(stderr,
148		"Extraneous startup information: \"%s\", aborting\n",
149		argv[i]);
150	    usage(argv[0]);
151	    return 1;
152	}
153    }
154    if (modules_path == NULL)
155	modules_path = "modules";
156    if (outfile == NULL)
157	outfile = ".asf";
158    if ((out = fopen(outfile, filemode)) == NULL) {
159	fprintf(stderr,
160	    "Can't open output file %s: %s (%d)\n",
161	    outfile,
162	    strerror(errno),
163	    errno);
164	return 1;
165    }
166    while (fgets(buf, MAXLINE, kldstat)) {
167	if ((!(strstr(buf, "kernel")))
168	    && buf[0] != 'I') {
169	    quad_t base;
170	    quad_t textaddr;
171	    quad_t dataaddr;
172	    quad_t bssaddr;
173
174	    tokens = tokenize(buf, token, MAXTOKEN);
175	    base = strtoll(token[2], NULL, 16);
176	    strcpy(basetoken, token[4]);
177	    basetoken[strlen(basetoken) - 3] = '\0'; /* cut off the .ko */
178	    snprintf(ocbuf,
179		MAXLINE,
180		"/usr/bin/objdump --section-headers %s/%s/%s%s",
181		modules_path,
182		basetoken,
183		token[4],
184		debugname);
185	    if (!(objcopy = popen(ocbuf, "r"))) {
186		fprintf(stderr,
187		    "Can't start %s: %s (%d)\n",
188		    ocbuf,
189		    strerror(errno),
190		    errno);
191		return 1;
192	    }
193	    while (fgets(ocbuf, MAXLINE, objcopy)) {
194		int octokens;
195		char *octoken[MAXTOKEN];
196
197		octokens = tokenize(ocbuf, octoken, MAXTOKEN);
198		if (octokens > 1) {
199		    if (!strcmp(octoken[1], ".text"))
200			textaddr = strtoll(octoken[3], NULL, 16) + base;
201		    else if (!strcmp(octoken[1], ".data"))
202			dataaddr = strtoll(octoken[3], NULL, 16) + base;
203		    else if (!strcmp(octoken[1], ".bss"))
204			bssaddr = strtoll(octoken[3], NULL, 16) + base;
205		}
206	    }
207	    if (textaddr) {		/* we must have a text address */
208		fprintf(out,
209		    "add-symbol-file %s/%s/%s/%s%s 0x%llx",
210		    cwd,
211		    modules_path,
212		    basetoken,
213		    token[4],
214		    debugname,
215		    textaddr);
216		if (dataaddr)
217		    fprintf(out, " -s .data 0x%llx", dataaddr);
218		if (bssaddr)
219		    fprintf(out, " -s .bss 0x%llx", bssaddr);
220		fprintf(out, "\n");
221	    }
222	}
223    }
224    return 0;
225}
226