summaryrefslogtreecommitdiffstats
path: root/usr.sbin/kldxref
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2001-09-11 01:13:15 +0000
committerpeter <peter@FreeBSD.org>2001-09-11 01:13:15 +0000
commit2975fbf1b355f2dc0a55d5112534cfdc48d0e9df (patch)
tree86821a17ef3a1d1bb08bc8b0b0c46d5d91382f47 /usr.sbin/kldxref
parent808991de7cecc9e774ffe5031e29362207e75564 (diff)
downloadFreeBSD-src-2975fbf1b355f2dc0a55d5112534cfdc48d0e9df.zip
FreeBSD-src-2975fbf1b355f2dc0a55d5112534cfdc48d0e9df.tar.gz
Add kldxref(8), for maintaining the linker.hints file for translating
module->pathname.ko. It supports only ELF for now. Submitted by: bp (with some minor tweaks)
Diffstat (limited to 'usr.sbin/kldxref')
-rw-r--r--usr.sbin/kldxref/Makefile7
-rw-r--r--usr.sbin/kldxref/ef.c376
-rw-r--r--usr.sbin/kldxref/ef.h43
-rw-r--r--usr.sbin/kldxref/fileformat40
-rw-r--r--usr.sbin/kldxref/kldxref.825
-rw-r--r--usr.sbin/kldxref/kldxref.c346
6 files changed, 837 insertions, 0 deletions
diff --git a/usr.sbin/kldxref/Makefile b/usr.sbin/kldxref/Makefile
new file mode 100644
index 0000000..0fa6c4c
--- /dev/null
+++ b/usr.sbin/kldxref/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= kldxref
+SRCS= kldxref.c ef.c
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c
new file mode 100644
index 0000000..9d3a478
--- /dev/null
+++ b/usr.sbin/kldxref/ef.c
@@ -0,0 +1,376 @@
+/*
+ * 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 <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include <err.h>
+
+#include "ef.h"
+
+void
+ef_print_phdr(Elf_Phdr *phdr)
+{
+
+ if ((phdr->p_flags & PF_W) == 0) {
+ printf("text=0x%lx ", (long)phdr->p_filesz);
+ } else {
+ printf("data=0x%lx", (long)phdr->p_filesz);
+ if (phdr->p_filesz < phdr->p_memsz)
+ printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
+ printf(" ");
+ }
+}
+
+u_long
+ef_get_offset(elf_file_t ef, Elf_Off off)
+{
+ Elf_Phdr *ph;
+ int i;
+
+ for (i = 0; i < ef->ef_nsegs; i++) {
+ ph = ef->ef_segs[i];
+ if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
+ return ph->p_offset + (off - ph->p_vaddr);
+ }
+ }
+ return 0;
+}
+
+/*
+ * next three functions copied from link_elf.c
+ */
+static unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+int
+ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
+{
+ unsigned long symnum;
+ Elf_Sym* symp;
+ char *strp;
+ unsigned long hash;
+
+ /* First, search hashed global symbols */
+ hash = elf_hash(name);
+ symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
+
+ while (symnum != STN_UNDEF) {
+ if (symnum >= ef->ef_nchains) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ symp = ef->ef_symtab + symnum;
+ if (symp->st_name == 0) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ strp = ef->ef_strtab + symp->st_name;
+
+ if (strcmp(name, strp) == 0) {
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
+ *sym = symp;
+ return 0;
+ } else
+ return ENOENT;
+ }
+
+ symnum = ef->ef_chains[symnum];
+ }
+
+ return ENOENT;
+}
+
+int
+ef_parse_dynamic(elf_file_t ef)
+{
+ Elf_Dyn *dp;
+ Elf_Off hashhdr[2];
+/* int plttype = DT_REL;*/
+ int error;
+
+ for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
+ switch (dp->d_tag) {
+ case DT_HASH:
+ error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
+ sizeof(hashhdr), hashhdr);
+ if (error) {
+ warnx("can't read hash header (%lx)",
+ ef_get_offset(ef, dp->d_un.d_ptr));
+ return error;
+ }
+ ef->ef_nbuckets = hashhdr[0];
+ ef->ef_nchains = hashhdr[1];
+ error = ef_read_entry(ef, -1,
+ (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Off),
+ (void**)&ef->ef_hashtab);
+ if (error) {
+ warnx("can't read hash table");
+ return error;
+ }
+ ef->ef_buckets = ef->ef_hashtab;
+ ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
+ break;
+ case DT_STRTAB:
+ ef->ef_stroff = dp->d_un.d_ptr;
+ break;
+ case DT_STRSZ:
+ ef->ef_strsz = dp->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ ef->ef_symoff = dp->d_un.d_ptr;
+ break;
+ case DT_SYMENT:
+ if (dp->d_un.d_val != sizeof(Elf_Sym))
+ return EFTYPE;
+ break;
+ }
+ }
+ if (ef->ef_symoff == 0) {
+ warnx("%s: no .dynsym section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef->ef_stroff == 0) {
+ warnx("%s: no .dynstr section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
+ ef->ef_nchains * sizeof(Elf_Sym),
+ (void**)&ef->ef_symtab) != 0) {
+ if (ef->ef_verbose)
+ warnx("%s: can't load .dynsym section (0x%lx)",
+ ef->ef_name, (long)ef->ef_symoff);
+ return EIO;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
+ (void**)&ef->ef_strtab) != 0) {
+ warnx("can't load .dynstr section");
+ return EIO;
+ }
+ return 0;
+}
+
+int
+ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+
+ if (offset != -1) {
+ if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
+ return EIO;
+ }
+ return read(ef->ef_fd, dest, len) == len ? 0 : EIO;
+}
+
+int
+ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ u_long ofs = ef_get_offset(ef, offset);
+
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ ef->ef_name, (long)offset, ofs);
+ return EFAULT;
+ }
+ return ef_read(ef, ofs, len, dest);
+}
+
+int
+ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_seg_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_open(const char *filename, elf_file_t ef, int verbose)
+{
+ Elf_Ehdr *hdr;
+ int fd;
+ int error;
+ int phlen, res;
+ int nsegs;
+ Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
+
+ bzero(ef, sizeof(*ef));
+ if (filename == NULL)
+ return EFTYPE;
+ ef->ef_verbose = verbose;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ hdr = (Elf_Ehdr *)&ef->ef_hdr;
+ do {
+ res = read(fd, hdr, sizeof(*hdr));
+ error = EFTYPE;
+ if (res != sizeof(*hdr))
+ break;
+ if (!IS_ELF(*hdr))
+ break;
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT ||
+ hdr->e_machine != ELF_TARG_MACH ||
+ hdr->e_phentsize != sizeof(Elf_Phdr))
+ break;
+ phlen = hdr->e_phnum * sizeof(Elf_Phdr);
+ if (ef_read_entry(ef, hdr->e_phoff, phlen,
+ (void**)&ef->ef_ph) != 0)
+ break;
+ phdr = ef->ef_ph;
+ phlimit = phdr + hdr->e_phnum;
+ nsegs = 0;
+ phdyn = NULL;
+ phphdr = NULL;
+ while (phdr < phlimit) {
+ if (verbose > 1)
+ ef_print_phdr(phdr);
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (nsegs == 2) {
+ warnx("%s: too many sections",
+ filename);
+ break;
+ }
+ ef->ef_segs[nsegs++] = phdr;
+ break;
+ case PT_PHDR:
+ phphdr = phdr;
+ break;
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ }
+ phdr++;
+ }
+ if (verbose > 1)
+ printf("\n");
+ ef->ef_nsegs = nsegs;
+ if (phdyn == NULL) {
+ warnx("file isn't dynamically-linked");
+ break;
+ }
+ if (ef_read_entry(ef, phdyn->p_offset,
+ phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
+ printf("ef_read_entry failed\n");
+ break;
+ }
+ error = ef_parse_dynamic(ef);
+ if (error)
+ break;
+ if (hdr->e_type == ET_DYN) {
+ ef->ef_type = EFT_KLD;
+/* pad = (u_int)dest & PAGE_MASK;
+ if (pad)
+ dest += PAGE_SIZE - pad;*/
+ error = 0;
+ } else if (hdr->e_type == ET_EXEC) {
+/* dest = hdr->e_entry;
+ if (dest == 0)
+ break;*/
+ ef->ef_type = EFT_KERNEL;
+ error = 0;
+ } else
+ break;
+ } while(0);
+ if (error) {
+ ef_close(ef);
+ if (ef->ef_verbose)
+ warnc(error, "elf_open(%s)", filename);
+ }
+ return error;
+}
+
+int
+ef_close(elf_file_t ef)
+{
+ close(ef->ef_fd);
+/* if (ef->ef_fpage)
+ free(ef->ef_fpage);*/
+ if (ef->ef_name)
+ free(ef->ef_name);
+ return 0;
+}
diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h
new file mode 100644
index 0000000..7dd110f
--- /dev/null
+++ b/usr.sbin/kldxref/ef.h
@@ -0,0 +1,43 @@
+/* $FreeBSD$ */
+
+#ifndef _EF_H_
+#define _EF_H_
+
+#define EFT_KLD 1
+#define EFT_KERNEL 2
+
+typedef struct elf_file {
+ char* ef_name;
+ Elf_Phdr * ef_ph;
+ int ef_fd;
+ int ef_type;
+ Elf_Ehdr ef_hdr;
+ void* ef_fpage; /* First block of the file */
+ int ef_fplen; /* length of first block */
+ Elf_Dyn* ef_dyn; /* Symbol table etc. */
+ Elf_Off ef_nbuckets;
+ Elf_Off ef_nchains;
+ Elf_Off* ef_buckets;
+ Elf_Off* ef_chains;
+ Elf_Off* ef_hashtab;
+ Elf_Off ef_stroff;
+ caddr_t ef_strtab;
+ int ef_strsz;
+ Elf_Off ef_symoff;
+ Elf_Sym* ef_symtab;
+ int ef_nsegs;
+ Elf_Phdr * ef_segs[2];
+ int ef_verbose;
+} *elf_file_t;
+
+__BEGIN_DECLS
+int ef_open(const char *, elf_file_t, int);
+int ef_close(elf_file_t ef);
+int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
+int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
+int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+__END_DECLS
+
+#endif /* _EF_H_*/
diff --git a/usr.sbin/kldxref/fileformat b/usr.sbin/kldxref/fileformat
new file mode 100644
index 0000000..2eddcca
--- /dev/null
+++ b/usr.sbin/kldxref/fileformat
@@ -0,0 +1,40 @@
+$FreeBSD$
+
+ linker.hints file consists from the one or more records. First record of
+file is special and determines its version:
+
+int version;
+
+ All subsequent records have following format:
+
+struct record {
+ int length; /* length of following data */
+ char data[length];
+};
+
+ Each record is aligned on sizeof(int) boundary. First integer of the field
+'data' determines its type:
+
+struct data {
+ int type; /* type of data. currently MTD_* values */
+};
+
+ The rest of record depends on the type.
+
+struct string {
+ int length; /* length of string */
+ char val[]; /* string itself (no terminating zero) */
+};
+
+struct data_mdt_version {
+ int type = MDT_VERSION;
+ struct string modname;
+ int version;
+ struct string kldname;
+};
+
+struct data_mdt_module {
+ int type = MDT_VERSION;
+ struct string modname;
+ struct string kldname;
+};
diff --git a/usr.sbin/kldxref/kldxref.8 b/usr.sbin/kldxref/kldxref.8
new file mode 100644
index 0000000..f0866dc
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.8
@@ -0,0 +1,25 @@
+$FreeBSD$
+
+[DRAFT]
+
+kldxref(8) used to generate linker.hints file which contains list of
+modules, their version numbers and container KLDs. This file used by loader
+and kernel linker.
+
+Each directory with KLDs should have its own linker.hints file.
+
+Typical invocation of kldxref utility may look like this:
+
+kldxref /boot/kernel /modules
+
+which will build hints file in both directories.
+
+A recursive behaviour can be specified with -R option:
+
+kldxref /boot
+
+If no hint records written, hints file will not be created and old file
+will be removed.
+
+If -d flag specified then no files generated and program prints metadata
+records to stdout.
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