2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2002, 2003 Greg Lehey
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * This software is provided by the author ``as is'' and any express
17 * or implied warranties, including, but not limited to, the implied
18 * warranties of merchantability and fitness for a particular purpose
19 * are disclaimed. In no event shall the author be liable for any
20 * direct, indirect, incidental, special, exemplary, or consequential
21 * damages (including, but not limited to, procurement of substitute
22 * goods or services; loss of use, data, or profits; or business
23 * interruption) however caused and on any theory of liability,
24 * whether in contract, strict liability, or tort (including
25 * negligence or otherwise) arising in any way out of the use of this
26 * software, even if advised of the possibility of such damage.
28 /* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/types.h>
34 #include <sys/queue.h>
53 STAILQ_ENTRY(kfile) link;
56 static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head);
59 kfile_add(const char *name, caddr_t addr)
63 if ((kfp = malloc(sizeof(*kfp))) == NULL ||
64 (kfp->name = strdup(name)) == NULL)
65 errx(2, "out of memory");
68 STAILQ_INSERT_TAIL(&kfile_head, kfp, link);
72 kfile_find(const char *name)
76 STAILQ_FOREACH(kfp, &kfile_head, link)
77 if (strcmp(kfp->name, name) == 0)
78 return (kfp); /* found */
80 return (NULL); /* not found */
88 STAILQ_FOREACH(kfp, &kfile_head, link)
90 return (0); /* at least one unseen */
92 return (1); /* all seen */
98 return (STAILQ_EMPTY(&kfile_head));
102 * Take a blank separated list of tokens and turn it into a list of
103 * individual nul-delimited strings. Build a list of pointers at
104 * token, which must have enough space for the tokens. Return the
105 * number of tokens, or -1 on error (typically a missing string
109 tokenize(char *cptr, char *token[], int maxtoken)
111 char delim; /* delimiter to search for */
112 int tokennr; /* index of this token */
114 for (tokennr = 0; tokennr < maxtoken;) {
115 while (isspace(*cptr))
116 cptr++; /* skip initial white space */
117 if ((*cptr == '\0') || (*cptr == '\n')
118 || (*cptr == '#')) /* end of line */
119 return tokennr; /* return number of tokens found */
121 token[tokennr] = cptr; /* point to it */
122 tokennr++; /* one more */
123 if (tokennr == maxtoken) /* run off the end? */
125 if ((delim == '\'') || (delim == '"')) { /* delimitered */
129 && (cptr[-1] != '\\')) { /* found the partner */
130 cptr++; /* move on past */
131 if (!isspace(*cptr)) /* no space after closing quote */
133 *cptr++ = '\0'; /* delimit */
134 } else if ((*cptr == '\0')
135 || (*cptr == '\n')) /* end of line */
138 } else { /* not quoted */
139 while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
141 if (*cptr != '\0') /* not end of the line, */
142 *cptr++ = '\0'; /* delimit and move to the next */
145 return maxtoken; /* can't get here */
149 doobj(const char *path, caddr_t addr, FILE *out)
151 uintmax_t base = (uintptr_t)addr;
152 uintmax_t textaddr = 0;
153 uintmax_t dataaddr = 0;
154 uintmax_t bssaddr = 0;
157 char *octoken[MAXTOKEN];
158 char ocbuf[LINE_MAX + PATH_MAX];
161 snprintf(ocbuf, sizeof(ocbuf),
162 "/usr/bin/objdump --section-headers %s", path);
163 if ((objcopy = popen(ocbuf, "r")) == NULL)
164 err(2, "can't start %s", ocbuf);
165 while (fgets(ocbuf, sizeof(ocbuf), objcopy)) {
166 octokens = tokenize(ocbuf, octoken, MAXTOKEN);
170 if (strcmp(octoken[1], ".text") == 0)
172 else if (strcmp(octoken[1], ".data") == 0)
174 else if (strcmp(octoken[1], ".bss") == 0)
178 *up = strtoumax(octoken[3], NULL, 16) + base;
180 if (textaddr) { /* we must have a text address */
181 fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr);
183 fprintf(out, " -s .data 0x%jx", dataaddr);
185 fprintf(out, " -s .bss 0x%jx", bssaddr);
191 findmodules(char *path_argv[], const char *sfx[], FILE *out)
200 /* Have to fts once per suffix to find preferred suffixes first */
202 sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */
203 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
205 err(2, "can't begin traversing module path");
206 while ((ftsent = fts_read(fts)) != NULL) {
207 if (ftsent->fts_info == FTS_DNR ||
208 ftsent->fts_info == FTS_ERR ||
209 ftsent->fts_info == FTS_NS) {
210 errno = ftsent->fts_errno;
211 err(2, "error while traversing path %s", ftsent->fts_path);
213 if (ftsent->fts_info != FTS_F)
214 continue; /* not a plain file */
217 /* non-blank suffix; see if file name has it */
218 i = ftsent->fts_namelen - sl;
219 if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0)
220 continue; /* no such suffix */
221 if ((p = strdup(ftsent->fts_name)) == NULL)
222 errx(2, "out of memory");
223 p[i] = '\0'; /* remove suffix in the copy */
227 kfp = kfile_find(ftsent->fts_name);
229 if (kfp && !kfp->seen) {
230 doobj(ftsent->fts_path, kfp->addr, out);
232 /* Optimization: stop fts as soon as seen all loaded modules */
233 if (kfile_allseen()) {
239 if (ftsent == NULL && errno != 0)
240 err(2, "couldn't complete traversing module path");
246 usage(const char *myname)
250 "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n"
251 "%*s [modules-path [outfile]]\n\n"
252 "\t-a\tappend to outfile\n"
253 "\t-f\tfind the module in any subdirectory of modules-path\n"
254 "\t-K\tuse kld(2) to get the list of modules\n"
255 "\t-k\ttake input from kldstat(8)\n"
256 "\t-M\tspecify core name for kvm(3)\n"
257 "\t-N\tspecify system name for kvm(3)\n"
258 "\t-o\tuse outfile instead of \".asf\"\n"
259 "\t-s\tdon't prepend subdir for module path\n"
260 "\t-V\tuse kvm(3) to get the list of modules\n"
261 "\t-X\tappend suffix to list of possible module file name suffixes\n"
262 "\t-x\tclear list of possible module file name suffixes\n",
263 myname, (int)strlen(myname), "");
268 #define MAXSUFFIXES 15
270 /* KLD file names end in this */
271 static int nsuffixes = 2;
272 static const char *suffixes[MAXSUFFIXES + 1] = {
279 main(int argc, char *argv[])
281 char basename[PATH_MAX];
283 char *modules_argv[MAXPATHS + 1];
286 const char *filemode = "w"; /* mode for outfile */
287 const char *modules_path = "modules"; /* path to kernel build directory */
288 const char *outfile = ".asf"; /* and where to write the output */
289 const char *corefile = NULL; /* for kvm(3) */
290 const char *sysfile = NULL; /* for kvm(3) */
294 FILE *out; /* output file */
301 const int sl = strlen(KLDSUFFIX);
303 while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1)
306 filemode = "a"; /* append to outfile */
309 dofind = 1; /* find .ko (recursively) */
312 dokld = 1; /* use kld(2) interface */
315 runprog = 1; /* get input from kldstat(8) */
318 corefile = optarg; /* core file for kvm(3) */
321 sysfile = optarg; /* system file (kernel) for kvm(3) */
324 outfile = optarg; /* output file name */
327 nosubdir = 1; /* don't descend into subdirs */
330 dokvm = 1; /* use kvm(3) interface */
333 if (nsuffixes >= MAXSUFFIXES)
334 errx(2, "only %d suffixes can be specified", MAXSUFFIXES);
335 suffixes[nsuffixes++] = optarg;
336 suffixes[nsuffixes] = NULL;
350 modules_path = argv[0];
360 if (strcmp(outfile, "-") == 0)
363 if ((out = fopen(outfile, filemode)) == NULL)
364 err(2, "can't open output file %s", outfile);
366 if (dokvm || corefile || sysfile) {
367 if (dokld || runprog)
368 warnx("using kvm(3) instead");
369 asf_kvm(sysfile, corefile);
372 warnx("using kld(2) instead");
377 /* Avoid long operations like module tree traversal when nothing to do */
379 warnx("no kernel modules loaded");
383 if ((copy = strdup(modules_path)) == NULL)
384 errx(2, "out of memory");
386 ap = modules_argv, p = copy;
387 (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS];
391 errx(2, "only %d module path elements can be specified", MAXPATHS);
394 STAILQ_FOREACH(kfp, &kfile_head, link) {
395 for (ap = modules_argv; *ap; ap++) {
397 /* prepare basename of KLD, w/o suffix */
398 strlcpy(basename, kfp->name, sizeof(basename) - 1);
399 i = strlen(basename);
400 if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0)
403 basename[i + 1] = '\0';
405 for (sfx = suffixes;; sfx++) {
406 snprintf(path, sizeof(path),
409 nosubdir ? "" : basename,
412 if (stat(path, &st) == 0) {
413 doobj(path, kfp->addr, out);
420 warnx("module %s not found in search path", kfp->name);
425 findmodules(modules_argv, suffixes, out);