Page MenuHomeFreeBSD

D46719.diff
No OneTemporary

D46719.diff

diff --git a/Makefile.inc1 b/Makefile.inc1
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -2506,8 +2506,14 @@
_bt_libelf_depend=${_bt}-lib/libelf
.endif
+.if ${BOOTSTRAPPING} < 1500024
+_libkldelf= lib/libkldelf
+${_bt}-lib/libkldelf: ${_bt_libelf_depend}
+_bt_libkldelf_depend=${_bt}-lib/libkldelf
+.endif
+
_kldxref= usr.sbin/kldxref
-${_bt}-usr.sbin/kldxref: ${_bt_libelf_depend}
+${_bt}-usr.sbin/kldxref: ${_bt_libelf_depend} ${_bt_libkldelf_depend}
# flua is required to regenerate syscall files. It first appeared during the
# 13.0-CURRENT cycle, thus needs to be built on -older releases and stable
@@ -2770,6 +2776,7 @@
${_cat} \
${_kbdcontrol} \
${_elftoolchain_libs} \
+ ${_libkldelf} \
${_kldxref} \
lib/libopenbsd \
usr.bin/mandoc \
@@ -3213,7 +3220,8 @@
lib/libfigpar \
${_lib_libgssapi} \
lib/libjail \
- lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \
+ lib/libkiconv lib/libkldelf lib/libkvm \
+ lib/liblzma lib/libmd lib/libnv \
lib/libzstd \
${_lib_casper} \
lib/ncurses/tinfo \
@@ -3249,6 +3257,7 @@
.endif
lib/libgeom__L: lib/libexpat__L lib/libsbuf__L
+lib/libkldelf__L: lib/libelf__L
lib/libkvm__L: lib/libelf__L
.if ${MK_RADIUS_SUPPORT} != "no"
diff --git a/lib/libkldelf/Makefile b/lib/libkldelf/Makefile
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/Makefile
@@ -0,0 +1,19 @@
+.include <bsd.own.mk>
+
+INTERNALLIB=
+
+LIB= kldelf
+SRCS= ef.c ef_obj.c elf.c \
+ ef_aarch64.c \
+ ef_arm.c \
+ ef_amd64.c \
+ ef_i386.c \
+ ef_mips.c \
+ ef_powerpc.c \
+ ef_riscv.c
+INCS= kldelf.h
+WARNS?= 2
+
+LIBADD= elf
+
+.include <bsd.lib.mk>
diff --git a/lib/libkldelf/ef.c b/lib/libkldelf/ef.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef.c
@@ -0,0 +1,653 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "kldelf.h"
+
+#define MAXSEGS 16
+struct ef_file {
+ char *ef_name;
+ struct elf_file *ef_efile;
+ GElf_Phdr *ef_ph;
+ void *ef_fpage; /* First block of the file */
+ int ef_fplen; /* length of first block */
+ GElf_Hashelt ef_nbuckets;
+ GElf_Hashelt ef_nchains;
+ GElf_Hashelt *ef_buckets;
+ GElf_Hashelt *ef_chains;
+ GElf_Hashelt *ef_hashtab;
+ caddr_t ef_strtab;
+ long ef_strsz;
+ GElf_Sym *ef_symtab;
+ int ef_nsegs;
+ GElf_Phdr *ef_segs[MAXSEGS];
+ int ef_verbose;
+ GElf_Rel *ef_rel; /* relocation table */
+ long ef_relsz; /* number of entries */
+ GElf_Rela *ef_rela; /* relocation table */
+ long ef_relasz; /* number of entries */
+};
+
+static void ef_print_phdr(GElf_Phdr *);
+static GElf_Off ef_get_offset(elf_file_t, GElf_Addr);
+
+static void ef_close(elf_file_t ef);
+
+static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len,
+ void *dest);
+static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len,
+ char *dest);
+
+static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx);
+static int ef_lookup_set(elf_file_t ef, const char *name,
+ GElf_Addr *startp, GElf_Addr *stopp, long *countp);
+static int ef_lookup_symbol(elf_file_t ef, const char *name,
+ GElf_Sym **sym);
+
+static struct elf_file_ops ef_file_ops = {
+ .close = ef_close,
+ .seg_read_rel = ef_seg_read_rel,
+ .seg_read_string = ef_seg_read_string,
+ .symaddr = ef_symaddr,
+ .lookup_set = ef_lookup_set,
+};
+
+static void
+ef_print_phdr(GElf_Phdr *phdr)
+{
+
+ if ((phdr->p_flags & PF_W) == 0) {
+ printf("text=0x%jx ", (uintmax_t)phdr->p_filesz);
+ } else {
+ printf("data=0x%jx", (uintmax_t)phdr->p_filesz);
+ if (phdr->p_filesz < phdr->p_memsz)
+ printf("+0x%jx",
+ (uintmax_t)(phdr->p_memsz - phdr->p_filesz));
+ printf(" ");
+ }
+}
+
+static GElf_Off
+ef_get_offset(elf_file_t ef, GElf_Addr addr)
+{
+ GElf_Phdr *ph;
+ int i;
+
+ for (i = 0; i < ef->ef_nsegs; i++) {
+ ph = ef->ef_segs[i];
+ if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) {
+ return (ph->p_offset + (addr - ph->p_vaddr));
+ }
+ }
+ return (0);
+}
+
+/*
+ * next two functions copied from link_elf.c
+ */
+static int
+ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
+{
+ unsigned long hash, symnum;
+ GElf_Sym *symp;
+ char *strp;
+
+ /* 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 &&
+ GELF_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, GElf_Addr *startp,
+ GElf_Addr *stopp, long *countp)
+{
+ GElf_Sym *sym;
+ char *setsym;
+ int error, len;
+
+ len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
+ setsym = malloc(len);
+ if (setsym == NULL)
+ return (errno);
+
+ /* get address of first entry */
+ snprintf(setsym, len, "%s%s", "__start_set_", name);
+ error = ef_lookup_symbol(ef, setsym, &sym);
+ if (error != 0)
+ 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 != 0)
+ goto out;
+ *stopp = sym->st_value;
+
+ /* and the number of entries */
+ *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile);
+
+out:
+ free(setsym);
+ return (error);
+}
+
+static GElf_Addr
+ef_symaddr(elf_file_t ef, GElf_Size symidx)
+{
+ const GElf_Sym *sym;
+
+ if (symidx >= ef->ef_nchains)
+ return (0);
+ sym = ef->ef_symtab + symidx;
+
+ if (GELF_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, const GElf_Phdr *phdyn)
+{
+ GElf_Shdr *shdr;
+ GElf_Dyn *dyn, *dp;
+ size_t i, ndyn, nshdr, nsym;
+ int error;
+ GElf_Off hash_off, sym_off, str_off;
+ GElf_Off rel_off;
+ GElf_Off rela_off;
+ int rel_sz;
+ int rela_sz;
+ int dynamic_idx;
+
+ /*
+ * The kernel linker parses the PT_DYNAMIC segment to find
+ * various important tables. The gelf API of libelf is
+ * section-oriented and requires extracting data from sections
+ * instead of segments (program headers). As a result,
+ * iterate over section headers to read various tables after
+ * parsing values from PT_DYNAMIC.
+ */
+ error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr);
+ if (error != 0)
+ return (EFTYPE);
+ dyn = NULL;
+
+ /* Find section for .dynamic. */
+ dynamic_idx = -1;
+ for (i = 0; i < nshdr; i++) {
+ if (shdr[i].sh_type == SHT_DYNAMIC) {
+ /*
+ * PowerPC kernels contain additional sections
+ * beyond .dynamic in PT_DYNAMIC due to a linker
+ * script bug. Permit a section with a smaller
+ * size as a workaround.
+ */
+ if (shdr[i].sh_offset != phdyn->p_offset ||
+ ((elf_machine(ef->ef_efile) == EM_PPC ||
+ elf_machine(ef->ef_efile) == EM_PPC64) ?
+ shdr[i].sh_size > phdyn->p_filesz :
+ shdr[i].sh_size != phdyn->p_filesz)) {
+ warnx(".dynamic section doesn't match phdr");
+ error = EFTYPE;
+ goto out;
+ }
+ if (dynamic_idx != -1) {
+ warnx("multiple SHT_DYNAMIC sections");
+ error = EFTYPE;
+ goto out;
+ }
+ dynamic_idx = i;
+ }
+ }
+
+ error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
+ if (error != 0)
+ goto out;
+
+ hash_off = rel_off = rela_off = sym_off = str_off = 0;
+ rel_sz = rela_sz = 0;
+ for (i = 0; i < ndyn; i++) {
+ dp = &dyn[i];
+ if (dp->d_tag == DT_NULL)
+ break;
+
+ switch (dp->d_tag) {
+ case DT_HASH:
+ if (hash_off != 0)
+ warnx("second DT_HASH entry ignored");
+ else
+ hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
+ break;
+ case DT_STRTAB:
+ if (str_off != 0)
+ warnx("second DT_STRTAB entry ignored");
+ else
+ str_off = ef_get_offset(ef, dp->d_un.d_ptr);
+ break;
+ case DT_SYMTAB:
+ if (sym_off != 0)
+ warnx("second DT_SYMTAB entry ignored");
+ else
+ sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
+ break;
+ case DT_SYMENT:
+ if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
+ ELF_T_SYM)) {
+ error = EFTYPE;
+ goto out;
+ }
+ break;
+ case DT_REL:
+ if (rel_off != 0)
+ warnx("second DT_REL entry ignored");
+ else
+ rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ if (rel_sz != 0)
+ warnx("second DT_RELSZ entry ignored");
+ else
+ rel_sz = dp->d_un.d_val;
+ break;
+ case DT_RELENT:
+ if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
+ ELF_T_REL)) {
+ error = EFTYPE;
+ goto out;
+ }
+ break;
+ case DT_RELA:
+ if (rela_off != 0)
+ warnx("second DT_RELA entry ignored");
+ else
+ rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ if (rela_sz != 0)
+ warnx("second DT_RELSZ entry ignored");
+ else
+ rela_sz = dp->d_un.d_val;
+ break;
+ case DT_RELAENT:
+ if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
+ ELF_T_RELA)) {
+ error = EFTYPE;
+ goto out;
+ }
+ break;
+ }
+ }
+ if (hash_off == 0) {
+ warnx("%s: no .hash section found\n", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (sym_off == 0) {
+ warnx("%s: no .dynsym section found\n", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (str_off == 0) {
+ warnx("%s: no .dynstr section found\n", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (rel_off == 0 && rela_off == 0) {
+ warnx("%s: no ELF relocation table found\n", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+
+ nsym = 0;
+ for (i = 0; i < nshdr; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_HASH:
+ if (shdr[i].sh_offset != hash_off) {
+ warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
+ ef->ef_name);
+ break;
+ }
+
+ /*
+ * libelf(3) mentions ELF_T_HASH, but it is
+ * not defined.
+ */
+ if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
+ warnx("hash section too small");
+ error = EFTYPE;
+ goto out;
+ }
+ error = elf_read_data(ef->ef_efile, ELF_T_WORD,
+ shdr[i].sh_offset, shdr[i].sh_size,
+ (void **)&ef->ef_hashtab);
+ if (error != 0) {
+ warnc(error, "can't read hash table");
+ goto out;
+ }
+ ef->ef_nbuckets = ef->ef_hashtab[0];
+ ef->ef_nchains = ef->ef_hashtab[1];
+ if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
+ sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
+ warnx("inconsistent hash section size");
+ error = EFTYPE;
+ goto out;
+ }
+
+ ef->ef_buckets = ef->ef_hashtab + 2;
+ ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
+ break;
+ case SHT_DYNSYM:
+ if (shdr[i].sh_offset != sym_off) {
+ warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
+ ef->ef_name);
+ break;
+ }
+ error = elf_read_symbols(ef->ef_efile, i, &nsym,
+ &ef->ef_symtab);
+ if (error != 0) {
+ if (ef->ef_verbose)
+ warnx("%s: can't load .dynsym section (0x%jx)",
+ ef->ef_name, (uintmax_t)sym_off);
+ goto out;
+ }
+ break;
+ case SHT_STRTAB:
+ if (shdr[i].sh_offset != str_off)
+ break;
+ error = elf_read_string_table(ef->ef_efile,
+ &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
+ if (error != 0) {
+ warnx("can't load .dynstr section");
+ error = EIO;
+ goto out;
+ }
+ break;
+ case SHT_REL:
+ if (shdr[i].sh_offset != rel_off)
+ break;
+ if (shdr[i].sh_size != rel_sz) {
+ warnx("%s: size mismatch for DT_REL section",
+ ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
+ &ef->ef_rel);
+ if (error != 0) {
+ warnx("%s: cannot load DT_REL section",
+ ef->ef_name);
+ goto out;
+ }
+ break;
+ case SHT_RELA:
+ if (shdr[i].sh_offset != rela_off)
+ break;
+ if (shdr[i].sh_size != rela_sz) {
+ warnx("%s: size mismatch for DT_RELA section",
+ ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
+ &ef->ef_rela);
+ if (error != 0) {
+ warnx("%s: cannot load DT_RELA section",
+ ef->ef_name);
+ goto out;
+ }
+ break;
+ }
+ }
+
+ if (ef->ef_hashtab == NULL) {
+ warnx("%s: did not find a symbol hash table", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (ef->ef_symtab == NULL) {
+ warnx("%s: did not find a dynamic symbol table", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (nsym != ef->ef_nchains) {
+ warnx("%s: symbol count mismatch", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (ef->ef_strtab == NULL) {
+ warnx("%s: did not find a dynamic string table", ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (rel_off != 0 && ef->ef_rel == NULL) {
+ warnx("%s: did not find a DT_REL relocation table",
+ ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+ if (rela_off != 0 && ef->ef_rela == NULL) {
+ warnx("%s: did not find a DT_RELA relocation table",
+ ef->ef_name);
+ error = EFTYPE;
+ goto out;
+ }
+
+ error = 0;
+out:
+ free(dyn);
+ free(shdr);
+ return (error);
+}
+
+static int
+ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
+{
+ GElf_Off ofs;
+ const GElf_Rela *a;
+ const GElf_Rel *r;
+ int error;
+
+ ofs = ef_get_offset(ef, address);
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read_rel(%s): bad address (%jx)",
+ ef->ef_name, (uintmax_t)address);
+ return (EFAULT);
+ }
+ error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
+ if (error != 0)
+ return (error);
+
+ for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
+ error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
+ len, dest);
+ if (error != 0)
+ return (error);
+ }
+ for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
+ error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
+ len, dest);
+ if (error != 0)
+ return (error);
+ }
+ return (0);
+}
+
+static int
+ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
+{
+ GElf_Off ofs;
+
+ ofs = ef_get_offset(ef, address);
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
+ ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
+ return (EFAULT);
+ }
+
+ return (elf_read_raw_string(ef->ef_efile, ofs, dest, len));
+}
+
+int
+ef_open(struct elf_file *efile, int verbose)
+{
+ elf_file_t ef;
+ GElf_Ehdr *hdr;
+ size_t i, nphdr, nsegs;
+ int error;
+ GElf_Phdr *phdr, *phdyn;
+
+ hdr = &efile->ef_hdr;
+ if (hdr->e_phnum == 0 ||
+ hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
+ hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
+ hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
+ return (EFTYPE);
+
+ ef = malloc(sizeof(*ef));
+ if (ef == NULL)
+ return (errno);
+
+ efile->ef_ef = ef;
+ efile->ef_ops = &ef_file_ops;
+
+ bzero(ef, sizeof(*ef));
+ ef->ef_verbose = verbose;
+ ef->ef_name = strdup(efile->ef_filename);
+ ef->ef_efile = efile;
+
+ error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
+ if (error != 0) {
+ phdr = NULL;
+ goto out;
+ }
+
+ error = EFTYPE;
+ nsegs = 0;
+ phdyn = NULL;
+ phdr = ef->ef_ph;
+ for (i = 0; i < nphdr; i++, phdr++) {
+ 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;
+ }
+ }
+ if (verbose > 1)
+ printf("\n");
+ if (phdyn == NULL) {
+ warnx("Skipping %s: not dynamically-linked",
+ ef->ef_name);
+ goto out;
+ }
+
+ if (nsegs > MAXSEGS) {
+ warnx("%s: too many segments", ef->ef_name);
+ goto out;
+ }
+ ef->ef_nsegs = nsegs;
+
+ error = ef_parse_dynamic(ef, phdyn);
+out:
+ if (error != 0)
+ ef_close(ef);
+ return (error);
+}
+
+static void
+ef_close(elf_file_t ef)
+{
+ free(ef->ef_rela);
+ free(ef->ef_rel);
+ free(ef->ef_strtab);
+ free(ef->ef_symtab);
+ free(ef->ef_hashtab);
+ free(ef->ef_ph);
+ if (ef->ef_name)
+ free(ef->ef_name);
+ ef->ef_efile->ef_ops = NULL;
+ ef->ef_efile->ef_ef = NULL;
+ free(ef);
+}
diff --git a/lib/libkldelf/ef_aarch64.c b/lib/libkldelf/ef_aarch64.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_aarch64.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2005 Peter Grehan.
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_aarch64_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ switch (rtype) {
+ case R_AARCH64_RELATIVE:
+ addr = relbase + addend;
+ le64enc(where, addr);
+ break;
+ case R_AARCH64_ABS64:
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le64enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ break;
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_AARCH64, ef_aarch64_reloc);
diff --git a/lib/libkldelf/ef_amd64.c b/lib/libkldelf/ef_amd64.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_amd64.c
@@ -0,0 +1,113 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_amd64_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rel *rel;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_REL:
+ rel = (const GElf_Rel *)reldata;
+ where = (char *)dest + (relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = GELF_R_TYPE(rel->r_info);
+ symidx = GELF_R_SYM(rel->r_info);
+ break;
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == ELF_T_REL) {
+ /* Addend is 32 bit on 32 bit relocs */
+ switch (rtype) {
+ case R_X86_64_PC32:
+ case R_X86_64_32S:
+ addend = le32dec(where);
+ break;
+ default:
+ addend = le64dec(where);
+ break;
+ }
+ }
+
+ switch (rtype) {
+ case R_X86_64_NONE: /* none */
+ break;
+ case R_X86_64_64: /* S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le64enc(where, addr);
+ break;
+ case R_X86_64_32S: /* S + A sign extend */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le32enc(where, addr);
+ break;
+ case R_X86_64_GLOB_DAT: /* S */
+ addr = EF_SYMADDR(ef, symidx);
+ le64enc(where, addr);
+ break;
+ case R_X86_64_RELATIVE: /* B + A */
+ addr = relbase + addend;
+ le64enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_X86_64, ef_amd64_reloc);
diff --git a/lib/libkldelf/ef_arm.c b/lib/libkldelf/ef_arm.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_arm.c
@@ -0,0 +1,94 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ * Copyright (c) 2023 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_arm_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rel *rel;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_REL:
+ rel = (const GElf_Rel *)reldata;
+ where = (char *)dest + (relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = GELF_R_TYPE(rel->r_info);
+ symidx = GELF_R_SYM(rel->r_info);
+ break;
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == ELF_T_REL)
+ addend = le32dec(where);
+
+ switch (rtype) {
+ case R_ARM_ABS32: /* S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le32enc(where, addr);
+ break;
+ case R_ARM_RELATIVE: /* B + A */
+ addr = relbase + addend;
+ le32enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS32, ELFDATA2LSB, EM_ARM, ef_arm_reloc);
diff --git a/lib/libkldelf/ef_i386.c b/lib/libkldelf/ef_i386.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_i386.c
@@ -0,0 +1,97 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_i386_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rel *rel;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_REL:
+ rel = (const GElf_Rel *)reldata;
+ where = (char *)dest + (relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = GELF_R_TYPE(rel->r_info);
+ symidx = GELF_R_SYM(rel->r_info);
+ break;
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == ELF_T_REL)
+ addend = le32dec(where);
+
+ switch (rtype) {
+ case R_386_RELATIVE: /* B + A */
+ addr = relbase + addend;
+ le32enc(where, addr);
+ break;
+ case R_386_32: /* S + A - P */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le32enc(where, addr);
+ break;
+ case R_386_GLOB_DAT: /* S */
+ addr = EF_SYMADDR(ef, symidx);
+ le32enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS32, ELFDATA2LSB, EM_386, ef_i386_reloc);
diff --git a/lib/libkldelf/ef_mips.c b/lib/libkldelf/ef_mips.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_mips.c
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 John Baldwin <jhb@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_mips_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rel *rel;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_REL:
+ rel = (const GElf_Rel *)reldata;
+ where = (char *)dest + (relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = GELF_R_TYPE(rel->r_info);
+ symidx = GELF_R_SYM(rel->r_info);
+ break;
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == ELF_T_REL) {
+ if (elf_class(ef) == ELFCLASS64) {
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ addend = le64dec(where);
+ else
+ addend = be64dec(where);
+ } else {
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ addend = le32dec(where);
+ else
+ addend = be32dec(where);
+ }
+ }
+
+ switch (rtype) {
+ case R_MIPS_64: /* S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ le64enc(where, addr);
+ else
+ be64enc(where, addr);
+ break;
+ case R_MIPS_32: /* S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ le32enc(where, addr);
+ else
+ be32enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS32, ELFDATA2LSB, EM_MIPS, ef_mips_reloc);
+ELF_RELOC(ELFCLASS32, ELFDATA2MSB, EM_MIPS, ef_mips_reloc);
+ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_MIPS, ef_mips_reloc);
+ELF_RELOC(ELFCLASS64, ELFDATA2MSB, EM_MIPS, ef_mips_reloc);
diff --git a/lib/libkldelf/ef_obj.c b/lib/libkldelf/ef_obj.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_obj.c
@@ -0,0 +1,469 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * Copyright (c) 2000, Boris Popov
+ * Copyright (c) 1998-2000 Doug Rabson
+ * Copyright (c) 2004 Peter Wemm
+ * 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.
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "kldelf.h"
+
+typedef struct {
+ GElf_Addr addr;
+ GElf_Off offset;
+ GElf_Off size;
+ int flags;
+ int sec; /* Original section */
+ char *name;
+} Elf_progent;
+
+typedef struct {
+ GElf_Rel *rel;
+ long nrel;
+ int sec;
+} Elf_relent;
+
+typedef struct {
+ GElf_Rela *rela;
+ long nrela;
+ int sec;
+} Elf_relaent;
+
+struct ef_file {
+ char *ef_name;
+ struct elf_file *ef_efile;
+
+ Elf_progent *progtab;
+ int nprogtab;
+
+ Elf_relaent *relatab;
+ int nrela;
+
+ Elf_relent *reltab;
+ int nrel;
+
+ GElf_Sym *ddbsymtab; /* The symbol table we are using */
+ size_t ddbsymcnt; /* Number of symbols */
+ caddr_t ddbstrtab; /* String table */
+ long ddbstrcnt; /* number of bytes in string table */
+
+ caddr_t shstrtab; /* Section name string table */
+ long shstrcnt; /* number of bytes in string table */
+
+ int ef_verbose;
+};
+
+static void ef_obj_close(elf_file_t ef);
+
+static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address,
+ size_t len, void *dest);
+static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address,
+ size_t len, char *dest);
+
+static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx);
+static int ef_obj_lookup_set(elf_file_t ef, const char *name,
+ GElf_Addr *startp, GElf_Addr *stopp, long *countp);
+static int ef_obj_lookup_symbol(elf_file_t ef, const char *name,
+ GElf_Sym **sym);
+
+static struct elf_file_ops ef_obj_file_ops = {
+ .close = ef_obj_close,
+ .seg_read_rel = ef_obj_seg_read_rel,
+ .seg_read_string = ef_obj_seg_read_string,
+ .symaddr = ef_obj_symaddr,
+ .lookup_set = ef_obj_lookup_set,
+};
+
+static GElf_Off
+ef_obj_get_offset(elf_file_t ef, GElf_Addr addr)
+{
+ Elf_progent *pt;
+ int i;
+
+ for (i = 0; i < ef->nprogtab; i++) {
+ pt = &ef->progtab[i];
+ if (pt->offset == (GElf_Off)-1)
+ continue;
+ if (addr >= pt->addr && addr < pt->addr + pt->size)
+ return (pt->offset + (addr - pt->addr));
+ }
+ return (0);
+}
+
+static int
+ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
+{
+ GElf_Sym *symp;
+ const char *strp;
+ int i;
+
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ strp = ef->ddbstrtab + symp->st_name;
+ if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
+ *sym = symp;
+ return (0);
+ }
+ }
+ return (ENOENT);
+}
+
+static int
+ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
+ GElf_Addr *stopp, long *countp)
+{
+ int i;
+
+ for (i = 0; i < ef->nprogtab; i++) {
+ if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
+ strcmp(ef->progtab[i].name + 4, name) == 0) {
+ *startp = ef->progtab[i].addr;
+ *stopp = ef->progtab[i].addr + ef->progtab[i].size;
+ *countp = (*stopp - *startp) /
+ elf_pointer_size(ef->ef_efile);
+ return (0);
+ }
+ }
+ return (ESRCH);
+}
+
+static GElf_Addr
+ef_obj_symaddr(elf_file_t ef, GElf_Size symidx)
+{
+ const GElf_Sym *sym;
+
+ if (symidx >= ef->ddbsymcnt)
+ return (0);
+ sym = ef->ddbsymtab + symidx;
+
+ if (sym->st_shndx != SHN_UNDEF)
+ return (sym->st_value);
+ return (0);
+}
+
+static int
+ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
+{
+ GElf_Off secofs;
+ GElf_Rel *r;
+ GElf_Rela *a;
+ GElf_Addr secbase, dataoff;
+ int error, i, sec;
+
+ /* Find out which section contains the data. */
+ sec = -1;
+ for (i = 0; i < ef->nprogtab; i++) {
+ if (address < ef->progtab[i].addr)
+ continue;
+
+ dataoff = address - ef->progtab[i].addr;
+ if (dataoff + len > ef->progtab[i].size)
+ continue;
+
+ sec = ef->progtab[i].sec;
+ secbase = ef->progtab[i].addr;
+ secofs = ef->progtab[i].offset;
+ break;
+ }
+
+ if (sec == -1) {
+ if (ef->ef_verbose)
+ warnx("ef_obj_seg_read_rel(%s): bad address (%jx)",
+ ef->ef_name, (uintmax_t)address);
+ return (EFAULT);
+ }
+
+ if (secofs == (GElf_Off)-1) {
+ memset(dest, 0, len);
+ } else {
+ error = elf_read_raw_data(ef->ef_efile, secofs + dataoff, dest,
+ len);
+ if (error != 0)
+ return (error);
+ }
+
+ /* Now do the relocations. */
+ for (i = 0; i < ef->nrel; i++) {
+ if (ef->reltab[i].sec != sec)
+ continue;
+ for (r = ef->reltab[i].rel;
+ r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) {
+ error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase,
+ address, len, dest);
+ if (error != 0)
+ return (error);
+ }
+ }
+ for (i = 0; i < ef->nrela; i++) {
+ if (ef->relatab[i].sec != sec)
+ continue;
+ for (a = ef->relatab[i].rela;
+ a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) {
+ error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase,
+ address, len, dest);
+ if (error != 0)
+ return (error);
+ }
+ }
+ return (0);
+}
+
+static int
+ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
+{
+ GElf_Off ofs;
+
+ ofs = ef_obj_get_offset(ef, address);
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_obj_seg_read_string(%s): bad address (%jx)",
+ ef->ef_name, (uintmax_t)address);
+ return (EFAULT);
+ }
+
+ return (elf_read_raw_string(ef->ef_efile, ofs, dest, len));
+}
+
+int
+ef_obj_open(struct elf_file *efile, int verbose)
+{
+ elf_file_t ef;
+ GElf_Ehdr *hdr;
+ GElf_Shdr *shdr;
+ GElf_Sym *es;
+ GElf_Addr mapbase;
+ size_t i, nshdr;
+ int error, pb, ra, rl;
+ int j, nsym, symstrindex, symtabindex;
+
+ hdr = &efile->ef_hdr;
+ if (hdr->e_type != ET_REL || hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
+ hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
+ return (EFTYPE);
+
+ ef = calloc(1, sizeof(*ef));
+ if (ef == NULL)
+ return (errno);
+
+ efile->ef_ef = ef;
+ efile->ef_ops = &ef_obj_file_ops;
+
+ ef->ef_verbose = verbose;
+ ef->ef_name = strdup(efile->ef_filename);
+ ef->ef_efile = efile;
+
+ error = elf_read_shdrs(efile, &nshdr, &shdr);
+ if (error != 0) {
+ shdr = NULL;
+ goto out;
+ }
+
+ /* Scan the section headers for information and table sizing. */
+ nsym = 0;
+ symtabindex = -1;
+ symstrindex = -1;
+ for (i = 0; i < nshdr; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ ef->nprogtab++;
+ break;
+ case SHT_SYMTAB:
+ nsym++;
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ break;
+ case SHT_REL:
+ ef->nrel++;
+ break;
+ case SHT_RELA:
+ ef->nrela++;
+ break;
+ case SHT_STRTAB:
+ break;
+ }
+ }
+
+ if (ef->nprogtab == 0) {
+ warnx("%s: file has no contents", ef->ef_name);
+ goto out;
+ }
+ if (nsym != 1) {
+ warnx("%s: file has no valid symbol table", ef->ef_name);
+ goto out;
+ }
+ if (symstrindex < 0 || symstrindex > nshdr ||
+ shdr[symstrindex].sh_type != SHT_STRTAB) {
+ warnx("%s: file has invalid symbol strings", ef->ef_name);
+ goto out;
+ }
+
+ /* Allocate space for tracking the load chunks */
+ if (ef->nprogtab != 0)
+ ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab));
+ if (ef->nrel != 0)
+ ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab));
+ if (ef->nrela != 0)
+ ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab));
+ if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
+ (ef->nrel != 0 && ef->reltab == NULL) ||
+ (ef->nrela != 0 && ef->relatab == NULL)) {
+ printf("malloc failed\n");
+ error = ENOMEM;
+ goto out;
+ }
+
+ if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt,
+ &ef->ddbsymtab) != 0) {
+ printf("elf_read_symbols failed\n");
+ goto out;
+ }
+
+ if (elf_read_string_table(efile, &shdr[symstrindex], &ef->ddbstrcnt,
+ &ef->ddbstrtab) != 0) {
+ printf("elf_read_string_table failed\n");
+ goto out;
+ }
+
+ /* Do we have a string table for the section names? */
+ if (hdr->e_shstrndx != 0 &&
+ shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
+ if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx],
+ &ef->shstrcnt, &ef->shstrtab) != 0) {
+ printf("elf_read_string_table failed\n");
+ goto out;
+ }
+ }
+
+ /*
+ * Now allocate address space for code/data(progbits) and
+ * bss(nobits) and allocate space for and load relocs.
+ */
+ pb = 0;
+ rl = 0;
+ ra = 0;
+ mapbase = 0;
+ for (i = 0; i < nshdr; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ mapbase = roundup2(mapbase, shdr[i].sh_addralign);
+ ef->progtab[pb].addr = mapbase;
+ if (shdr[i].sh_type == SHT_PROGBITS) {
+ ef->progtab[pb].name = "<<PROGBITS>>";
+ ef->progtab[pb].offset = shdr[i].sh_offset;
+ } else {
+ ef->progtab[pb].name = "<<NOBITS>>";
+ ef->progtab[pb].offset = (GElf_Off)-1;
+ }
+ ef->progtab[pb].size = shdr[i].sh_size;
+ ef->progtab[pb].sec = i;
+ if (ef->shstrtab && shdr[i].sh_name != 0)
+ ef->progtab[pb].name =
+ ef->shstrtab + shdr[i].sh_name;
+
+ /* Update all symbol values with the offset. */
+ for (j = 0; j < ef->ddbsymcnt; j++) {
+ es = &ef->ddbsymtab[j];
+ if (es->st_shndx != i)
+ continue;
+ es->st_value += ef->progtab[pb].addr;
+ }
+ mapbase += shdr[i].sh_size;
+ pb++;
+ break;
+ case SHT_REL:
+ ef->reltab[rl].sec = shdr[i].sh_info;
+ if (elf_read_rel(efile, i, &ef->reltab[rl].nrel,
+ &ef->reltab[rl].rel) != 0) {
+ printf("elf_read_rel failed\n");
+ goto out;
+ }
+ rl++;
+ break;
+ case SHT_RELA:
+ ef->relatab[ra].sec = shdr[i].sh_info;
+ if (elf_read_rela(efile, i, &ef->relatab[ra].nrela,
+ &ef->relatab[ra].rela) != 0) {
+ printf("elf_read_rela failed\n");
+ goto out;
+ }
+ ra++;
+ break;
+ }
+ }
+ error = 0;
+out:
+ free(shdr);
+ if (error != 0)
+ ef_obj_close(ef);
+ return (error);
+}
+
+static void
+ef_obj_close(elf_file_t ef)
+{
+ int i;
+
+ if (ef->ef_name)
+ free(ef->ef_name);
+ if (ef->nprogtab != 0)
+ free(ef->progtab);
+ if (ef->nrel != 0) {
+ for (i = 0; i < ef->nrel; i++)
+ if (ef->reltab[i].rel != NULL)
+ free(ef->reltab[i].rel);
+ free(ef->reltab);
+ }
+ if (ef->nrela != 0) {
+ for (i = 0; i < ef->nrela; i++)
+ if (ef->relatab[i].rela != NULL)
+ free(ef->relatab[i].rela);
+ free(ef->relatab);
+ }
+ if (ef->ddbsymtab != NULL)
+ free(ef->ddbsymtab);
+ if (ef->ddbstrtab != NULL)
+ free(ef->ddbstrtab);
+ if (ef->shstrtab != NULL)
+ free(ef->shstrtab);
+ ef->ef_efile->ef_ops = NULL;
+ ef->ef_efile->ef_ef = NULL;
+ free(ef);
+}
diff --git a/lib/libkldelf/ef_powerpc.c b/lib/libkldelf/ef_powerpc.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_powerpc.c
@@ -0,0 +1,97 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2005 Peter Grehan.
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_ppc_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ switch (rtype) {
+ case R_PPC_RELATIVE: /* word32|doubleword64 B + A */
+ addr = relbase + addend;
+ if (elf_class(ef) == ELFCLASS64) {
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ le64enc(where, addr);
+ else
+ be64enc(where, addr);
+ } else
+ be32enc(where, addr);
+ break;
+ case R_PPC_ADDR32: /* word32 S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ be32enc(where, addr);
+ break;
+ case R_PPC64_ADDR64: /* doubleword64 S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ if (elf_encoding(ef) == ELFDATA2LSB)
+ le64enc(where, addr);
+ else
+ be64enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS32, ELFDATA2MSB, EM_PPC, ef_ppc_reloc);
+ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_PPC64, ef_ppc_reloc);
+ELF_RELOC(ELFCLASS64, ELFDATA2MSB, EM_PPC64, ef_ppc_reloc);
diff --git a/lib/libkldelf/ef_riscv.c b/lib/libkldelf/ef_riscv.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/ef_riscv.c
@@ -0,0 +1,85 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 John Baldwin <jhb@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+
+#include "kldelf.h"
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+static int
+ef_riscv_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ char *where;
+ GElf_Addr addr, addend;
+ GElf_Size rtype, symidx;
+ const GElf_Rela *rela;
+
+ switch (reltype) {
+ case ELF_T_RELA:
+ rela = (const GElf_Rela *)reldata;
+ where = (char *)dest + (relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = GELF_R_TYPE(rela->r_info);
+ symidx = GELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (where < (char *)dest || where >= (char *)dest + len)
+ return (0);
+
+ switch (rtype) {
+ case R_RISCV_64: /* S + A */
+ addr = EF_SYMADDR(ef, symidx) + addend;
+ le64enc(where, addr);
+ break;
+ case R_RISCV_RELATIVE: /* B + A */
+ addr = relbase + addend;
+ le64enc(where, addr);
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
+
+ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_RISCV, ef_riscv_reloc);
diff --git a/lib/libkldelf/elf.c b/lib/libkldelf/elf.c
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/elf.c
@@ -0,0 +1,688 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021-2023 John Baldwin <jhb@FreeBSD.org>
+ *
+ * This software was developed by SRI International and the University
+ * of Cambridge Computer Laboratory (Department of Computer Science
+ * and Technology) under Defense Advanced Research Projects Agency
+ * (DARPA) contract HR0011-18-C-0016 ("ECATS"), as part of the DARPA
+ * SSITH research programme and under DARPA Contract No. HR001123C0031
+ * ("MTSS").
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kldelf.h"
+
+SET_DECLARE(elf_reloc, struct elf_reloc_data);
+
+static elf_reloc_t *
+elf_find_reloc(const GElf_Ehdr *hdr)
+{
+ struct elf_reloc_data **erd;
+
+ SET_FOREACH(erd, elf_reloc) {
+ if (hdr->e_ident[EI_CLASS] == (*erd)->class &&
+ hdr->e_ident[EI_DATA] == (*erd)->data &&
+ hdr->e_machine == (*erd)->machine)
+ return ((*erd)->reloc);
+ }
+ return (NULL);
+}
+
+int
+elf_open_file(struct elf_file *efile, const char *filename, int verbose)
+{
+ int error;
+
+ memset(efile, 0, sizeof(*efile));
+ efile->ef_filename = filename;
+ efile->ef_fd = open(filename, O_RDONLY);
+ if (efile->ef_fd == -1) {
+ if (verbose)
+ warn("open(%s)", filename);
+ return (errno);
+ }
+
+ efile->ef_elf = elf_begin(efile->ef_fd, ELF_C_READ, NULL);
+ if (efile->ef_elf == NULL) {
+ if (verbose)
+ warnx("elf_begin(%s): %s", filename, elf_errmsg(0));
+ elf_close_file(efile);
+ return (EINVAL);
+ }
+
+ if (elf_kind(efile->ef_elf) != ELF_K_ELF) {
+ if (verbose)
+ warnx("%s: not an ELF file", filename);
+ elf_close_file(efile);
+ return (EINVAL);
+ }
+
+ if (gelf_getehdr(efile->ef_elf, &efile->ef_hdr) == NULL) {
+ if (verbose)
+ warnx("gelf_getehdr(%s): %s", filename, elf_errmsg(0));
+ elf_close_file(efile);
+ return (EINVAL);
+ }
+
+ efile->ef_reloc = elf_find_reloc(&efile->ef_hdr);
+ if (efile->ef_reloc == NULL) {
+ if (verbose)
+ warnx("%s: unsupported architecture", filename);
+ elf_close_file(efile);
+ return (EFTYPE);
+ }
+
+ error = ef_open(efile, verbose);
+ if (error != 0) {
+ error = ef_obj_open(efile, verbose);
+ if (error != 0) {
+ if (verbose)
+ warnc(error, "%s: not a valid DSO or object file",
+ filename);
+ elf_close_file(efile);
+ return (error);
+ }
+ }
+
+ efile->ef_pointer_size = elf_object_size(efile, ELF_T_ADDR);
+
+ return (0);
+}
+
+void
+elf_close_file(struct elf_file *efile)
+{
+ if (efile->ef_ops != NULL) {
+ EF_CLOSE(efile);
+ }
+ if (efile->ef_elf != NULL) {
+ elf_end(efile->ef_elf);
+ efile->ef_elf = NULL;
+ }
+ if (efile->ef_fd > 0) {
+ close(efile->ef_fd);
+ efile->ef_fd = -1;
+ }
+}
+
+bool
+elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr)
+{
+ if (efile->ef_hdr.e_ident[EI_CLASS] != hdr->e_ident[EI_CLASS] ||
+ efile->ef_hdr.e_ident[EI_DATA] != hdr->e_ident[EI_DATA] ||
+ efile->ef_hdr.e_machine != hdr->e_machine)
+ return (false);
+ return (true);
+}
+
+size_t
+elf_object_size(struct elf_file *efile, Elf_Type type)
+{
+ return (gelf_fsize(efile->ef_elf, type, 1, efile->ef_hdr.e_version));
+}
+
+/*
+ * The number of objects of 'type' in region of the file of size
+ * 'file_size'.
+ */
+static size_t
+elf_object_count(struct elf_file *efile, Elf_Type type, size_t file_size)
+{
+ return (file_size / elf_object_size(efile, type));
+}
+
+int
+elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len)
+{
+ ssize_t nread;
+
+ nread = pread(efile->ef_fd, dst, len, offset);
+ if (nread == -1)
+ return (errno);
+ if (nread != len)
+ return (EIO);
+ return (0);
+}
+
+int
+elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len,
+ void **out)
+{
+ void *buf;
+ int error;
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return (ENOMEM);
+ error = elf_read_raw_data(efile, offset, buf, len);
+ if (error != 0) {
+ free(buf);
+ return (error);
+ }
+ *out = buf;
+ return (0);
+}
+
+int
+elf_read_raw_string(struct elf_file *efile, off_t offset, char *dst, size_t len)
+{
+ ssize_t nread;
+
+ nread = pread(efile->ef_fd, dst, len, offset);
+ if (nread == -1)
+ return (errno);
+ if (nread == 0)
+ return (EIO);
+
+ /* A short read is ok so long as the data contains a terminator. */
+ if (strnlen(dst, nread) == nread)
+ return (EFAULT);
+
+ return (0);
+}
+
+int
+elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len,
+ void **out)
+{
+ Elf_Data dst, src;
+ void *buf;
+ int error;
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ error = elf_read_raw_data(efile, offset, buf, len);
+ if (error != 0) {
+ free(buf);
+ return (error);
+ }
+
+ memset(&dst, 0, sizeof(dst));
+ memset(&src, 0, sizeof(src));
+
+ src.d_buf = buf;
+ src.d_size = len;
+ src.d_type = type;
+ src.d_version = efile->ef_hdr.e_version;
+
+ dst.d_buf = buf;
+ dst.d_size = len;
+ dst.d_version = EV_CURRENT;
+
+ if (gelf_xlatetom(efile->ef_elf, &dst, &src, elf_encoding(efile)) ==
+ NULL) {
+ free(buf);
+ return (ENXIO);
+ }
+
+ if (dst.d_size != len)
+ warnx("elf_read_data: translation of type %u size mismatch",
+ type);
+
+ *out = buf;
+ return (0);
+}
+
+int
+elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len,
+ void **buf)
+{
+ int error;
+ void *p;
+
+ p = malloc(len);
+ if (p == NULL)
+ return (ENOMEM);
+ error = EF_SEG_READ_REL(efile, address, len, p);
+ if (error != 0) {
+ free(p);
+ return (error);
+ }
+ *buf = p;
+ return (0);
+}
+
+int
+elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp)
+{
+ GElf_Phdr *phdr;
+ size_t nphdr, i;
+ int error;
+
+ if (elf_getphdrnum(efile->ef_elf, &nphdr) == -1)
+ return (EFTYPE);
+
+ phdr = calloc(nphdr, sizeof(*phdr));
+ if (phdr == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < nphdr; i++) {
+ if (gelf_getphdr(efile->ef_elf, i, &phdr[i]) == NULL) {
+ error = EFTYPE;
+ goto out;
+ }
+ }
+
+ *nphdrp = nphdr;
+ *phdrp = phdr;
+ return (0);
+out:
+ free(phdr);
+ return (error);
+}
+
+int
+elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp)
+{
+ GElf_Shdr *shdr;
+ Elf_Scn *scn;
+ size_t nshdr, i;
+ int error;
+
+ if (elf_getshdrnum(efile->ef_elf, &nshdr) == -1)
+ return (EFTYPE);
+
+ shdr = calloc(nshdr, sizeof(*shdr));
+ if (shdr == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < nshdr; i++) {
+ scn = elf_getscn(efile->ef_elf, i);
+ if (scn == NULL) {
+ error = EFTYPE;
+ goto out;
+ }
+ if (gelf_getshdr(scn, &shdr[i]) == NULL) {
+ error = EFTYPE;
+ goto out;
+ }
+ }
+
+ *nshdrp = nshdr;
+ *shdrp = shdr;
+ return (0);
+out:
+ free(shdr);
+ return (error);
+}
+
+int
+elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp,
+ GElf_Dyn **dynp)
+{
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Dyn *dyn;
+ long i, ndyn;
+
+ scn = elf_getscn(efile->ef_elf, section_index);
+ if (scn == NULL)
+ return (EINVAL);
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return (EINVAL);
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ return (EINVAL);
+
+ ndyn = elf_object_count(efile, ELF_T_DYN, shdr.sh_size);
+ dyn = calloc(ndyn, sizeof(*dyn));
+ if (dyn == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < ndyn; i++) {
+ if (gelf_getdyn(data, i, &dyn[i]) == NULL) {
+ free(dyn);
+ return (EINVAL);
+ }
+ }
+
+ *ndynp = ndyn;
+ *dynp = dyn;
+ return (0);
+}
+
+int
+elf_read_symbols(struct elf_file *efile, int section_index, size_t *nsymp,
+ GElf_Sym **symp)
+{
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Sym *sym;
+ size_t i, nsym;
+
+ scn = elf_getscn(efile->ef_elf, section_index);
+ if (scn == NULL)
+ return (EINVAL);
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return (EINVAL);
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ return (EINVAL);
+
+ nsym = elf_object_count(efile, ELF_T_SYM, shdr.sh_size);
+ sym = calloc(nsym, sizeof(*sym));
+ if (sym == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < nsym; i++) {
+ if (gelf_getsym(data, i, &sym[i]) == NULL) {
+ free(sym);
+ return (EINVAL);
+ }
+ }
+
+ *nsymp = nsym;
+ *symp = sym;
+ return (0);
+}
+
+int
+elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
+ long *strcnt, char **strtab)
+{
+ int error;
+
+ if (shdr->sh_type != SHT_STRTAB)
+ return (EINVAL);
+ error = elf_read_raw_data_alloc(efile, shdr->sh_offset, shdr->sh_size,
+ (void **)strtab);
+ if (error != 0)
+ return (error);
+ *strcnt = shdr->sh_size;
+ return (0);
+}
+
+int
+elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
+ GElf_Rel **relp)
+{
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Rel *rel;
+ long i, nrel;
+
+ scn = elf_getscn(efile->ef_elf, section_index);
+ if (scn == NULL)
+ return (EINVAL);
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return (EINVAL);
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ return (EINVAL);
+
+ nrel = elf_object_count(efile, ELF_T_REL, shdr.sh_size);
+ rel = calloc(nrel, sizeof(*rel));
+ if (rel == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < nrel; i++) {
+ if (gelf_getrel(data, i, &rel[i]) == NULL) {
+ free(rel);
+ return (EINVAL);
+ }
+ }
+
+ *nrelp = nrel;
+ *relp = rel;
+ return (0);
+}
+
+int
+elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
+ GElf_Rela **relap)
+{
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Rela *rela;
+ long i, nrela;
+
+ scn = elf_getscn(efile->ef_elf, section_index);
+ if (scn == NULL)
+ return (EINVAL);
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ return (EINVAL);
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ return (EINVAL);
+
+ nrela = elf_object_count(efile, ELF_T_RELA, shdr.sh_size);
+ rela = calloc(nrela, sizeof(*rela));
+ if (rela == NULL)
+ return (ENOMEM);
+
+ for (i = 0; i < nrela; i++) {
+ if (gelf_getrela(data, i, &rela[i]) == NULL) {
+ free(rela);
+ return (EINVAL);
+ }
+ }
+
+ *nrelap = nrela;
+ *relap = rela;
+ return (0);
+}
+
+size_t
+elf_pointer_size(struct elf_file *efile)
+{
+ return (efile->ef_pointer_size);
+}
+
+int
+elf_int(struct elf_file *efile, const void *p)
+{
+ if (elf_encoding(efile) == ELFDATA2LSB)
+ return (le32dec(p));
+ else
+ return (be32dec(p));
+}
+
+GElf_Addr
+elf_address_from_pointer(struct elf_file *efile, const void *p)
+{
+ switch (elf_class(efile)) {
+ case ELFCLASS32:
+ if (elf_encoding(efile) == ELFDATA2LSB)
+ return (le32dec(p));
+ else
+ return (be32dec(p));
+ case ELFCLASS64:
+ if (elf_encoding(efile) == ELFDATA2LSB)
+ return (le64dec(p));
+ else
+ return (be64dec(p));
+ default:
+ __unreachable();
+ }
+}
+
+int
+elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
+ size_t len)
+{
+ return (EF_SEG_READ_STRING(efile, address, len, dst));
+}
+
+int
+elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **bufp,
+ long *countp)
+{
+ GElf_Addr *buf, start, stop;
+ char *p;
+ void *raw;
+ long i, count;
+ int error;
+
+ error = EF_LOOKUP_SET(efile, name, &start, &stop, &count);
+ if (error != 0)
+ return (error);
+
+ error = elf_read_relocated_data(efile, start,
+ count * elf_pointer_size(efile), &raw);
+ if (error != 0)
+ return (error);
+
+ buf = calloc(count, sizeof(*buf));
+ if (buf == NULL) {
+ free(raw);
+ return (ENOMEM);
+ }
+
+ p = raw;
+ for (i = 0; i < count; i++) {
+ buf[i] = elf_address_from_pointer(efile, p);
+ p += elf_pointer_size(efile);
+ }
+ free(raw);
+
+ *bufp = buf;
+ *countp = count;
+ return (0);
+}
+
+int
+elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_depend *mdp)
+{
+ int *p;
+ int error;
+
+ error = elf_read_relocated_data(efile, addr, sizeof(int) * 3,
+ (void **)&p);
+ if (error != 0)
+ return (error);
+
+ memset(mdp, 0, sizeof(*mdp));
+ mdp->md_ver_minimum = elf_int(efile, p);
+ mdp->md_ver_preferred = elf_int(efile, p + 1);
+ mdp->md_ver_maximum = elf_int(efile, p + 2);
+ free(p);
+ return (0);
+}
+
+int
+elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_version *mdv)
+{
+ int error, value;
+
+ error = EF_SEG_READ_REL(efile, addr, sizeof(int), &value);
+ if (error != 0)
+ return (error);
+
+ memset(mdv, 0, sizeof(*mdv));
+ mdv->mv_version = elf_int(efile, &value);
+ return (0);
+}
+
+int
+elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_metadata *md)
+{
+ char *p;
+ size_t len, offset, pointer_size;
+ int error;
+
+ pointer_size = elf_pointer_size(efile);
+ len = 2 * sizeof(int);
+ len = roundup(len, pointer_size);
+ len += 2 * pointer_size;
+
+ error = elf_read_relocated_data(efile, addr, len, (void **)&p);
+ if (error != 0)
+ return (error);
+
+ memset(md, 0, sizeof(*md));
+ offset = 0;
+ md->md_version = elf_int(efile, p + offset);
+ offset += sizeof(int);
+ md->md_type = elf_int(efile, p + offset);
+ offset += sizeof(int);
+ offset = roundup(offset, pointer_size);
+ md->md_data = elf_address_from_pointer(efile, p + offset);
+ offset += pointer_size;
+ md->md_cval = elf_address_from_pointer(efile, p + offset);
+ free(p);
+ return (0);
+}
+
+int
+elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_pnp_match_info *pnp)
+{
+ char *p;
+ size_t len, offset, pointer_size;
+ int error;
+
+ pointer_size = elf_pointer_size(efile);
+ len = 3 * pointer_size;
+ len = roundup(len, pointer_size);
+ len += 2 * sizeof(int);
+
+ error = elf_read_relocated_data(efile, addr, len, (void **)&p);
+ if (error != 0)
+ return (error);
+
+ memset(pnp, 0, sizeof(*pnp));
+ offset = 0;
+ pnp->descr = elf_address_from_pointer(efile, p + offset);
+ offset += pointer_size;
+ pnp->bus = elf_address_from_pointer(efile, p + offset);
+ offset += pointer_size;
+ pnp->table = elf_address_from_pointer(efile, p + offset);
+ offset += pointer_size;
+ offset = roundup(offset, pointer_size);
+ pnp->entry_len = elf_int(efile, p + offset);
+ offset += sizeof(int);
+ pnp->num_entry = elf_int(efile, p + offset);
+ free(p);
+ return (0);
+}
+
+int
+elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
+{
+ return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len,
+ dest));
+}
diff --git a/lib/libkldelf/kldelf.h b/lib/libkldelf/kldelf.h
new file mode 100644
--- /dev/null
+++ b/lib/libkldelf/kldelf.h
@@ -0,0 +1,315 @@
+/*-
+ * SPDX-License-Identifier: BSD-4-Clause
+ *
+ * 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.
+ */
+
+#ifndef _KLDELF_H_
+#define _KLDELF_H_
+
+#include <sys/linker_set.h>
+#include <stdbool.h>
+
+#define EF_CLOSE(ef) \
+ (ef)->ef_ops->close((ef)->ef_ef)
+#define EF_SEG_READ_REL(ef, address, len, dest) \
+ (ef)->ef_ops->seg_read_rel((ef)->ef_ef, address, len, dest)
+#define EF_SEG_READ_STRING(ef, address, len, dest) \
+ (ef)->ef_ops->seg_read_string((ef)->ef_ef, address, len, dest)
+#define EF_SYMADDR(ef, symidx) \
+ (ef)->ef_ops->symaddr((ef)->ef_ef, symidx)
+#define EF_LOOKUP_SET(ef, name, startp, stopp, countp) \
+ (ef)->ef_ops->lookup_set((ef)->ef_ef, name, startp, stopp, countp)
+
+/* XXX, should have a different name. */
+typedef struct ef_file *elf_file_t;
+
+/* FreeBSD's headers define additional typedef's for ELF structures. */
+typedef Elf64_Size GElf_Size;
+typedef Elf64_Hashelt GElf_Hashelt;
+
+struct elf_file;
+
+struct elf_file_ops {
+ void (*close)(elf_file_t ef);
+ int (*seg_read_rel)(elf_file_t ef, GElf_Addr address, size_t len,
+ void *dest);
+ int (*seg_read_string)(elf_file_t ef, GElf_Addr address, size_t len,
+ char *dest);
+ GElf_Addr (*symaddr)(elf_file_t ef, GElf_Size symidx);
+ int (*lookup_set)(elf_file_t ef, const char *name, GElf_Addr *startp,
+ GElf_Addr *stopp, long *countp);
+};
+
+typedef int (elf_reloc_t)(struct elf_file *ef, const void *reldata,
+ Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len,
+ void *dest);
+
+struct elf_reloc_data {
+ unsigned char class;
+ unsigned char data;
+ GElf_Half machine;
+ elf_reloc_t *reloc;
+};
+
+#define ELF_RELOC(_class, _data, _machine, _reloc) \
+ static struct elf_reloc_data __CONCAT(elf_reloc_data_, __LINE__) = { \
+ .class = (_class), \
+ .data = (_data), \
+ .machine = (_machine), \
+ .reloc = (_reloc) \
+ }; \
+ DATA_SET(elf_reloc, __CONCAT(elf_reloc_data_, __LINE__))
+
+struct elf_file {
+ elf_file_t ef_ef;
+ struct elf_file_ops *ef_ops;
+ const char *ef_filename;
+ Elf *ef_elf;
+ elf_reloc_t *ef_reloc;
+ GElf_Ehdr ef_hdr;
+ size_t ef_pointer_size;
+ int ef_fd;
+};
+
+#define elf_machine(ef) ((ef)->ef_hdr.e_machine)
+#define elf_class(ef) ((ef)->ef_hdr.e_ident[EI_CLASS])
+#define elf_encoding(ef) ((ef)->ef_hdr.e_ident[EI_DATA])
+
+/*
+ * "Generic" versions of module metadata structures.
+ */
+struct Gmod_depend {
+ int md_ver_minimum;
+ int md_ver_preferred;
+ int md_ver_maximum;
+};
+
+struct Gmod_version {
+ int mv_version;
+};
+
+struct Gmod_metadata {
+ int md_version; /* structure version MDTV_* */
+ int md_type; /* type of entry MDT_* */
+ GElf_Addr md_data; /* specific data */
+ GElf_Addr md_cval; /* common string label */
+};
+
+struct Gmod_pnp_match_info
+{
+ GElf_Addr descr; /* Description of the table */
+ GElf_Addr bus; /* Name of the bus for this table */
+ GElf_Addr table; /* Pointer to pnp table */
+ int entry_len; /* Length of each entry in the table (may be */
+ /* longer than descr describes). */
+ int num_entry; /* Number of entries in the table */
+};
+
+__BEGIN_DECLS
+
+/*
+ * Attempt to parse an open ELF file as either an executable or DSO
+ * (ef_open) or an object file (ef_obj_open). On success, these
+ * routines initialize the 'ef_ef' and 'ef_ops' members of 'ef'.
+ */
+int ef_open(struct elf_file *ef, int verbose);
+int ef_obj_open(struct elf_file *ef, int verbose);
+
+/*
+ * Direct operations on an ELF file regardless of type. Many of these
+ * use libelf.
+ */
+
+/*
+ * Open an ELF file with libelf. Populates fields other than ef_ef
+ * and ef_ops in '*efile'.
+ */
+int elf_open_file(struct elf_file *efile, const char *filename,
+ int verbose);
+
+/* Close an ELF file. */
+void elf_close_file(struct elf_file *efile);
+
+/* Is an ELF file the same architecture as hdr? */
+bool elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr);
+
+/* The size of a single object of 'type'. */
+size_t elf_object_size(struct elf_file *efile, Elf_Type type);
+
+/* The size of a pointer in architecture of 'efile'. */
+size_t elf_pointer_size(struct elf_file *efile);
+
+/*
+ * Read and convert an array of a data type from an ELF file. This is
+ * a wrapper around gelf_xlatetom() which reads an array of raw ELF
+ * objects from the file and converts them into host structures using
+ * native endianness. The data is returned in a dynamically-allocated
+ * buffer.
+ */
+int elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset,
+ size_t len, void **out);
+
+/* Reads "raw" data from an ELF file without any translation. */
+int elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst,
+ size_t len);
+
+/*
+ * A wrapper around elf_read_raw_data which returns the data in a
+ * dynamically-allocated buffer.
+ */
+int elf_read_raw_data_alloc(struct elf_file *efile, off_t offset,
+ size_t len, void **out);
+
+/* Reads a single string at the given offset from an ELF file. */
+int elf_read_raw_string(struct elf_file *efile, off_t offset, char *dst,
+ size_t len);
+
+/*
+ * Read relocated data from an ELF file and return it in a
+ * dynamically-allocated buffer. Note that no translation
+ * (byte-swapping for endianness, 32-vs-64) is performed on the
+ * returned data, but any ELF relocations which affect the contents
+ * are applied to the returned data. The address parameter gives the
+ * address of the data buffer if the ELF file were loaded into memory
+ * rather than a direct file offset.
+ */
+int elf_read_relocated_data(struct elf_file *efile, GElf_Addr address,
+ size_t len, void **buf);
+
+/*
+ * Read the program headers from an ELF file and return them in a
+ * dynamically-allocated array of GElf_Phdr objects.
+ */
+int elf_read_phdrs(struct elf_file *efile, size_t *nphdrp,
+ GElf_Phdr **phdrp);
+
+/*
+ * Read the section headers from an ELF file and return them in a
+ * dynamically-allocated array of GElf_Shdr objects.
+ */
+int elf_read_shdrs(struct elf_file *efile, size_t *nshdrp,
+ GElf_Shdr **shdrp);
+
+/*
+ * Read the dynamic table from a section of an ELF file into a
+ * dynamically-allocated array of GElf_Dyn objects.
+ */
+int elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp,
+ GElf_Dyn **dynp);
+
+/*
+ * Read a symbol table from a section of an ELF file into a
+ * dynamically-allocated array of GElf_Sym objects.
+ */
+int elf_read_symbols(struct elf_file *efile, int section_index,
+ size_t *nsymp, GElf_Sym **symp);
+
+/*
+ * Read a string table described by a section header of an ELF file
+ * into a dynamically-allocated buffer.
+ */
+int elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
+ long *strcnt, char **strtab);
+
+/*
+ * Read a table of relocation objects from a section of an ELF file
+ * into a dynamically-allocated array of GElf_Rel objects.
+ */
+int elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
+ GElf_Rel **relp);
+
+/*
+ * Read a table of relocation-with-addend objects from a section of an
+ * ELF file into a dynamically-allocated array of GElf_Rela objects.
+ */
+int elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
+ GElf_Rela **relap);
+
+/*
+ * Read a string from an ELF file and return it in the provided
+ * buffer. If the string is longer than the buffer, this fails with
+ * EFAULT. The address parameter gives the address of the data buffer
+ * if the ELF file were loaded into memory rather than a direct file
+ * offset.
+ */
+int elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
+ size_t len);
+
+/* Return the address extracted from a target pointer stored at 'p'. */
+GElf_Addr elf_address_from_pointer(struct elf_file *efile, const void *p);
+
+/*
+ * Read a linker set and return an array of addresses extracted from the
+ * relocated pointers in the linker set.
+ */
+int elf_read_linker_set(struct elf_file *efile, const char *name,
+ GElf_Addr **buf, long *countp);
+
+/*
+ * Read and convert a target 'struct mod_depend' into a host
+ * 'struct Gmod_depend'.
+ */
+int elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_depend *mdp);
+
+/*
+ * Read and convert a target 'struct mod_version' into a host
+ * 'struct Gmod_version'.
+ */
+int elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_version *mdv);
+
+/*
+ * Read and convert a target 'struct mod_metadata' into a host
+ * 'struct Gmod_metadata'.
+ */
+int elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_metadata *md);
+
+/*
+ * Read and convert a target 'struct mod_pnp_match_info' into a host
+ * 'struct Gmod_pnp_match_info'.
+ */
+int elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
+ struct Gmod_pnp_match_info *pnp);
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+int elf_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
+ GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest);
+
+__END_DECLS
+
+#endif /* _KLDELF_H_*/
diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk
--- a/share/mk/src.libnames.mk
+++ b/share/mk/src.libnames.mk
@@ -50,6 +50,7 @@
ifconfig \
ipf \
iscsiutil \
+ kldelf \
lpr \
lua \
lutok \
@@ -313,6 +314,7 @@
_DP_geom= bsdxml sbuf
_DP_cam= sbuf
_DP_kvm= elf
+_DP_kldelf= elf
_DP_casper= nv
_DP_cap_dns= nv
_DP_cap_fileargs= nv
@@ -556,6 +558,9 @@
LIBFDTDIR= ${_LIB_OBJTOP}/lib/libfdt
LIBFDT?= ${LIBFDTDIR}/libfdt${PIE_SUFFIX}.a
+LIBKLDELFDIR= ${_LIB_OBJTOP}/lib/libkldelf
+LIBKLDELF?= ${LIBKLDELFDIR}/libkldelf${PIE_SUFFIX}.a
+
LIBLUADIR= ${_LIB_OBJTOP}/lib/liblua
LIBLUA?= ${LIBLUADIR}/liblua${PIE_SUFFIX}.a
diff --git a/sys/sys/param.h b/sys/sys/param.h
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -73,7 +73,7 @@
* cannot include sys/param.h and should only be updated here.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1500023
+#define __FreeBSD_version 1500024
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,

File Metadata

Mime Type
text/plain
Expires
Sun, Sep 22, 7:42 AM (21 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
12437201
Default Alt Text
D46719.diff (80 KB)

Event Timeline