Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / usr.sbin / asf / asf.c
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.2 2006/01/11 02:00:13 corecode 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, ch;
187     const char *filemode = "w";         /* mode for outfile */
188     char cwd[MAXPATHLEN];               /* current directory */
189     char *token[MAXTOKEN];
190     int nosubdir = 0;
191     int dofind = 0;
192
193     getcwd(cwd, MAXPATHLEN);            /* find where we are */
194     kldstat = stdin;
195     while ((ch = getopt(argc, argv, "afks")) != -1) {
196         switch (ch) {
197         case 'k': /* get input from kldstat(8) */
198             if (!(kldstat = popen("kldstat", "r"))) {
199                 perror("Can't start kldstat");
200                 return 1;
201             }
202             break;
203         case 'a': /* append to outfile */
204             filemode = "a";
205             break;
206         case 's': /* no subdir */
207             nosubdir = 1;               /* nothing */
208             break;
209         case 'f': /* find .ko (recursively) */
210             dofind = 1;
211             break;
212         default:
213             fprintf(stderr,
214                     "Invalid option: %s, aborting\n",
215                     argv[i]);
216             usage(argv[0]);
217             return 1;
218         }
219     }
220
221     argv += optind;
222     argc -= optind;
223
224     if (argc >= 1) {
225         modules_path = argv[0];
226         argc--;
227         argv++;
228     }
229
230     if (argc >= 1) {
231         outfile = argv[0];
232         argc--;
233         argv++;
234     }
235
236     if (argc > 0) {
237         fprintf(stderr,
238             "Extraneous startup information: \"%s\", aborting\n",
239             argv[0]);
240         usage(getprogname());
241         return 1;
242     }
243     if (modules_path == NULL)
244         modules_path = "modules";
245     if (outfile == NULL)
246         outfile = ".asf";
247     if ((out = fopen(outfile, filemode)) == NULL) {
248         fprintf(stderr,
249             "Can't open output file %s: %s (%d)\n",
250             outfile,
251             strerror(errno),
252             errno);
253         return 1;
254     }
255     while (fgets(buf, MAXLINE, kldstat)) {
256         if ((!(strstr(buf, "kernel")))
257             && buf[0] != 'I') {
258             quad_t base;
259             quad_t textaddr;
260             quad_t dataaddr;
261             quad_t bssaddr;
262
263             tokens = tokenize(buf, token, MAXTOKEN);
264             base = strtoll(token[2], NULL, 16);
265             if (!dofind) {
266                 strcpy(basetoken, token[4]);
267                 basetoken[strlen(basetoken) - 3] = '/';
268                 basetoken[strlen(basetoken) - 2] = '\0'; /* cut off the .ko */
269                 snprintf(ocbuf,
270                     MAXLINE,
271                     "/usr/bin/objdump --section-headers %s/%s%s",
272                     modules_path,
273                     nosubdir ? "" : basetoken,
274                     token[4]);
275             } else {
276                 char *modpath;
277                 
278                 modpath = findmodule(strdup(modules_path), token[4]);
279                 if (modpath == NULL)
280                     continue;
281                 snprintf(ocbuf,
282                     MAXLINE,
283                     "/usr/bin/objdump --section-headers %s",
284                     modpath);
285                 free(modpath);
286             }
287             if (!(objcopy = popen(ocbuf, "r"))) {
288                 fprintf(stderr,
289                     "Can't start %s: %s (%d)\n",
290                     ocbuf,
291                     strerror(errno),
292                     errno);
293                 return 1;
294             }
295             while (fgets(ocbuf, MAXLINE, objcopy)) {
296                 int octokens;
297                 char *octoken[MAXTOKEN];
298
299                 octokens = tokenize(ocbuf, octoken, MAXTOKEN);
300                 if (octokens > 1) {
301                     if (!strcmp(octoken[1], ".text"))
302                         textaddr = strtoll(octoken[3], NULL, 16) + base;
303                     else if (!strcmp(octoken[1], ".data"))
304                         dataaddr = strtoll(octoken[3], NULL, 16) + base;
305                     else if (!strcmp(octoken[1], ".bss"))
306                         bssaddr = strtoll(octoken[3], NULL, 16) + base;
307                 }
308             }
309             if (textaddr) {             /* we must have a text address */
310                 if (!dofind) {
311                     fprintf(out,
312                         "add-symbol-file %s%s%s/%s%s 0x%llx",
313                         modules_path[0] != '/' ? cwd : "",
314                         modules_path[0] != '/' ? "/" : "",
315                         modules_path,
316                         nosubdir ? "" : basetoken,
317                         token[4],
318                         textaddr);
319                 } else {
320                     char *modpath;
321
322                     modpath = findmodule(strdup(modules_path), token[4]);
323                     if (modpath == NULL)
324                         continue;
325                     fprintf(out,
326                         "add-symbol-file %s 0x%llx",
327                         modpath,
328                         textaddr);
329                     free(modpath);
330                 }
331                 if (dataaddr)
332                     fprintf(out, " -s .data 0x%llx", dataaddr);
333                 if (bssaddr)
334                     fprintf(out, " -s .bss 0x%llx", bssaddr);
335                 fprintf(out, "\n");
336             }
337         }
338     }
339     return 0;
340 }