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 + +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 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 + +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include + +#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 + +#include +#include +#include + +#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 + * + * 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 + +#include +#include +#include + +#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 + +#include +#include +#include + +#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 + * + * 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 + +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include + +#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 = "<>"; + ef->progtab[pb].offset = shdr[i].sh_offset; + } else { + ef->progtab[pb].name = "<>"; + 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 + +#include +#include +#include + +#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 + * + * 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 + +#include +#include +#include + +#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 + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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,