summaryrefslogtreecommitdiffstats
path: root/usr.sbin/kldxref/ef.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/kldxref/ef.c')
-rw-r--r--usr.sbin/kldxref/ef.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c
new file mode 100644
index 0000000..88fbc34
--- /dev/null
+++ b/usr.sbin/kldxref/ef.c
@@ -0,0 +1,647 @@
+/*
+ * 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/linker.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <machine/elf.h>
+#define FREEBSD_ELF
+
+#include <err.h>
+
+#include "ef.h"
+
+#define MAXSEGS 2
+struct ef_file {
+ char* ef_name;
+ struct elf_file *ef_efile;
+ 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_Hashelt ef_nbuckets;
+ Elf_Hashelt ef_nchains;
+ Elf_Hashelt* ef_buckets;
+ Elf_Hashelt* ef_chains;
+ Elf_Hashelt* 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[MAXSEGS];
+ int ef_verbose;
+ Elf_Rel * ef_rel; /* relocation table */
+ int ef_relsz; /* number of entries */
+ Elf_Rela * ef_rela; /* relocation table */
+ int ef_relasz; /* number of entries */
+};
+
+static void ef_print_phdr(Elf_Phdr *);
+static u_long ef_get_offset(elf_file_t, Elf_Off);
+static int ef_parse_dynamic(elf_file_t);
+
+static int ef_get_type(elf_file_t ef);
+static int ef_close(elf_file_t ef);
+static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
+static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void *dest);
+static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx);
+static int ef_lookup_set(elf_file_t ef, const char *name, long *startp,
+ long *stopp, long *countp);
+static int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+
+static struct elf_file_ops ef_file_ops = {
+ ef_get_type,
+ ef_close,
+ ef_read,
+ ef_read_entry,
+ ef_seg_read,
+ ef_seg_read_rel,
+ ef_seg_read_entry,
+ ef_seg_read_entry_rel,
+ ef_symaddr,
+ ef_lookup_set,
+ ef_lookup_symbol
+};
+
+static 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(" ");
+ }
+}
+
+static 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;
+}
+
+static int
+ef_get_type(elf_file_t ef)
+{
+
+ return (ef->ef_type);
+}
+
+/*
+ * 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;
+}
+
+static 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;
+}
+
+static int
+ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
+ long *countp)
+{
+ Elf_Sym *sym;
+ char *setsym;
+ int error, len;
+
+ len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
+ setsym = malloc(len);
+ if (setsym == NULL)
+ return (ENOMEM);
+
+ /* get address of first entry */
+ snprintf(setsym, len, "%s%s", "__start_set_", name);
+ error = ef_lookup_symbol(ef, setsym, &sym);
+ if (error)
+ goto out;
+ *startp = sym->st_value;
+
+ /* get address of last entry */
+ snprintf(setsym, len, "%s%s", "__stop_set_", name);
+ error = ef_lookup_symbol(ef, setsym, &sym);
+ if (error)
+ goto out;
+ *stopp = sym->st_value;
+
+ /* and the number of entries */
+ *countp = (*stopp - *startp) / sizeof(void *);
+
+out:
+ free(setsym);
+ return (error);
+}
+
+static Elf_Addr
+ef_symaddr(elf_file_t ef, Elf_Size symidx)
+{
+ const Elf_Sym *sym;
+
+ if (symidx >= ef->ef_nchains)
+ return (0);
+ sym = ef->ef_symtab + symidx;
+
+ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
+ sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
+ return (sym->st_value);
+ return (0);
+}
+
+static int
+ef_parse_dynamic(elf_file_t ef)
+{
+ Elf_Dyn *dp;
+ Elf_Hashelt hashhdr[2];
+/* int plttype = DT_REL;*/
+ int error;
+ Elf_Off rel_off;
+ Elf_Off rela_off;
+ int rel_sz;
+ int rela_sz;
+ int rel_entry;
+ int rela_entry;
+
+ rel_off = rela_off = 0;
+ rel_sz = rela_sz = 0;
+ rel_entry = rela_entry = 0;
+ 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_Hashelt),
+ (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;
+ case DT_REL:
+ if (rel_off != 0)
+ warnx("second DT_REL entry ignored");
+ rel_off = dp->d_un.d_ptr;
+ break;
+ case DT_RELSZ:
+ if (rel_sz != 0)
+ warnx("second DT_RELSZ entry ignored");
+ rel_sz = dp->d_un.d_val;
+ break;
+ case DT_RELENT:
+ if (rel_entry != 0)
+ warnx("second DT_RELENT entry ignored");
+ rel_entry = dp->d_un.d_val;
+ break;
+ case DT_RELA:
+ if (rela_off != 0)
+ warnx("second DT_RELA entry ignored");
+ rela_off = dp->d_un.d_ptr;
+ break;
+ case DT_RELASZ:
+ if (rela_sz != 0)
+ warnx("second DT_RELASZ entry ignored");
+ rela_sz = dp->d_un.d_val;
+ break;
+ case DT_RELAENT:
+ if (rela_entry != 0)
+ warnx("second DT_RELAENT entry ignored");
+ rela_entry = dp->d_un.d_val;
+ 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;
+ }
+ if (rel_off != 0) {
+ if (rel_entry == 0) {
+ warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rel_entry != sizeof(Elf_Rel)) {
+ warnx("%s: inconsistent DT_RELENT value",
+ ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rel_sz % rel_entry != 0) {
+ warnx("%s: inconsistent values for DT_RELSZ and "
+ "DT_RELENT", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
+ (void **)&ef->ef_rel) != 0) {
+ warnx("%s: cannot load DT_REL section", ef->ef_name);
+ return (EIO);
+ }
+ ef->ef_relsz = rel_sz / rel_entry;
+ if (ef->ef_verbose)
+ warnx("%s: %d REL entries", ef->ef_name,
+ ef->ef_relsz);
+ }
+ if (rela_off != 0) {
+ if (rela_entry == 0) {
+ warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rela_entry != sizeof(Elf_Rela)) {
+ warnx("%s: inconsistent DT_RELAENT value",
+ ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rela_sz % rela_entry != 0) {
+ warnx("%s: inconsistent values for DT_RELASZ and "
+ "DT_RELAENT", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
+ (void **)&ef->ef_rela) != 0) {
+ warnx("%s: cannot load DT_RELA section", ef->ef_name);
+ return (EIO);
+ }
+ ef->ef_relasz = rela_sz / rela_entry;
+ if (ef->ef_verbose)
+ warnx("%s: %d RELA entries", ef->ef_name,
+ ef->ef_relasz);
+ }
+ return 0;
+}
+
+static int
+ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ ssize_t r;
+
+ if (offset != (Elf_Off)-1) {
+ if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
+ return EIO;
+ }
+
+ r = read(ef->ef_fd, dest, len);
+ if (r != -1 && (size_t)r == len)
+ return 0;
+ else
+ return EIO;
+}
+
+static 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;
+}
+
+static 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);
+}
+
+static int
+ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ u_long ofs = ef_get_offset(ef, offset);
+ const Elf_Rela *a;
+ const Elf_Rel *r;
+ int error;
+
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ ef->ef_name, (long)offset, ofs);
+ return EFAULT;
+ }
+ if ((error = ef_read(ef, ofs, len, dest)) != 0)
+ return (error);
+
+ for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
+ error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len,
+ dest);
+ if (error != 0)
+ return (error);
+ }
+ for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
+ error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len,
+ dest);
+ if (error != 0)
+ return (error);
+ }
+ return (0);
+}
+
+static 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;
+}
+
+static int
+ef_seg_read_entry_rel(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_rel(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_open(const char *filename, struct elf_file *efile, int verbose)
+{
+ elf_file_t ef;
+ Elf_Ehdr *hdr;
+ int fd;
+ int error;
+ int phlen, res;
+ int nsegs;
+ Elf_Phdr *phdr, *phdyn, *phlimit;
+
+ if (filename == NULL)
+ return EFTYPE;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+
+ ef = malloc(sizeof(*ef));
+ if (ef == NULL) {
+ close(fd);
+ return (ENOMEM);
+ }
+
+ efile->ef_ef = ef;
+ efile->ef_ops = &ef_file_ops;
+
+ bzero(ef, sizeof(*ef));
+ ef->ef_verbose = verbose;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ ef->ef_efile = efile;
+ 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;
+ while (phdr < phlimit) {
+ if (verbose > 1)
+ ef_print_phdr(phdr);
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (nsegs < MAXSEGS)
+ ef->ef_segs[nsegs] = phdr;
+ nsegs++;
+ break;
+ case PT_PHDR:
+ break;
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ }
+ phdr++;
+ }
+ if (verbose > 1)
+ printf("\n");
+ if (phdyn == NULL) {
+ warnx("Skipping %s: not dynamically-linked",
+ filename);
+ break;
+ } else if (nsegs > MAXSEGS) {
+ warnx("%s: too many sections", filename);
+ break;
+ }
+ ef->ef_nsegs = nsegs;
+ 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);
+ return error;
+}
+
+static 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);
+ ef->ef_efile->ef_ops = NULL;
+ ef->ef_efile->ef_ef = NULL;
+ free(ef);
+ return 0;
+}
OpenPOWER on IntegriCloud