diff options
author | John Baldwin <jhb@FreeBSD.org> | 2023-12-12 23:43:00 +0000 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2023-12-12 23:43:00 +0000 |
commit | 0299afdff145e5d861797fe9c2de8b090c456fba (patch) | |
tree | 7f424aa8d74f77fea9c3c048fe945edef0aadb89 | |
parent | c40fa3dc98d3cd8c39605c19c9cc08026d8e72c9 (diff) | |
download | src-0299afdff145e5d861797fe9c2de8b090c456fba.tar.gz src-0299afdff145e5d861797fe9c2de8b090c456fba.zip |
kldxref: Make use of libelf to be a portable cross tool
This allows kldxref to operate on kernel objects from any
architecture, not just the native architecture. In particular, this
will permit generating linker.hints files as part of a cross-arch
release build.
- elf.c is a new file that includes various wrappers around libelf
including routines to read ELF data structures such as program and
section headers and ELF relocations into the "generic" forms
described in <gelf.h>. This file also provides routines for
converting a linker set into an array of addresses (GElf_Addr)
as well as reading architecture-specific mod_* structures and
converting them into "generic" Gmod_* forms where pointers are
replaced with addresses.
- The various architecture-specific reloc handlers now use GElf_*
types for most values (including GElf_Rel and GElf_Rela for
relocation structures) and use routines from <sys/endian.h> to read
and write target values. A new linker set matches reloc handlers
to specific ELF (class, encoding, machine) tuples.
- The bits of kldxref.c that write out linker.hints now use the
encoding (ELFDATA2[LM]SB) of the first file encountered in a
directory to set the endianness of the output file. Input files
with a different architecture in the same directory are skipped with
a warning. In addition, the initial version record for the file
must be deferred until the first record is finished since the
architecture of the output file is not known until then.
- Various places that used 'sizeof(void *)' throughout now use
'elf_pointer_size()' to determine the size of a pointer in the
target architecture.
Tested by: amd64 binary on both amd64 and i386 /boot/kernel
Reviewed by: imp
Sponsored by: DARPA
Differential Revision: https://reviews.freebsd.org/D42966
-rw-r--r-- | usr.sbin/kldxref/Makefile | 9 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef.c | 730 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef.h | 291 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_aarch64.c | 32 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_amd64.c | 65 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_i386.c | 56 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_mips.c | 79 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_nop.c | 40 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_obj.c | 341 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_powerpc.c | 62 | ||||
-rw-r--r-- | usr.sbin/kldxref/ef_riscv.c | 35 | ||||
-rw-r--r-- | usr.sbin/kldxref/elf.c | 674 | ||||
-rw-r--r-- | usr.sbin/kldxref/kldxref.c | 196 |
13 files changed, 1685 insertions, 925 deletions
diff --git a/usr.sbin/kldxref/Makefile b/usr.sbin/kldxref/Makefile index 3d4ce7163b48..6e0a7244328b 100644 --- a/usr.sbin/kldxref/Makefile +++ b/usr.sbin/kldxref/Makefile @@ -2,14 +2,11 @@ PACKAGE= runtime PROG= kldxref MAN= kldxref.8 -SRCS= kldxref.c ef.c ef_obj.c +SRCS= kldxref.c ef.c ef_obj.c elf.c +SRCS+= ef_aarch64.c ef_amd64.c ef_i386.c ef_mips.c ef_powerpc.c ef_riscv.c WARNS?= 2 -.if exists(ef_${MACHINE_CPUARCH}.c) -SRCS+= ef_${MACHINE_CPUARCH}.c -.else -SRCS+= ef_nop.c -.endif +LIBADD= elf .include <bsd.prog.mk> diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c index 72e023e30783..aa9123d7f540 100644 --- a/usr.sbin/kldxref/ef.c +++ b/usr.sbin/kldxref/ef.c @@ -33,16 +33,13 @@ */ #include <sys/param.h> -#include <sys/linker.h> #include <err.h> #include <errno.h> -#include <fcntl.h> +#include <gelf.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <machine/elf.h> #include "ef.h" @@ -50,76 +47,52 @@ struct ef_file { char *ef_name; struct elf_file *ef_efile; - Elf_Phdr *ef_ph; - int ef_fd; - int ef_type; - Elf_Ehdr ef_hdr; + GElf_Phdr *ef_ph; void *ef_fpage; /* First block of the file */ int ef_fplen; /* length of first block */ - Elf_Dyn *ef_dyn; /* Symbol table etc. */ - Elf_Hashelt ef_nbuckets; - Elf_Hashelt ef_nchains; - Elf_Hashelt *ef_buckets; - Elf_Hashelt *ef_chains; - Elf_Hashelt *ef_hashtab; - Elf_Off ef_stroff; + GElf_Hashelt ef_nbuckets; + GElf_Hashelt ef_nchains; + GElf_Hashelt *ef_buckets; + GElf_Hashelt *ef_chains; + GElf_Hashelt *ef_hashtab; caddr_t ef_strtab; - int ef_strsz; - Elf_Off ef_symoff; - Elf_Sym *ef_symtab; + long ef_strsz; + GElf_Sym *ef_symtab; int ef_nsegs; - Elf_Phdr *ef_segs[MAXSEGS]; + GElf_Phdr *ef_segs[MAXSEGS]; int ef_verbose; - Elf_Rel *ef_rel; /* relocation table */ - int ef_relsz; /* number of entries */ - Elf_Rela *ef_rela; /* relocation table */ - int ef_relasz; /* number of entries */ + 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(Elf_Phdr *); -static Elf_Off ef_get_offset(elf_file_t, Elf_Off); -static int ef_parse_dynamic(elf_file_t); +static void ef_print_phdr(GElf_Phdr *); +static GElf_Off ef_get_offset(elf_file_t, GElf_Addr); -static int ef_get_type(elf_file_t ef); -static int ef_close(elf_file_t ef); -static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); -static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); +static void ef_close(elf_file_t ef); -static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, +static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest); -static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, - void *dest); -static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, +static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest); -static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); -static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); - -static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx); -static int ef_lookup_set(elf_file_t ef, const char *name, long *startp, - long *stopp, long *countp); + +static 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, - Elf_Sym **sym); + GElf_Sym **sym); static struct elf_file_ops ef_file_ops = { - .get_type = ef_get_type, .close = ef_close, - .read = ef_read, - .read_entry = ef_read_entry, - .seg_read = ef_seg_read, .seg_read_rel = ef_seg_read_rel, .seg_read_string = ef_seg_read_string, - .seg_read_entry = ef_seg_read_entry, - .seg_read_entry_rel = ef_seg_read_entry_rel, .symaddr = ef_symaddr, .lookup_set = ef_lookup_set, - .lookup_symbol = ef_lookup_symbol }; static void -ef_print_phdr(Elf_Phdr *phdr) +ef_print_phdr(GElf_Phdr *phdr) { if ((phdr->p_flags & PF_W) == 0) { @@ -133,53 +106,29 @@ ef_print_phdr(Elf_Phdr *phdr) } } -static Elf_Off -ef_get_offset(elf_file_t ef, Elf_Off off) +static GElf_Off +ef_get_offset(elf_file_t ef, GElf_Addr addr) { - Elf_Phdr *ph; + GElf_Phdr *ph; int i; for (i = 0; i < ef->ef_nsegs; i++) { ph = ef->ef_segs[i]; - if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { - return (ph->p_offset + (off - ph->p_vaddr)); + if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) { + return (ph->p_offset + (addr - ph->p_vaddr)); } } return (0); } -static int -ef_get_type(elf_file_t ef) -{ - - return (ef->ef_type); -} - /* - * next three functions copied from link_elf.c + * next two functions copied from link_elf.c */ -static unsigned long -elf_hash(const char *name) -{ - unsigned long h, g; - const unsigned char *p; - - h = 0; - p = (const unsigned char *)name; - while (*p != '\0') { - h = (h << 4) + *p++; - if ((g = h & 0xf0000000) != 0) - h ^= g >> 24; - h &= ~g; - } - return (h); -} - static int -ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) +ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) { unsigned long hash, symnum; - Elf_Sym *symp; + GElf_Sym *symp; char *strp; /* First, search hashed global symbols */ @@ -205,7 +154,7 @@ ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { + GELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = symp; return (0); } else @@ -219,10 +168,10 @@ ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) } static int -ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, - long *countp) +ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, + GElf_Addr *stopp, long *countp) { - Elf_Sym *sym; + GElf_Sym *sym; char *setsym; int error, len; @@ -246,258 +195,340 @@ ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, *stopp = sym->st_value; /* and the number of entries */ - *countp = (*stopp - *startp) / sizeof(void *); + *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile); out: free(setsym); return (error); } -static Elf_Addr -ef_symaddr(elf_file_t ef, Elf_Size symidx) +static GElf_Addr +ef_symaddr(elf_file_t ef, GElf_Size symidx) { - const Elf_Sym *sym; + const GElf_Sym *sym; if (symidx >= ef->ef_nchains) return (0); sym = ef->ef_symtab + symidx; - if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && + 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) +ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) { - Elf_Dyn *dp; - Elf_Hashelt hashhdr[2]; + GElf_Shdr *shdr; + GElf_Dyn *dyn, *dp; + size_t i, ndyn, nshdr, nsym; int error; - Elf_Off rel_off; - Elf_Off rela_off; + GElf_Off hash_off, sym_off, str_off; + GElf_Off rel_off; + GElf_Off rela_off; int rel_sz; int rela_sz; - int rel_entry; - int rela_entry; + 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) { + if (shdr[i].sh_offset != phdyn->p_offset || + 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; - rel_off = rela_off = 0; + hash_off = rel_off = rela_off = sym_off = str_off = 0; rel_sz = rela_sz = 0; - rel_entry = rela_entry = 0; - for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { + for (i = 0; i < ndyn; i++) { + dp = &dyn[i]; + if (dp->d_tag == DT_NULL) + break; + switch (dp->d_tag) { case DT_HASH: - error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), - sizeof(hashhdr), hashhdr); - if (error != 0) { - warnx("can't read hash header (%jx)", - (uintmax_t)ef_get_offset(ef, dp->d_un.d_ptr)); - return (error); - } - ef->ef_nbuckets = hashhdr[0]; - ef->ef_nchains = hashhdr[1]; - error = ef_read_entry(ef, -1, - (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt), - (void **)&ef->ef_hashtab); - if (error != 0) { - warnx("can't read hash table"); - return (error); - } - ef->ef_buckets = ef->ef_hashtab; - ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; + 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: - ef->ef_stroff = dp->d_un.d_ptr; - break; - case DT_STRSZ: - ef->ef_strsz = dp->d_un.d_val; + 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: - ef->ef_symoff = dp->d_un.d_ptr; + 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 != sizeof(Elf_Sym)) - return (EFTYPE); + 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"); - rel_off = dp->d_un.d_ptr; + 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"); - rel_sz = dp->d_un.d_val; + else + rel_sz = dp->d_un.d_val; break; case DT_RELENT: - if (rel_entry != 0) - warnx("second DT_RELENT entry ignored"); - rel_entry = dp->d_un.d_val; + 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"); - rela_off = dp->d_un.d_ptr; + else + rela_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_RELASZ: if (rela_sz != 0) - warnx("second DT_RELASZ entry ignored"); - rela_sz = dp->d_un.d_val; + warnx("second DT_RELSZ entry ignored"); + else + rela_sz = dp->d_un.d_val; break; case DT_RELAENT: - if (rela_entry != 0) - warnx("second DT_RELAENT entry ignored"); - rela_entry = dp->d_un.d_val; + if (dp->d_un.d_val != elf_object_size(ef->ef_efile, + ELF_T_RELA)) { + error = EFTYPE; + goto out; + } break; } } - if (ef->ef_symoff == 0) { + 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); - return (EFTYPE); + error = EFTYPE; + goto out; } - if (ef->ef_stroff == 0) { + if (str_off == 0) { warnx("%s: no .dynstr section found\n", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), - ef->ef_nchains * sizeof(Elf_Sym), - (void **)&ef->ef_symtab) != 0) { - if (ef->ef_verbose) - warnx("%s: can't load .dynsym section (0x%jx)", - ef->ef_name, (uintmax_t)ef->ef_symoff); - return (EIO); - } - if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, - (void **)&ef->ef_strtab) != 0) { - warnx("can't load .dynstr section"); - return (EIO); - } - if (rel_off != 0) { - if (rel_entry == 0) { - warnx("%s: no DT_RELENT for DT_REL", ef->ef_name); - return (EFTYPE); - } - if (rel_entry != sizeof(Elf_Rel)) { - warnx("%s: inconsistent DT_RELENT value", - ef->ef_name); - return (EFTYPE); - } - if (rel_sz % rel_entry != 0) { - warnx("%s: inconsistent values for DT_RELSZ and " - "DT_RELENT", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz, - (void **)&ef->ef_rel) != 0) { - warnx("%s: cannot load DT_REL section", ef->ef_name); - return (EIO); - } - ef->ef_relsz = rel_sz / rel_entry; - if (ef->ef_verbose) - warnx("%s: %d REL entries", ef->ef_name, - ef->ef_relsz); + error = EFTYPE; + goto out; } - if (rela_off != 0) { - if (rela_entry == 0) { - warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name); - return (EFTYPE); - } - if (rela_entry != sizeof(Elf_Rela)) { - warnx("%s: inconsistent DT_RELAENT value", - ef->ef_name); - return (EFTYPE); - } - if (rela_sz % rela_entry != 0) { - warnx("%s: inconsistent values for DT_RELASZ and " - "DT_RELAENT", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz, - (void **)&ef->ef_rela) != 0) { - warnx("%s: cannot load DT_RELA section", ef->ef_name); - return (EIO); - } - ef->ef_relasz = rela_sz / rela_entry; - if (ef->ef_verbose) - warnx("%s: %d RELA entries", ef->ef_name, - ef->ef_relasz); + if (rel_off == 0 && rela_off == 0) { + warnx("%s: no ELF relocation table found\n", ef->ef_name); + error = EFTYPE; + goto out; } - return (0); -} -static int -ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - ssize_t r; + 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; + } - if (offset != (Elf_Off)-1) { - if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) - return (EIO); - } + /* + * 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; + } - r = read(ef->ef_fd, dest, len); - if (r != -1 && (size_t)r == len) - return (0); - else - return (EIO); -} + 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; + } + } -static int -ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; + 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; + } - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); + error = 0; +out: + free(dyn); + free(shdr); return (error); } static int -ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) +ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) { - Elf_Off ofs; - - ofs = ef_get_offset(ef, offset); - if (ofs == 0) { - if (ef->ef_verbose) - warnx("ef_seg_read(%s): zero offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); - return (EFAULT); - } - return (ef_read(ef, ofs, len, dest)); -} - -static int -ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - Elf_Off ofs; - const Elf_Rela *a; - const Elf_Rel *r; + GElf_Off ofs; + const GElf_Rela *a; + const GElf_Rel *r; int error; - ofs = ef_get_offset(ef, offset); + ofs = ef_get_offset(ef, address); if (ofs == 0) { if (ef->ef_verbose) warnx("ef_seg_read_rel(%s): zero offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); + ef->ef_name, (uintmax_t)address, (uintmax_t)ofs); return (EFAULT); } - if ((error = ef_read(ef, ofs, len, dest)) != 0) + 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 = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len, - dest); + 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 = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len, - dest); + error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address, + len, dest); if (error != 0) return (error); } @@ -505,168 +536,115 @@ ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) } static int -ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest) +ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) { - Elf_Off ofs; - ssize_t r; + GElf_Off ofs; + int error; - ofs = ef_get_offset(ef, offset); - if (ofs == 0 || ofs == (Elf_Off)-1) { + ofs = ef_get_offset(ef, address); + if (ofs == 0 || ofs == (GElf_Off)-1) { if (ef->ef_verbose) warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); + ef->ef_name, (uintmax_t)address, (uintmax_t)ofs); return (EFAULT); } - r = pread(ef->ef_fd, dest, len, ofs); - if (r < 0) - return (errno); + error = elf_read_raw_data(ef->ef_efile, ofs, dest, len); + if (error != 0) + return (error); if (strnlen(dest, len) == len) return (EFAULT); return (0); } -static int -ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_seg_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - -static int -ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_seg_read_rel(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - int -ef_open(const char *filename, struct elf_file *efile, int verbose) +ef_open(struct elf_file *efile, int verbose) { elf_file_t ef; - Elf_Ehdr *hdr; - int fd; + GElf_Ehdr *hdr; + size_t i, nphdr, nsegs; int error; - int phlen, res; - int nsegs; - Elf_Phdr *phdr, *phdyn, *phlimit; + GElf_Phdr *phdr, *phdyn; - if (filename == NULL) - return (EINVAL); - if ((fd = open(filename, O_RDONLY)) == -1) - return (errno); + 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) { - close(fd); + 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_fd = fd; - ef->ef_name = strdup(filename); + ef->ef_name = strdup(efile->ef_filename); ef->ef_efile = efile; - hdr = (Elf_Ehdr *)&ef->ef_hdr; - do { - res = read(fd, hdr, sizeof(*hdr)); - error = EFTYPE; - if (res != sizeof(*hdr)) - break; - if (!IS_ELF(*hdr)) - break; - if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || - hdr->e_ident[EI_DATA] != ELF_TARG_DATA || - hdr->e_ident[EI_VERSION] != EV_CURRENT || - hdr->e_version != EV_CURRENT || - hdr->e_machine != ELF_TARG_MACH || - hdr->e_phentsize != sizeof(Elf_Phdr)) - break; - phlen = hdr->e_phnum * sizeof(Elf_Phdr); - if (ef_read_entry(ef, hdr->e_phoff, phlen, - (void **)&ef->ef_ph) != 0) - break; - phdr = ef->ef_ph; - phlimit = phdr + hdr->e_phnum; - nsegs = 0; - phdyn = NULL; - while (phdr < phlimit) { - if (verbose > 1) - ef_print_phdr(phdr); - switch (phdr->p_type) { - case PT_LOAD: - if (nsegs < MAXSEGS) - ef->ef_segs[nsegs] = phdr; - nsegs++; - break; - case PT_PHDR: - break; - case PT_DYNAMIC: - phdyn = phdr; - break; - } - phdr++; - } + + 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) - printf("\n"); - if (phdyn == NULL) { - warnx("Skipping %s: not dynamically-linked", - filename); + ef_print_phdr(phdr); + switch (phdr->p_type) { + case PT_LOAD: + if (nsegs < MAXSEGS) + ef->ef_segs[nsegs] = phdr; + nsegs++; break; - } else if (nsegs > MAXSEGS) { - warnx("%s: too many segments", filename); + case PT_PHDR: break; - } - ef->ef_nsegs = nsegs; - if (ef_read_entry(ef, phdyn->p_offset, - phdyn->p_filesz, (void **)&ef->ef_dyn) != 0) { - printf("ef_read_entry failed\n"); + case PT_DYNAMIC: + phdyn = phdr; break; } - error = ef_parse_dynamic(ef); - if (error != 0) - break; - if (hdr->e_type == ET_DYN) { - ef->ef_type = EFT_KLD; - error = 0; - } else if (hdr->e_type == ET_EXEC) { - ef->ef_type = EFT_KERNEL; - error = 0; - } else - break; - } while(0); + } + 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 int +static void ef_close(elf_file_t ef) { - - close(ef->ef_fd); + 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); - return (0); } diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h index 94aaca279a80..a96bd72d6931 100644 --- a/usr.sbin/kldxref/ef.h +++ b/usr.sbin/kldxref/ef.h @@ -35,71 +35,276 @@ #ifndef _EF_H_ #define _EF_H_ -#define EFT_KLD 1 -#define EFT_KERNEL 2 +#include <sys/linker_set.h> +#include <stdbool.h> -#define EF_RELOC_REL 1 -#define EF_RELOC_RELA 2 - -#define EF_GET_TYPE(ef) \ - (ef)->ef_ops->get_type((ef)->ef_ef) #define EF_CLOSE(ef) \ (ef)->ef_ops->close((ef)->ef_ef) -#define EF_READ(ef, offset, len, dest) \ - (ef)->ef_ops->read((ef)->ef_ef, offset, len, dest) -#define EF_READ_ENTRY(ef, offset, len, ptr) \ - (ef)->ef_ops->read_entry((ef)->ef_ef, offset, len, ptr) -#define EF_SEG_READ(ef, offset, len, dest) \ - (ef)->ef_ops->seg_read((ef)->ef_ef, offset, len, dest) -#define EF_SEG_READ_REL(ef, offset, len, dest) \ - (ef)->ef_ops->seg_read_rel((ef)->ef_ef, offset, len, dest) -#define EF_SEG_READ_STRING(ef, offset, len, dest) \ - (ef)->ef_ops->seg_read_string((ef)->ef_ef, offset, len, dest) -#define EF_SEG_READ_ENTRY(ef, offset, len, ptr) \ - (ef)->ef_ops->seg_read_entry((ef)->kf_ef, offset, len, ptr) -#define EF_SEG_READ_ENTRY_REL(ef, offset, len, ptr) \ - (ef)->ef_ops->seg_read_entry_rel((ef)->ef_ef, offset, len, ptr) +#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) -#define EF_LOOKUP_SYMBOL(ef, name, sym) \ - (ef)->ef_ops->lookup_symbol((ef)->ef_ef, name, sym) /* 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 { - int (*get_type)(elf_file_t ef); - int (*close)(elf_file_t ef); - int (*read)(elf_file_t ef, Elf_Off offset, size_t len, void* dest); - int (*read_entry)(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); - int (*seg_read)(elf_file_t ef, Elf_Off offset, size_t len, void *dest); - int (*seg_read_rel)(elf_file_t ef, Elf_Off offset, size_t len, + 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, Elf_Off offset, size_t len, + int (*seg_read_string)(elf_file_t ef, GElf_Addr address, size_t len, char *dest); - int (*seg_read_entry)(elf_file_t ef, Elf_Off offset, size_t len, - void**ptr); - int (*seg_read_entry_rel)(elf_file_t ef, Elf_Off offset, size_t len, - void**ptr); - Elf_Addr (*symaddr)(elf_file_t ef, Elf_Size symidx); - int (*lookup_set)(elf_file_t ef, const char *name, long *startp, - long *stopp, long *countp); - int (*lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym** sym); + 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_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 -int ef_open(const char *filename, struct elf_file *ef, int verbose); -int ef_obj_open(const char *filename, struct elf_file *ef, int verbose); -int ef_reloc(struct elf_file *ef, const void *reldata, int reltype, - Elf_Off relbase, Elf_Off dataoff, size_t len, void *dest); + +/* + * 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); + +/* + * 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, long *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, + long *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 /* _EF_H_*/ diff --git a/usr.sbin/kldxref/ef_aarch64.c b/usr.sbin/kldxref/ef_aarch64.c index 9c57457ae930..5bd521b28e38 100644 --- a/usr.sbin/kldxref/ef_aarch64.c +++ b/usr.sbin/kldxref/ef_aarch64.c @@ -25,12 +25,11 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> -#include <string.h> +#include <gelf.h> #include "ef.h" @@ -39,28 +38,29 @@ * target relocation address of the section, and `dataoff/len' is the region * that is to be relocated, and has been copied to *dest */ -int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *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) { - Elf_Addr *where, addend; - Elf_Size rtype; - const Elf_Rela *rela; + char *where; + Elf64_Addr addend; + GElf_Size rtype; + const GElf_Rela *rela; - if (reltype != EF_RELOC_RELA) + if (reltype != ELF_T_RELA) return (EINVAL); - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset); + rela = (const GElf_Rela *)reldata; + where = (char *)dest - dataoff + rela->r_offset; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); switch(rtype) { case R_AARCH64_RELATIVE: - *where = relbase + addend; + le64enc(where, relbase + addend); break; case R_AARCH64_ABS64: break; @@ -70,3 +70,5 @@ ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, } return (0); } + +ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_AARCH64, ef_aarch64_reloc); diff --git a/usr.sbin/kldxref/ef_amd64.c b/usr.sbin/kldxref/ef_amd64.c index 789894518329..729039daa509 100644 --- a/usr.sbin/kldxref/ef_amd64.c +++ b/usr.sbin/kldxref/ef_amd64.c @@ -27,11 +27,11 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> +#include <gelf.h> #include "ef.h" @@ -40,48 +40,48 @@ * target relocation address of the section, and `dataoff' is the target * relocation address of the data in `dest'. */ -int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *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) { - Elf64_Addr *where, val; - Elf32_Addr *where32, val32; - Elf_Addr addend, addr; - Elf_Size rtype, symidx; - const Elf_Rel *rel; - const Elf_Rela *rela; + char *where; + GElf_Addr val; + GElf_Addr addend, addr; + GElf_Size rtype, symidx; + const GElf_Rel *rel; + const GElf_Rela *rela; switch (reltype) { - case EF_RELOC_REL: - rel = (const Elf_Rel *)reldata; - where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff); + case ELF_T_REL: + rel = (const GElf_Rel *)reldata; + where = (char *)dest + relbase + rel->r_offset - dataoff; addend = 0; - rtype = ELF_R_TYPE(rel->r_info); - symidx = ELF_R_SYM(rel->r_info); + rtype = GELF_R_TYPE(rel->r_info); + symidx = GELF_R_SYM(rel->r_info); break; - case EF_RELOC_RELA: - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff); + case ELF_T_RELA: + rela = (const GElf_Rela *)reldata; + where = (char *)dest + relbase + rela->r_offset - dataoff; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); - symidx = ELF_R_SYM(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); + symidx = GELF_R_SYM(rela->r_info); break; default: return (EINVAL); } - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); - if (reltype == EF_RELOC_REL) { + 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 = *(Elf32_Addr *)where; + addend = le32dec(where); break; default: - addend = *where; + addend = le64dec(where); break; } } @@ -92,25 +92,26 @@ ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, case R_X86_64_64: /* S + A */ addr = EF_SYMADDR(ef, symidx); val = addr + addend; - *where = val; + le64enc(where, val); break; case R_X86_64_32S: /* S + A sign extend */ addr = EF_SYMADDR(ef, symidx); - val32 = (Elf32_Addr)(addr + addend); - where32 = (Elf32_Addr *)where; - *where32 = val32; + val = (Elf32_Addr)(addr + addend); + le32enc(where, val); break; case R_X86_64_GLOB_DAT: /* S */ addr = EF_SYMADDR(ef, symidx); - *where = addr; + le64enc(where, addr); break; case R_X86_64_RELATIVE: /* B + A */ - addr = (Elf_Addr)addend + relbase; + addr = addend + relbase; val = addr; - *where = val; + le64enc(where, val); break; default: warnx("unhandled relocation type %d", (int)rtype); } return (0); } + +ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_X86_64, ef_amd64_reloc); diff --git a/usr.sbin/kldxref/ef_i386.c b/usr.sbin/kldxref/ef_i386.c index 3e637cd62525..e4f73877c430 100644 --- a/usr.sbin/kldxref/ef_i386.c +++ b/usr.sbin/kldxref/ef_i386.c @@ -27,11 +27,11 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> +#include <gelf.h> #include "ef.h" @@ -40,57 +40,59 @@ * target relocation address of the section, and `dataoff' is the target * relocation address of the data in `dest'. */ -int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *_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) { - Elf_Addr *where, addr, addend; - Elf_Size rtype, symidx; - const Elf_Rel *rel; - const Elf_Rela *rela; - char *dest = _dest; + char *where; + GElf_Addr addr, addend; + GElf_Size rtype, symidx; + const GElf_Rel *rel; + const GElf_Rela *rela; switch (reltype) { - case EF_RELOC_REL: - rel = (const Elf_Rel *)reldata; - where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff); + case ELF_T_REL: + rel = (const GElf_Rel *)reldata; + where = (char *)dest + relbase + rel->r_offset - dataoff; addend = 0; - rtype = ELF_R_TYPE(rel->r_info); - symidx = ELF_R_SYM(rel->r_info); + rtype = GELF_R_TYPE(rel->r_info); + symidx = GELF_R_SYM(rel->r_info); break; - case EF_RELOC_RELA: - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff); + case ELF_T_RELA: + rela = (const GElf_Rela *)reldata; + where = (char *)dest + relbase + rela->r_offset - dataoff; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); - symidx = ELF_R_SYM(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); + symidx = GELF_R_SYM(rela->r_info); break; default: return (EINVAL); } - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); - if (reltype == EF_RELOC_REL) - addend = *where; + if (reltype == ELF_T_REL) + addend = le32dec(where); switch (rtype) { case R_386_RELATIVE: /* A + B */ - addr = (Elf_Addr)addend + relbase; - *where = addr; + addr = addend + relbase; + le32enc(where, addr); break; case R_386_32: /* S + A - P */ addr = EF_SYMADDR(ef, symidx); addr += addend; - *where = addr; + le32enc(where, addr); break; case R_386_GLOB_DAT: /* S */ addr = EF_SYMADDR(ef, symidx); - *where = addr; + 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/usr.sbin/kldxref/ef_mips.c b/usr.sbin/kldxref/ef_mips.c index 2897b4e7a235..edc99a7d2505 100644 --- a/usr.sbin/kldxref/ef_mips.c +++ b/usr.sbin/kldxref/ef_mips.c @@ -30,11 +30,11 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> +#include <gelf.h> #include "ef.h" @@ -43,55 +43,78 @@ * target relocation address of the section, and `dataoff' is the target * relocation address of the data in `dest'. */ -int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *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) { - Elf_Addr *where, val; - const Elf_Rel *rel; - const Elf_Rela *rela; - Elf_Addr addend, addr; - Elf_Size rtype, symidx; + char *where; + GElf_Addr val; + const GElf_Rel *rel; + const GElf_Rela *rela; + GElf_Addr addend, addr; + GElf_Size rtype, symidx; switch (reltype) { - case EF_RELOC_REL: - rel = (const Elf_Rel *)reldata; - where = (Elf_Addr *)((char *)dest + relbase + rel->r_offset - - dataoff); + case ELF_T_REL: + rel = (const GElf_Rel *)reldata; + where = (char *)dest + relbase + rel->r_offset - dataoff; addend = 0; - rtype = ELF_R_TYPE(rel->r_info); - symidx = ELF_R_SYM(rel->r_info); + rtype = GELF_R_TYPE(rel->r_info); + symidx = GELF_R_SYM(rel->r_info); break; - case EF_RELOC_RELA: - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *)((char *)dest + relbase + rela->r_offset - - dataoff); + case ELF_T_RELA: + rela = (const GElf_Rela *)reldata; + where = (char *)dest + relbase + rela->r_offset - dataoff; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); - symidx = ELF_R_SYM(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); + symidx = GELF_R_SYM(rela->r_info); break; default: return (EINVAL); } - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); - if (reltype == EF_RELOC_REL) + 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); + } addend = *where; + } switch (rtype) { -#ifdef __LP64__ case R_MIPS_64: /* S + A */ -#else + addr = EF_SYMADDR(ef, symidx); + val = addr + addend; + if (elf_encoding(ef) == ELFDATA2LSB) + le64enc(where, val); + else + be64enc(where, val); + break; case R_MIPS_32: /* S + A */ -#endif addr = EF_SYMADDR(ef, symidx); val = addr + addend; - *where = val; + if (elf_encoding(ef) == ELFDATA2LSB) + le32enc(where, val); + else + be32enc(where, val); 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/usr.sbin/kldxref/ef_nop.c b/usr.sbin/kldxref/ef_nop.c deleted file mode 100644 index 23e779d3f6c5..000000000000 --- a/usr.sbin/kldxref/ef_nop.c +++ /dev/null @@ -1,40 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2003 Jake Burkholder. - * 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/types.h> -#include <machine/elf.h> - -#include "ef.h" - -int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *dest) -{ - - return (0); -} diff --git a/usr.sbin/kldxref/ef_obj.c b/usr.sbin/kldxref/ef_obj.c index 027408876a5e..a1d46241b803 100644 --- a/usr.sbin/kldxref/ef_obj.c +++ b/usr.sbin/kldxref/ef_obj.c @@ -35,48 +35,42 @@ */ #include <sys/param.h> -#include <sys/linker.h> #include <err.h> #include <errno.h> -#include <fcntl.h> +#include <gelf.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <machine/elf.h> #include "ef.h" typedef struct { - void *addr; - Elf_Off size; + char *addr; + GElf_Off size; int flags; int sec; /* Original section */ char *name; } Elf_progent; typedef struct { - Elf_Rel *rel; - int nrel; + GElf_Rel *rel; + long nrel; int sec; } Elf_relent; typedef struct { - Elf_Rela *rela; - int nrela; + GElf_Rela *rela; + long nrela; int sec; } Elf_relaent; struct ef_file { char *ef_name; - int ef_fd; - Elf_Ehdr ef_hdr; struct elf_file *ef_efile; - caddr_t address; - Elf_Off size; - Elf_Shdr *e_shdr; + char *address; + GElf_Off size; Elf_progent *progtab; int nprogtab; @@ -87,7 +81,7 @@ struct ef_file { Elf_relent *reltab; int nrel; - Elf_Sym *ddbsymtab; /* The symbol table we are using */ + GElf_Sym *ddbsymtab; /* The symbol table we are using */ long ddbsymcnt; /* Number of symbols */ caddr_t ddbstrtab; /* String table */ long ddbstrcnt; /* number of bytes in string table */ @@ -98,54 +92,31 @@ struct ef_file { int ef_verbose; }; -static int ef_obj_get_type(elf_file_t ef); -static int ef_obj_close(elf_file_t ef); -static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, - void* dest); -static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); -static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, - void *dest); -static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, - void *dest); -static int ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, +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 int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); -static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, - size_t len, void **ptr); -static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx); -static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, - long *stopp, long *countp); -static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, - Elf_Sym** sym); + +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 = { - .get_type = ef_obj_get_type, .close = ef_obj_close, - .read = ef_obj_read, - .read_entry = ef_obj_read_entry, - .seg_read = ef_obj_seg_read, .seg_read_rel = ef_obj_seg_read_rel, .seg_read_string = ef_obj_seg_read_string, - .seg_read_entry = ef_obj_seg_read_entry, - .seg_read_entry_rel = ef_obj_seg_read_entry_rel, .symaddr = ef_obj_symaddr, .lookup_set = ef_obj_lookup_set, - .lookup_symbol = ef_obj_lookup_symbol }; static int -ef_obj_get_type(elf_file_t __unused ef) -{ - - return (EFT_KLD); -} - -static int -ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) +ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) { - Elf_Sym *symp; + GElf_Sym *symp; const char *strp; int i; @@ -160,102 +131,58 @@ ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) } static int -ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, - long *countp) +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 = (char *)ef->progtab[i].addr - ef->address; - *stopp = (char *)ef->progtab[i].addr + - ef->progtab[i].size - ef->address; - *countp = (*stopp - *startp) / sizeof(void *); + *startp = ef->progtab[i].addr - ef->address; + *stopp = ef->progtab[i].addr + ef->progtab[i].size - + ef->address; + *countp = (*stopp - *startp) / + elf_pointer_size(ef->ef_efile); return (0); } } return (ESRCH); } -static Elf_Addr -ef_obj_symaddr(elf_file_t ef, Elf_Size symidx) +static GElf_Addr +ef_obj_symaddr(elf_file_t ef, GElf_Size symidx) { - const Elf_Sym *sym; + const GElf_Sym *sym; - if (symidx >= (size_t) ef->ddbsymcnt) + if (symidx >= (size_t)ef->ddbsymcnt) return (0); sym = ef->ddbsymtab + symidx; if (sym->st_shndx != SHN_UNDEF) - return (sym->st_value - (Elf_Addr)ef->address); + return (sym->st_value - (GElf_Addr)ef->address); return (0); } static int -ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - ssize_t r; - - if (offset != (Elf_Off)-1) { - if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) - return (EIO); - } - - r = read(ef->ef_fd, dest, len); - if (r != -1 && (size_t)r == len) - return (0); - else - return (EIO); -} - -static int -ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_obj_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - -static int -ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - - if (offset + len > ef->size) { - if (ef->ef_verbose) - warnx("ef_obj_seg_read(%s): bad offset/len (%lx:%ld)", - ef->ef_name, (long)offset, (long)len); - return (EFAULT); - } - bcopy(ef->address + offset, dest, len); - return (0); -} - -static int -ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) +ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) { char *memaddr; - Elf_Rel *r; - Elf_Rela *a; - Elf_Off secbase, dataoff; + GElf_Rel *r; + GElf_Rela *a; + GElf_Addr secbase, dataoff; int error, i, sec; - if (offset + len > ef->size) { + if (address + len > ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read_rel(%s): bad offset/len (%lx:%ld)", - ef->ef_name, (long)offset, (long)len); + ef->ef_name, (long)address, (long)len); return (EFAULT); } - bcopy(ef->address + offset, dest, len); + bcopy(ef->address + address, dest, len); /* Find out which section contains the data. */ - memaddr = ef->address + offset; + memaddr = ef->address + address; sec = -1; secbase = dataoff = 0; for (i = 0; i < ef->nprogtab; i++) { @@ -280,7 +207,7 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) continue; for (r = ef->reltab[i].rel; r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { - error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secbase, + error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase, dataoff, len, dest); if (error != 0) return (error); @@ -291,8 +218,8 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) continue; for (a = ef->relatab[i].rela; a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { - error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, - secbase, dataoff, len, dest); + error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase, + dataoff, len, dest); if (error != 0) return (error); } @@ -301,117 +228,65 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) } static int -ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest) +ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) { - if (offset >= ef->size) { + if (address >= ef->size) { if (ef->ef_verbose) - warnx("ef_obj_seg_read_string(%s): bad offset (%lx)", - ef->ef_name, (long)offset); + warnx("ef_obj_seg_read_string(%s): bad address (%lx)", + ef->ef_name, (long)address); return (EFAULT); } - if (ef->size - offset < len) - len = ef->size - offset; + if (ef->size - address < len) + len = ef->size - address; - if (strnlen(ef->address + offset, len) == len) + if (strnlen(ef->address + address, len) == len) return (EFAULT); - memcpy(dest, ef->address + offset, len); + memcpy(dest, ef->address + address, len); return (0); } -static int -ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_obj_seg_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - -static int -ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_obj_seg_read_rel(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - int -ef_obj_open(const char *filename, struct elf_file *efile, int verbose) +ef_obj_open(struct elf_file *efile, int verbose) { elf_file_t ef; - Elf_Ehdr *hdr; - Elf_Shdr *shdr; - Elf_Sym *es; + GElf_Ehdr *hdr; + GElf_Shdr *shdr; + GElf_Sym *es; char *mapbase; - void *vtmp; - size_t mapsize, alignmask, max_addralign; - int error, fd, pb, ra, res, rl; - int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex; - - if (filename == NULL) - return (EINVAL); - if ((fd = open(filename, O_RDONLY)) == -1) - return (errno); + size_t i, mapsize, alignmask, max_addralign, 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) { - close(fd); + if (ef == NULL) return (errno); - } efile->ef_ef = ef; efile->ef_ops = &ef_obj_file_ops; ef->ef_verbose = verbose; - ef->ef_fd = fd; - ef->ef_name = strdup(filename); + ef->ef_name = strdup(efile->ef_filename); ef->ef_efile = efile; - hdr = (Elf_Ehdr *)&ef->ef_hdr; - res = read(fd, hdr, sizeof(*hdr)); - error = EFTYPE; - if (res != sizeof(*hdr)) - goto out; - if (!IS_ELF(*hdr)) - goto out; - if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || - hdr->e_ident[EI_DATA] != ELF_TARG_DATA || - hdr->e_ident[EI_VERSION] != EV_CURRENT || - hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || - hdr->e_type != ET_REL) - goto out; - - nbytes = hdr->e_shnum * hdr->e_shentsize; - if (nbytes == 0 || hdr->e_shoff == 0 || - hdr->e_shentsize != sizeof(Elf_Shdr)) - goto out; - - if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) { - printf("ef_read_entry failed\n"); + error = elf_read_shdrs(efile, &nshdr, &shdr); + if (error != 0) { + shdr = NULL; goto out; } - ef->e_shdr = shdr = vtmp; - /* Scan the section header for information and table sizing. */ + /* Scan the section headers for information and table sizing. */ nsym = 0; symtabindex = -1; symstrindex = -1; - for (i = 0; i < hdr->e_shnum; i++) { + for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: @@ -434,16 +309,16 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) } if (ef->nprogtab == 0) { - warnx("%s: file has no contents", filename); + warnx("%s: file has no contents", ef->ef_name); goto out; } if (nsym != 1) { - warnx("%s: file has no valid symbol table", filename); + warnx("%s: file has no valid symbol table", ef->ef_name); goto out; } - if (symstrindex < 0 || symstrindex > hdr->e_shnum || + if (symstrindex < 0 || symstrindex > nshdr || shdr[symstrindex].sh_type != SHT_STRTAB) { - warnx("%s: file has invalid symbol strings", filename); + warnx("%s: file has invalid symbol strings", ef->ef_name); goto out; } @@ -462,29 +337,24 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) goto out; } - ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym); - if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset, - shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) { - printf("ef_read_entry failed\n"); + if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt, + &ef->ddbsymtab) != 0) { + printf("elf_read_symbols failed\n"); goto out; } - ef->ddbstrcnt = shdr[symstrindex].sh_size; - if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset, - shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) { - printf("ef_read_entry failed\n"); + 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? */ - shstrindex = -1; if (hdr->e_shstrndx != 0 && shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { - shstrindex = hdr->e_shstrndx; - ef->shstrcnt = shdr[shstrindex].sh_size; - if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset, - shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) { - printf("ef_read_entry failed\n"); + if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx], + &ef->shstrcnt, &ef->shstrtab) != 0) { + printf("elf_read_string_table failed\n"); goto out; } } @@ -493,7 +363,7 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) alignmask = 0; max_addralign = 0; mapsize = 0; - for (i = 0; i < hdr->e_shnum; i++) { + for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: @@ -523,19 +393,19 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) rl = 0; ra = 0; alignmask = 0; - for (i = 0; i < hdr->e_shnum; i++) { + for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: alignmask = shdr[i].sh_addralign - 1; mapbase += alignmask; - mapbase = (char *)((uintptr_t)mapbase & ~alignmask); + mapbase = (char *)((uintptr_t)mapbase & ~alignmask); ef->progtab[pb].addr = (void *)(uintptr_t)mapbase; if (shdr[i].sh_type == SHT_PROGBITS) { ef->progtab[pb].name = "<<PROGBITS>>"; - if (ef_obj_read(ef, shdr[i].sh_offset, - shdr[i].sh_size, - ef->progtab[pb].addr) != 0) { + if (elf_read_raw_data(efile, + shdr[i].sh_offset, ef->progtab[pb].addr, + shdr[i].sh_size) != 0) { printf("failed to read progbits\n"); goto out; } @@ -554,30 +424,25 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) es = &ef->ddbsymtab[j]; if (es->st_shndx != i) continue; - es->st_value += (Elf_Addr)ef->progtab[pb].addr; + es->st_value += (GElf_Addr)ef->progtab[pb].addr; } mapbase += shdr[i].sh_size; pb++; break; case SHT_REL: - ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel); ef->reltab[rl].sec = shdr[i].sh_info; - if (ef_obj_read_entry(ef, shdr[i].sh_offset, - shdr[i].sh_size, (void**)&ef->reltab[rl].rel) != - 0) { - printf("ef_read_entry failed\n"); + 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].nrela = - shdr[i].sh_size / sizeof(Elf_Rela); ef->relatab[ra].sec = shdr[i].sh_info; - if (ef_obj_read_entry(ef, shdr[i].sh_offset, - shdr[i].sh_size, (void**)&ef->relatab[ra].rela) != - 0) { - printf("ef_read_entry failed\n"); + if (elf_read_rela(efile, i, &ef->relatab[ra].nrela, + &ef->relatab[ra].rela) != 0) { + printf("elf_read_rela failed\n"); goto out; } ra++; @@ -586,21 +451,19 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose) } error = 0; out: + free(shdr); if (error != 0) ef_obj_close(ef); return (error); } -static int +static void ef_obj_close(elf_file_t ef) { int i; - close(ef->ef_fd); if (ef->ef_name) free(ef->ef_name); - if (ef->e_shdr != NULL) - free(ef->e_shdr); if (ef->size != 0) free(ef->address); if (ef->nprogtab != 0) @@ -626,6 +489,4 @@ ef_obj_close(elf_file_t ef) ef->ef_efile->ef_ops = NULL; ef->ef_efile->ef_ef = NULL; free(ef); - - return (0); } diff --git a/usr.sbin/kldxref/ef_powerpc.c b/usr.sbin/kldxref/ef_powerpc.c index 2d84104248a5..068c8117e4a6 100644 --- a/usr.sbin/kldxref/ef_powerpc.c +++ b/usr.sbin/kldxref/ef_powerpc.c @@ -27,63 +27,67 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> -#include <inttypes.h> -#include <string.h> +#include <gelf.h> #include "ef.h" -#ifdef __powerpc64__ -#define PRI_ELF_SIZE PRIu64 -#else -#define PRI_ELF_SIZE PRIu32 -#endif - /* * 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 -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *dest) +ef_ppc_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest) { - Elf_Addr *where, addend; - Elf32_Addr *where32; - Elf_Size rtype, symidx; - const Elf_Rela *rela; + char *where; + GElf_Addr addend, val; + GElf_Size rtype, symidx; + const GElf_Rela *rela; - if (reltype != EF_RELOC_RELA) + if (reltype != ELF_T_RELA) return (EINVAL); - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset); - where32 = (Elf32_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset); + rela = (const GElf_Rela *)reldata; + where = (char *)dest - dataoff + rela->r_offset; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); - symidx = ELF_R_SYM(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); + symidx = GELF_R_SYM(rela->r_info); - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); switch (rtype) { case R_PPC_RELATIVE: /* word32|doubleword64 B + A */ - *where = relbase + addend; + val = relbase + addend; + if (elf_class(ef) == ELFCLASS64) { + if (elf_encoding(ef) == ELFDATA2LSB) + le64enc(where, val); + else + be64enc(where, val); + } break; case R_PPC_ADDR32: /* word32 S + A */ - *where32 = EF_SYMADDR(ef, symidx) + addend; + val = EF_SYMADDR(ef, symidx) + addend; + be32enc(where, val); break; -#ifdef __powerpc64__ case R_PPC64_ADDR64: /* doubleword64 S + A */ - *where = EF_SYMADDR(ef, symidx) + addend; + val = EF_SYMADDR(ef, symidx) + addend; + if (elf_encoding(ef) == ELFDATA2LSB) + le64enc(where, val); + else + be64enc(where, val); break; -#endif default: - warnx("unhandled relocation type %" PRI_ELF_SIZE, rtype); + 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/usr.sbin/kldxref/ef_riscv.c b/usr.sbin/kldxref/ef_riscv.c index c5ba17ed1ae1..38299a1e9b46 100644 --- a/usr.sbin/kldxref/ef_riscv.c +++ b/usr.sbin/kldxref/ef_riscv.c @@ -30,50 +30,51 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <machine/elf.h> +#include <sys/endian.h> #include <err.h> #include <errno.h> +#include <gelf.h> #include "ef.h" int -ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase, - Elf_Off dataoff, size_t len, void *dest) +ef_riscv_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest) { - Elf_Addr *where; - const Elf_Rela *rela; - Elf_Addr addend, addr; - Elf_Size rtype, symidx; + char *where; + const GElf_Rela *rela; + GElf_Addr addend, addr; + GElf_Size rtype, symidx; switch (reltype) { - case EF_RELOC_RELA: - rela = (const Elf_Rela *)reldata; - where = (Elf_Addr *)((char *)dest + relbase + rela->r_offset - - dataoff); + case ELF_T_RELA: + rela = (const GElf_Rela *)reldata; + where = (char *)dest + relbase + rela->r_offset - dataoff; addend = rela->r_addend; - rtype = ELF_R_TYPE(rela->r_info); - symidx = ELF_R_SYM(rela->r_info); + rtype = GELF_R_TYPE(rela->r_info); + symidx = GELF_R_SYM(rela->r_info); break; default: return (EINVAL); } - if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len) + if (where < (char *)dest || where >= (char *)dest + len) return (0); switch (rtype) { case R_RISCV_64: /* S + A */ addr = EF_SYMADDR(ef, symidx) + addend; - *where = addr; + le64enc(where, addr); break; case R_RISCV_RELATIVE: /* B + A */ addr = addend + relbase; - *where = addr; + 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/usr.sbin/kldxref/elf.c b/usr.sbin/kldxref/elf.c new file mode 100644 index 000000000000..a30eb4456a76 --- /dev/null +++ b/usr.sbin/kldxref/elf.c @@ -0,0 +1,674 @@ +/*- + * 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. HR001122S0003 + * ("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 "ef.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; + + if (offset != (off_t)-1) { + if (lseek(efile->ef_fd, offset, SEEK_SET) == -1) + return (EIO); + } + nread = read(efile->ef_fd, dst, len); + 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_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, long *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, long *nsymp, + GElf_Sym **symp) +{ + GElf_Shdr shdr; + Elf_Scn *scn; + Elf_Data *data; + GElf_Sym *sym; + long 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: + __builtin_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/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c index 913b64ee2a16..68f010a6db98 100644 --- a/usr.sbin/kldxref/kldxref.c +++ b/usr.sbin/kldxref/kldxref.c @@ -32,27 +32,24 @@ * SUCH DAMAGE. */ -#include <sys/types.h> #include <sys/param.h> #include <sys/endian.h> -#include <sys/exec.h> #include <sys/queue.h> -#include <sys/kernel.h> -#include <sys/reboot.h> -#include <sys/linker.h> #include <sys/stat.h> #include <sys/module.h> +#include <assert.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <fts.h> +#include <gelf.h> +#include <libelf.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <machine/elf.h> #include "ef.h" @@ -63,6 +60,9 @@ static bool dflag; /* do not create a hint file, only write on stdout */ static int verbose; static FILE *fxref; /* current hints file */ +static int byte_order; +static GElf_Ehdr ehdr; +static char *ehdr_filename; static const char *xref_file = "linker.hints"; @@ -81,6 +81,19 @@ intalign(void) } static void +write_int(int val) +{ + char buf[4]; + + assert(byte_order != ELFDATANONE); + if (byte_order == ELFDATA2LSB) + le32enc(buf, val); + else + be32enc(buf, val); + fwrite(buf, sizeof(buf), 1, fxref); +} + +static void record_start(void) { @@ -92,11 +105,24 @@ static int record_end(void) { - if (recpos == 0) + if (recpos == 0) { + /* + * Pretend to have written a record in debug mode so + * the architecture check works. + */ + if (dflag) + reccnt++; return (0); + } + + if (reccnt == 0) { + /* File version record. */ + write_int(1); + } + reccnt++; intalign(); - fwrite(&recpos, sizeof(recpos), 1, fxref); + write_int(recpos); return (fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0); } @@ -112,14 +138,21 @@ record_buf(const void *buf, size_t size) } /* - * An int is stored in host order and aligned + * An int is stored in target byte order and aligned */ static int record_int(int val) { + char buf[4]; + + assert(byte_order != ELFDATANONE); + if (byte_order == ELFDATA2LSB) + le32enc(buf, val); + else + be32enc(buf, val); intalign(); - return (record_buf(&val, sizeof(val))); + return (record_buf(buf, sizeof(buf))); } /* @@ -227,7 +260,8 @@ typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list; * sign extension to uint32_t to simplify parsing downstream. */ static int -parse_pnp_list(const char *desc, char **new_desc, pnp_list *list) +parse_pnp_list(struct elf_file *ef, const char *desc, char **new_desc, + pnp_list *list) { const char *walker, *ep; const char *colon, *semi; @@ -274,7 +308,7 @@ parse_pnp_list(const char *desc, char **new_desc, pnp_list *list) printf("Found type %s for name %s\n", type, key); /* Skip pointer place holders */ if (strcmp(type, "P") == 0) { - off += sizeof(void *); + off += elf_pointer_size(ef); continue; } @@ -333,8 +367,8 @@ parse_pnp_list(const char *desc, char **new_desc, pnp_list *list) /* doesn't actually consume space in the table */ off = elt->pe_offset; } else { - elt->pe_offset = roundup2(elt->pe_offset, sizeof(void *)); - off = elt->pe_offset + sizeof(void *); + elt->pe_offset = roundup2(elt->pe_offset, elf_pointer_size(ef)); + off = elt->pe_offset + elf_pointer_size(ef); } if (elt->pe_kind & TYPE_PAIRED) { char *word, *ctx, newtype; @@ -392,6 +426,24 @@ free_pnp_list(char *new_desc, pnp_list *list) free(new_desc); } +static uint16_t +parse_16(const void *p) +{ + if (byte_order == ELFDATA2LSB) + return (le16dec(p)); + else + return (be16dec(p)); +} + +static uint32_t +parse_32(const void *p) +{ + if (byte_order == ELFDATA2LSB) + return (le32dec(p)); + else + return (be32dec(p)); +} + static void parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) { @@ -402,7 +454,7 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) char buffer[1024]; if (elt->pe_kind == TYPE_W32) { - memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); + v4 = parse_32(walker + elt->pe_offset); value = v4 & 0xffff; record_int(value); if (verbose > 1) @@ -421,14 +473,14 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) value = v1; break; case 2: - memcpy(&v2, walker + elt->pe_offset, sizeof(v2)); + v2 = parse_16(walker + elt->pe_offset); if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff) value = -1; else value = v2; break; case 4: - memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); + v4 = parse_32(walker + elt->pe_offset); if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff) value = -1; else @@ -444,16 +496,17 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) /* Do nothing */ } else { /* E, Z or D -- P already filtered */ if (elt->pe_kind == TYPE_E) { - memcpy(&v4, walker + elt->pe_offset, sizeof(v4)); + v4 = parse_32(walker + elt->pe_offset); strcpy(buffer, pnp_eisaformat(v4)); } else { - char *ptr; + GElf_Addr address; - ptr = *(char **)(walker + elt->pe_offset); + address = elf_address_from_pointer(ef, walker + + elt->pe_offset); buffer[0] = '\0'; - if (ptr != NULL) { - EF_SEG_READ_STRING(ef, (Elf_Off)ptr, - sizeof(buffer), buffer); + if (address != 0) { + elf_read_string(ef, address, buffer, + sizeof(buffer)); buffer[sizeof(buffer) - 1] = '\0'; } } @@ -466,7 +519,7 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker) static void record_pnp_info(struct elf_file *ef, const char *cval, - struct mod_pnp_match_info *pnp, const char *descr) + struct Gmod_pnp_match_info *pnp, const char *descr) { pnp_list list; struct pnp_elt *elt; @@ -483,17 +536,15 @@ record_pnp_info(struct elf_file *ef, const char *cval, * Parse descr to weed out the chaff and to create a list * of offsets to output. */ - parse_pnp_list(descr, &new_descr, &list); + parse_pnp_list(ef, descr, &new_descr, &list); record_int(MDT_PNP_INFO); record_string(cval); record_string(new_descr); record_int(pnp->num_entry); len = pnp->num_entry * pnp->entry_len; - table = malloc(len); - error = EF_SEG_READ_REL(ef, (Elf_Off)pnp->table, len, table); + error = elf_read_relocated_data(ef, pnp->table, len, &table); if (error != 0) { free_pnp_list(new_descr, &list); - free(table); return; } @@ -517,29 +568,29 @@ record_pnp_info(struct elf_file *ef, const char *cval, } static int -parse_entry(struct mod_metadata *md, const char *cval, +parse_entry(struct Gmod_metadata *md, const char *cval, struct elf_file *ef, const char *kldname) { - struct mod_depend mdp; - struct mod_version mdv; - struct mod_pnp_match_info pnp; + struct Gmod_depend mdp; + struct Gmod_version mdv; + struct Gmod_pnp_match_info pnp; char descr[1024]; - Elf_Off data; + GElf_Addr data; int error; - data = (Elf_Off)md->md_data; + data = md->md_data; error = 0; record_start(); switch (md->md_type) { case MDT_DEPEND: if (!dflag) break; - check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp)); + check(elf_read_mod_depend(ef, data, &mdp)); printf(" depends on %s.%d (%d,%d)\n", cval, mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum); break; case MDT_VERSION: - check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv)); + check(elf_read_mod_version(ef, data, &mdv)); if (dflag) { printf(" interface %s.%d\n", cval, mdv.mv_version); } else { @@ -559,9 +610,8 @@ parse_entry(struct mod_metadata *md, const char *cval, } break; case MDT_PNP_INFO: - check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp)); - check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr)); - descr[sizeof(descr) - 1] = '\0'; + check(elf_read_mod_pnp_match_info(ef, data, &pnp)); + check(elf_read_string(ef, pnp.descr, descr, sizeof(descr))); if (dflag) { printf(" pnp info for bus %s format %s %d entries of %d bytes\n", cval, descr, pnp.num_entry, pnp.entry_len); @@ -580,34 +630,35 @@ parse_entry(struct mod_metadata *md, const char *cval, static int read_kld(char *filename, char *kldname) { - struct mod_metadata md; + struct Gmod_metadata md; struct elf_file ef; - void **p; - int error, eftype; - long start, finish, entries, i; + GElf_Addr *p; + int error; + long entries, i; char cval[MAXMODNAME + 1]; if (verbose || dflag) printf("%s\n", filename); - error = ef_open(filename, &ef, verbose); - if (error != 0) { - error = ef_obj_open(filename, &ef, verbose); - if (error != 0) { - if (verbose) - warnc(error, "elf_open(%s)", filename); - return (error); - } - } - eftype = EF_GET_TYPE(&ef); - if (eftype != EFT_KLD && eftype != EFT_KERNEL) { - EF_CLOSE(&ef); - return (0); + + error = elf_open_file(&ef, filename, verbose); + if (error != 0) + return (error); + + if (reccnt == 0) { + ehdr = ef.ef_hdr; + byte_order = elf_encoding(&ef); + free(ehdr_filename); + ehdr_filename = strdup(filename); + } else if (!elf_compatible(&ef, &ehdr)) { + warnx("%s does not match architecture of %s", + filename, ehdr_filename); + elf_close_file(&ef); + return (EINVAL); } + do { - check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish, - &entries)); - check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries, - (void *)&p)); + check(elf_read_linker_set(&ef, MDT_SETNAME, &p, &entries)); + /* * Do a first pass to find MDT_MODULE. It is required to be * ordered first in the output linker.hints stream because it @@ -627,16 +678,16 @@ read_kld(char *filename, char *kldname) * in the same kld. */ for (i = 0; i < entries; i++) { - check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), - &md)); - check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, - sizeof(cval), cval)); + check(elf_read_mod_metadata(&ef, p[i], &md)); + check(elf_read_string(&ef, md.md_cval, cval, + sizeof(cval))); if (md.md_type == MDT_MODULE) { parse_entry(&md, cval, &ef, kldname); break; } } if (error != 0) { + free(p); warnc(error, "error while reading %s", filename); break; } @@ -645,10 +696,9 @@ read_kld(char *filename, char *kldname) * Second pass for all !MDT_MODULE entries. */ for (i = 0; i < entries; i++) { - check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md), - &md)); - check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval, - sizeof(cval), cval)); + check(elf_read_mod_metadata(&ef, p[i], &md)); + check(elf_read_string(&ef, md.md_cval, cval, + sizeof(cval))); if (md.md_type != MDT_MODULE) parse_entry(&md, cval, &ef, kldname); } @@ -656,7 +706,7 @@ read_kld(char *filename, char *kldname) warnc(error, "error while reading %s", filename); free(p); } while(0); - EF_CLOSE(&ef); + elf_close_file(&ef); return (error); } @@ -714,7 +764,7 @@ main(int argc, char *argv[]) FTS *ftsp; FTSENT *p; char *dot = NULL; - int opt, fts_options, ival; + int opt, fts_options; struct stat sb; fts_options = FTS_PHYSICAL; @@ -750,6 +800,9 @@ main(int argc, char *argv[]) err(1, "%s", argv[0]); } + if (elf_version(EV_CURRENT) == EV_NONE) + errx(1, "unsupported libelf"); + ftsp = fts_open(argv, fts_options, compare); if (ftsp == NULL) exit(1); @@ -777,8 +830,7 @@ main(int argc, char *argv[]) fxref = maketempfile(tempname, ftsp->fts_path); if (fxref == NULL) err(1, "can't create %s", tempname); - ival = 1; - fwrite(&ival, sizeof(ival), 1, fxref); + byte_order = ELFDATANONE; reccnt = 0; } /* skip non-files.. */ |