diff options
author | peter <peter@FreeBSD.org> | 2001-09-11 01:13:15 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2001-09-11 01:13:15 +0000 |
commit | 2975fbf1b355f2dc0a55d5112534cfdc48d0e9df (patch) | |
tree | 86821a17ef3a1d1bb08bc8b0b0c46d5d91382f47 /usr.sbin/kldxref | |
parent | 808991de7cecc9e774ffe5031e29362207e75564 (diff) | |
download | FreeBSD-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/Makefile | 7 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef.c | 376 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef.h | 43 | ||||
-rw-r--r-- | usr.sbin/kldxref/fileformat | 40 | ||||
-rw-r--r-- | usr.sbin/kldxref/kldxref.8 | 25 | ||||
-rw-r--r-- | usr.sbin/kldxref/kldxref.c | 346 |
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); +} |