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