#define MAXLINE 1024
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fts.h>
#include <unistd.h>
#define MAXTOKEN 10
const char *modules_path;
const char *outfile;
static int
tokenize(char *cptr, char *token[], int maxtoken)
{
char delim;
int tokennr;
for (tokennr = 0; tokennr < maxtoken;) {
while (isspace(*cptr))
cptr++;
if ((*cptr == '\0') || (*cptr == '\n')
|| (*cptr == '#'))
return tokennr;
delim = *cptr;
token[tokennr] = cptr;
tokennr++;
if (tokennr == maxtoken)
return tokennr;
if ((delim == '\'') || (delim == '"')) {
for (;;) {
cptr++;
if ((*cptr == delim)
&& (cptr[-1] != '\\')) {
cptr++;
if (!isspace(*cptr))
return -1;
*cptr++ = '\0';
} else if ((*cptr == '\0')
|| (*cptr == '\n'))
return -1;
}
} else {
while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
cptr++;
if (*cptr != '\0')
*cptr++ = '\0';
}
}
return maxtoken;
}
static char *
findmodule(char *mod_path, const char *module_name)
{
char *const path_argv[2] = { mod_path, NULL };
char *module_path = NULL;
size_t module_name_len = strlen(module_name);
FTS *fts;
FTSENT *ftsent;
if (mod_path == NULL) {
fprintf(stderr,
"Can't allocate memory to traverse a path: %s (%d)\n",
strerror(errno),
errno);
exit(1);
}
fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
if (fts == NULL) {
fprintf(stderr,
"Can't begin traversing path %s: %s (%d)\n",
mod_path,
strerror(errno),
errno);
exit(1);
}
while ((ftsent = fts_read(fts)) != NULL) {
if (ftsent->fts_info == FTS_DNR ||
ftsent->fts_info == FTS_ERR ||
ftsent->fts_info == FTS_NS) {
fprintf(stderr,
"Error while traversing path %s: %s (%d)\n",
mod_path,
strerror(errno),
errno);
exit(1);
}
if (ftsent->fts_info != FTS_F ||
ftsent->fts_namelen != module_name_len ||
memcmp(module_name, ftsent->fts_name, module_name_len) != 0)
continue;
if (asprintf(&module_path,
"%.*s",
(int)ftsent->fts_pathlen,
ftsent->fts_path) == -1) {
fprintf(stderr,
"Can't allocate memory traversing path %s: %s (%d)\n",
mod_path,
strerror(errno),
errno);
exit(1);
}
break;
}
if (ftsent == NULL && errno != 0) {
fprintf(stderr,
"Couldn't complete traversing path %s: %s (%d)\n",
mod_path,
strerror(errno),
errno);
exit(1);
}
fts_close(fts);
free(mod_path);
return (module_path);
}
static void
usage(const char *myname)
{
fprintf(stderr,
"Usage:\n"
"%s [-a] [-f] [-k] [modules-path [outfile]]\n\n"
"\t-a\tappend to outfile)\n"
"\t-f\tfind the module in any subdirectory of module-path\n"
"\t-k\ttake input from kldstat(8)\n",
myname);
}
int
main(int argc, char *argv[])
{
char buf[MAXLINE];
FILE *kldstat;
FILE *objcopy;
FILE *out;
char ocbuf[MAXLINE];
int tokens;
int ch;
const char *filemode = "w";
char cwd[MAXPATHLEN];
char *token[MAXTOKEN];
int dofind = 0;
getcwd(cwd, MAXPATHLEN);
kldstat = stdin;
while ((ch = getopt(argc, argv, "afk")) != -1) {
switch (ch) {
case 'k':
if (!(kldstat = popen("kldstat", "r"))) {
perror("Can't start kldstat");
return 1;
}
break;
case 'a':
filemode = "a";
break;
case 'f':
dofind = 1;
break;
default:
usage(argv[0]);
return 1;
}
}
argv += optind;
argc -= optind;
if (argc >= 1) {
modules_path = argv[0];
argc--;
argv++;
}
if (argc >= 1) {
outfile = argv[0];
argc--;
argv++;
}
if (argc > 0) {
fprintf(stderr,
"Extraneous startup information: \"%s\", aborting\n",
argv[0]);
usage(getprogname());
return 1;
}
if (modules_path == NULL)
modules_path = "/boot/kernel";
if (outfile == NULL)
outfile = ".asf";
if ((out = fopen(outfile, filemode)) == NULL) {
fprintf(stderr,
"Can't open output file %s: %s (%d)\n",
outfile,
strerror(errno),
errno);
return 1;
}
while (fgets(buf, MAXLINE, kldstat)) {
if ((!(strstr(buf, "kernel")))
&& buf[0] != 'I') {
long long base;
long long textaddr = 0;
long long dataaddr = 0;
long long bssaddr = 0;
tokens = tokenize(buf, token, MAXTOKEN);
if (tokens <= 1)
continue;
base = strtoll(token[2], NULL, 16);
if (!dofind) {
snprintf(ocbuf,
MAXLINE,
"/usr/bin/objdump --section-headers %s/%s",
modules_path,
token[4]);
} else {
char *modpath;
modpath = findmodule(strdup(modules_path), token[4]);
if (modpath == NULL)
continue;
snprintf(ocbuf,
MAXLINE,
"/usr/bin/objdump --section-headers %s",
modpath);
free(modpath);
}
if (!(objcopy = popen(ocbuf, "r"))) {
fprintf(stderr,
"Can't start %s: %s (%d)\n",
ocbuf,
strerror(errno),
errno);
return 1;
}
while (fgets(ocbuf, MAXLINE, objcopy)) {
int octokens;
char *octoken[MAXTOKEN];
octokens = tokenize(ocbuf, octoken, MAXTOKEN);
if (octokens > 1) {
if (!strcmp(octoken[1], ".text"))
textaddr = strtoll(octoken[3], NULL, 16) + base;
else if (!strcmp(octoken[1], ".data"))
dataaddr = strtoll(octoken[3], NULL, 16) + base;
else if (!strcmp(octoken[1], ".bss"))
bssaddr = strtoll(octoken[3], NULL, 16) + base;
}
}
if (textaddr) {
if (!dofind) {
fprintf(out,
"add-symbol-file %s%s%s/%s 0x%llx",
modules_path[0] != '/' ? cwd : "",
modules_path[0] != '/' ? "/" : "",
modules_path,
token[4],
textaddr);
} else {
char *modpath;
modpath = findmodule(strdup(modules_path), token[4]);
if (modpath == NULL)
continue;
fprintf(out,
"add-symbol-file %s 0x%llx",
modpath,
textaddr);
free(modpath);
}
if (dataaddr)
fprintf(out, " -s .data 0x%llx", dataaddr);
if (bssaddr)
fprintf(out, " -s .bss 0x%llx", bssaddr);
fprintf(out, "\n");
}
}
}
return 0;
}