summaryrefslogtreecommitdiffstats
path: root/usr.sbin/kldxref/kldxref.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/kldxref/kldxref.c')
-rw-r--r--usr.sbin/kldxref/kldxref.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c
new file mode 100644
index 0000000..79ab350
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/stat.h>
+#include <sys/module.h>
+#define FREEBSD_ELF
+#include <link.h>
+#include <err.h>
+#include <fts.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "ef.h"
+
+#define MAXRECSIZE 1024
+#define check(val) if ((error = (val)) != 0) break
+
+#ifndef min
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+struct mod_info {
+ char* mi_name;
+ int mi_ver;
+ SLIST_ENTRY(mod_info) mi_next;
+};
+
+#ifdef notnow
+struct kld_info {
+ char* k_filename;
+ SLIST_HEAD(mod_list_head, mod_info) k_modules;
+ SLIST_ENTRY(kld_info) k_next;
+};
+
+SLIST_HEAD(kld_list_head, kld_info) kldlist;
+#endif
+
+static int dflag, verbose;
+
+FILE *fxref;
+
+static char *xref_file = "linker.hints";
+
+static char recbuf[MAXRECSIZE];
+static int recpos, reccnt;
+
+static void usage(void);
+
+static void
+intalign(void)
+{
+ recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
+}
+
+static void
+record_start(void)
+{
+ recpos = 0;
+ memset(recbuf, 0, MAXRECSIZE);
+}
+
+static int
+record_end(void)
+{
+ if (dflag || recpos == 0)
+ return 0;
+ reccnt++;
+ intalign();
+ fwrite(&recpos, sizeof(recpos), 1, fxref);
+ return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0;
+}
+
+static int
+record_buf(const void *buf, int size)
+{
+ if (MAXRECSIZE - recpos < size)
+ errx(1, "record buffer overflow");
+ memcpy(recbuf + recpos, buf, size);
+ recpos += size;
+ return 0;
+}
+
+static int
+record_int(int val)
+{
+ intalign();
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_byte(u_char val)
+{
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_string(const char *str)
+{
+ int len = strlen(str);
+ int error;
+
+ if (dflag)
+ return 0;
+ error = record_byte(len);
+ if (error)
+ return error;
+ return record_buf(str, len);
+}
+
+static int
+parse_entry(struct mod_metadata *md, const char *cval,
+ struct elf_file *ef, const char *kldname)
+{
+ struct mod_depend mdp;
+ struct mod_version mdv;
+ Elf_Off data = (Elf_Off)md->md_data;
+ int error = 0;
+
+ record_start();
+ switch (md->md_type) {
+ case MDT_DEPEND:
+ if (!dflag)
+ break;
+ check(ef_seg_read(ef, data, sizeof(mdp), (void**)&mdp));
+ printf(" depends on %s.%d (%d,%d)\n", cval,
+ mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
+ break;
+ case MDT_VERSION:
+ check(ef_seg_read(ef, data, sizeof(mdv), (void**)&mdv));
+ record_int(MDT_VERSION);
+ record_string(cval);
+ record_int(mdv.mv_version);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" interface %s.%d\n", cval, mdv.mv_version);
+ break;
+ case MDT_MODULE:
+ record_int(MDT_MODULE);
+ record_string(cval);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" module %s\n", cval);
+ break;
+ default:
+ warnx("unknown metdata record %d in file %s", md->md_type, kldname);
+ }
+ if (!error)
+ record_end();
+ return error;
+}
+
+static int
+read_kld(char *filename, char *kldname)
+{
+ struct mod_metadata md;
+ struct elf_file ef;
+/* struct kld_info *kip;
+ struct mod_info *mip;*/
+ void **p, **orgp;
+ int error, nmlen;
+ long start, finish, entries;
+ Elf_Sym *sym;
+ char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
+
+ if (verbose || dflag)
+ printf("%s\n", filename);
+ error = ef_open(filename, &ef, verbose);
+ if (error)
+ return error;
+ if (ef.ef_type != EFT_KLD && ef.ef_type != EFT_KERNEL) {
+ ef_close(&ef);
+ return 0;
+ }
+ if (!dflag) {
+ cp = strrchr(kldname, '.');
+ nmlen = cp ? min(MAXMODNAME, cp - kldname) :
+ min(MAXMODNAME, strlen(kldname));
+ strncpy(kldmodname, kldname, nmlen);
+ kldmodname[nmlen] = '\0';
+/* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
+ }
+ do {
+ check(ef_lookup_symbol(&ef, "__start_set_" MDT_SETNAME, &sym));
+ start = sym->st_value;
+ check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
+ finish = sym->st_value;
+ entries = (finish - start) / sizeof(void *);
+ check(ef_seg_read_entry(&ef, start, sizeof(*p) * entries, (void**)&p));
+ orgp = p;
+ while(entries--) {
+ check(ef_seg_read(&ef, (Elf_Off)*p, sizeof(md), &md));
+ p++;
+ check(ef_seg_read(&ef, (Elf_Off)md.md_cval, sizeof(cval), cval));
+ cval[MAXMODNAME] = '\0';
+ parse_entry(&md, cval, &ef, kldname);
+ }
+ if (error)
+ warnc(error, "error while reading %s", filename);
+ free(orgp);
+ } while(0);
+ ef_close(&ef);
+ return error;
+}
+
+void
+maketempfile(char *dest, const char *root)
+{
+ char *p;
+
+ strncpy(dest, root, MAXPATHLEN - 1);
+ dest[MAXPATHLEN] = '\0';
+
+ if ((p = strrchr(dest, '/')) != 0)
+ p++;
+ else
+ p = dest;
+ strcpy(p, "lhint.XXXXXX");
+ if (mkstemp(dest) == -1)
+ err(1, "%s", dest);
+}
+
+static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int opt, fts_options, ival;
+
+ fts_options = FTS_PHYSICAL;
+/* SLIST_INIT(&kldlist);*/
+
+ while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
+ switch (opt) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ xref_file = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'R':
+ fts_options |= FTS_COMFOLLOW;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (argc - optind < 1)
+ usage();
+ argc -= optind;
+ argv += optind;
+
+ ftsp = fts_open(argv, fts_options, 0);
+ if (ftsp == NULL)
+ exit(1);
+
+ for (;;) {
+ p = fts_read(ftsp);
+ if ((p == NULL || p->fts_info == FTS_D) && !dflag && fxref) {
+ fclose(fxref);
+ if (reccnt) {
+ rename(tempname, xrefname);
+ } else {
+ unlink(tempname);
+ unlink(xrefname);
+ }
+ }
+ if (p == NULL)
+ break;
+ if (p && p->fts_info == FTS_D && !dflag) {
+ snprintf(xrefname, sizeof(xrefname), "%s/%s",
+ ftsp->fts_path, xref_file);
+ maketempfile(tempname, ftsp->fts_path);
+ fxref = fopen(tempname, "w+t");
+ if (fxref == NULL)
+ err(1, "can't create %s", tempname);
+ ival = 1;
+ fwrite(&ival, sizeof(ival), 1, fxref);
+ reccnt = 0;
+ }
+ if (p->fts_info != FTS_F)
+ continue;
+ read_kld(p->fts_path, p->fts_name);
+ }
+ fts_close(ftsp);
+ return 0;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n",
+ "Usage: kldxref [-Rdv] [-f hintfile] path [path..]"
+ );
+ exit(1);
+}
OpenPOWER on IntegriCloud