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)
This commit is contained in:
parent
808991de7c
commit
2975fbf1b3
@ -46,6 +46,7 @@ SUBDIR= IPXrouted \
|
||||
kbdcontrol \
|
||||
kbdmap \
|
||||
kernbb \
|
||||
kldxref \
|
||||
lastlogin \
|
||||
mailwrapper \
|
||||
manctl \
|
||||
|
7
usr.sbin/kldxref/Makefile
Normal file
7
usr.sbin/kldxref/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= kldxref
|
||||
SRCS= kldxref.c ef.c
|
||||
NOMAN=
|
||||
|
||||
.include <bsd.prog.mk>
|
376
usr.sbin/kldxref/ef.c
Normal file
376
usr.sbin/kldxref/ef.c
Normal file
@ -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;
|
||||
}
|
43
usr.sbin/kldxref/ef.h
Normal file
43
usr.sbin/kldxref/ef.h
Normal file
@ -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_*/
|
40
usr.sbin/kldxref/fileformat
Normal file
40
usr.sbin/kldxref/fileformat
Normal file
@ -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;
|
||||
};
|
25
usr.sbin/kldxref/kldxref.8
Normal file
25
usr.sbin/kldxref/kldxref.8
Normal file
@ -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.
|
346
usr.sbin/kldxref/kldxref.c
Normal file
346
usr.sbin/kldxref/kldxref.c
Normal file
@ -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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user