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 @@ -1,15 +1,12 @@ 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 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 @@ -1,672 +1,650 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include -#include #include #include -#include +#include #include #include #include -#include -#include #include "ef.h" #define MAXSEGS 16 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) { printf("text=0x%jx ", (uintmax_t)phdr->p_filesz); } else { printf("data=0x%jx", (uintmax_t)phdr->p_filesz); if (phdr->p_filesz < phdr->p_memsz) printf("+0x%jx", (uintmax_t)(phdr->p_memsz - phdr->p_filesz)); printf(" "); } } -static 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 */ hash = elf_hash(name); symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; while (symnum != STN_UNDEF) { if (symnum >= ef->ef_nchains) { warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", ef->ef_name); return (ENOENT); } symp = ef->ef_symtab + symnum; if (symp->st_name == 0) { warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", ef->ef_name); return (ENOENT); } strp = ef->ef_strtab + symp->st_name; if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { + GELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = symp; return (0); } else return (ENOENT); } symnum = ef->ef_chains[symnum]; } return (ENOENT); } static int -ef_lookup_set(elf_file_t ef, const char *name, 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; len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ setsym = malloc(len); if (setsym == NULL) return (errno); /* get address of first entry */ snprintf(setsym, len, "%s%s", "__start_set_", name); error = ef_lookup_symbol(ef, setsym, &sym); if (error != 0) goto out; *startp = sym->st_value; /* get address of last entry */ snprintf(setsym, len, "%s%s", "__stop_set_", name); error = ef_lookup_symbol(ef, setsym, &sym); if (error != 0) goto out; *stopp = sym->st_value; /* and the number of entries */ - *countp = (*stopp - *startp) / 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); } return (0); } 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 @@ -1,105 +1,310 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _EF_H_ #define _EF_H_ -#define EFT_KLD 1 -#define EFT_KERNEL 2 +#include +#include -#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 @@ -1,72 +1,74 @@ /*- * Copyright (c) 2005 Peter Grehan. * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include -#include +#include #include "ef.h" /* * Apply relocations to the values obtained from the file. `relbase' is the * target relocation address of the section, and `dataoff/len' is the region * that is to be relocated, and has been copied to *dest */ -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; default: warnx("unhandled relocation type %lu", rtype); break; } 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 @@ -1,116 +1,117 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2003 Jake Burkholder. * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include +#include #include "ef.h" /* * Apply relocations to the values we got from the file. `relbase' is the * 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; } } switch (rtype) { case R_X86_64_NONE: /* none */ break; 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 @@ -1,96 +1,98 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2003 Jake Burkholder. * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include +#include #include "ef.h" /* * Apply relocations to the values we got from the file. `relbase' is the * 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 @@ -1,97 +1,120 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 John Baldwin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory (Department of Computer Science and * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the * DARPA SSITH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include +#include #include "ef.h" /* * Apply relocations to the values we got from the file. `relbase' is the * 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 -#include - -#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 @@ -1,631 +1,492 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * Copyright (c) 1998-2000 Doug Rabson * Copyright (c) 2004 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include -#include #include #include -#include +#include #include #include #include -#include -#include #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; Elf_relaent *relatab; int nrela; 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 */ caddr_t shstrtab; /* Section name string table */ long shstrcnt; /* number of bytes in string table */ 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; for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { *sym = symp; return (0); } } return (ENOENT); } static int -ef_obj_lookup_set(elf_file_t ef, const char *name, 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++) { if (ef->progtab[i].addr == NULL) continue; if (memaddr < (char *)ef->progtab[i].addr || memaddr + len > (char *)ef->progtab[i].addr + ef->progtab[i].size) continue; sec = ef->progtab[i].sec; /* We relocate to address 0. */ secbase = (char *)ef->progtab[i].addr - ef->address; dataoff = memaddr - ef->address; break; } if (sec == -1) return (EFAULT); /* Now do the relocations. */ for (i = 0; i < ef->nrel; i++) { if (ef->reltab[i].sec != sec) continue; for (r = ef->reltab[i].rel; r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { - error = 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); } } for (i = 0; i < ef->nrela; i++) { if (ef->relatab[i].sec != sec) continue; for (a = ef->relatab[i].rela; a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { - error = 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); } } return (0); } 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: ef->nprogtab++; break; case SHT_SYMTAB: nsym++; symtabindex = i; symstrindex = shdr[i].sh_link; break; case SHT_REL: ef->nrel++; break; case SHT_RELA: ef->nrela++; break; case SHT_STRTAB: break; } } if (ef->nprogtab == 0) { - warnx("%s: file has no contents", 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; } /* Allocate space for tracking the load chunks */ if (ef->nprogtab != 0) ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); if (ef->nrel != 0) ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); if (ef->nrela != 0) ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); if ((ef->nprogtab != 0 && ef->progtab == NULL) || (ef->nrel != 0 && ef->reltab == NULL) || (ef->nrela != 0 && ef->relatab == NULL)) { printf("malloc failed\n"); error = ENOMEM; goto out; } - 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; } } /* Size up code/data(progbits) and bss(nobits). */ 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: alignmask = shdr[i].sh_addralign - 1; if (shdr[i].sh_addralign > max_addralign) max_addralign = shdr[i].sh_addralign; mapsize += alignmask; mapsize &= ~alignmask; mapsize += shdr[i].sh_size; break; } } /* We know how much space we need for the text/data/bss/etc. */ ef->size = mapsize; if (posix_memalign((void **)&ef->address, max_addralign, mapsize)) { printf("posix_memalign failed\n"); goto out; } mapbase = ef->address; /* * Now load code/data(progbits), zero bss(nobits), allocate * space for and load relocs */ pb = 0; 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 = "<>"; - 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; } } else { ef->progtab[pb].name = "<>"; bzero(ef->progtab[pb].addr, shdr[i].sh_size); } ef->progtab[pb].size = shdr[i].sh_size; ef->progtab[pb].sec = i; if (ef->shstrtab && shdr[i].sh_name != 0) ef->progtab[pb].name = ef->shstrtab + shdr[i].sh_name; /* Update all symbol values with the offset. */ for (j = 0; j < ef->ddbsymcnt; j++) { es = &ef->ddbsymtab[j]; if (es->st_shndx != i) continue; - es->st_value += (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++; break; } } 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) free(ef->progtab); if (ef->nrel != 0) { for (i = 0; i < ef->nrel; i++) if (ef->reltab[i].rel != NULL) free(ef->reltab[i].rel); free(ef->reltab); } if (ef->nrela != 0) { for (i = 0; i < ef->nrela; i++) if (ef->relatab[i].rela != NULL) free(ef->relatab[i].rela); free(ef->relatab); } if (ef->ddbsymtab != NULL) free(ef->ddbsymtab); if (ef->ddbstrtab != NULL) free(ef->ddbstrtab); if (ef->shstrtab != NULL) free(ef->shstrtab); ef->ef_efile->ef_ops = NULL; ef->ef_efile->ef_ef = NULL; free(ef); - - 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 @@ -1,89 +1,93 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2005 Peter Grehan. * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include -#include -#include +#include #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 @@ -1,79 +1,80 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 John Baldwin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory (Department of Computer Science and * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the * DARPA SSITH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include +#include #include #include +#include #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 + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 @@ -1,798 +1,850 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include -#include #include -#include -#include -#include #include #include +#include #include #include #include #include +#include +#include #include #include #include #include #include -#include #include "ef.h" #define MAXRECSIZE (64 << 10) /* 64k */ #define check(val) if ((error = (val)) != 0) break 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"; /* * A record is stored in the static buffer recbuf before going to disk. */ static char recbuf[MAXRECSIZE]; static int recpos; /* current write position */ static int reccnt; /* total record written to this file so far */ static void intalign(void) { recpos = roundup2(recpos, sizeof(int)); } +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) { recpos = 0; memset(recbuf, 0, MAXRECSIZE); } 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); } static int record_buf(const void *buf, size_t size) { if (MAXRECSIZE - recpos < size) errx(1, "record buffer overflow"); memcpy(recbuf + recpos, buf, size); recpos += size; return (0); } /* - * 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))); } /* * A string is stored as 1-byte length plus data, no padding */ static int record_string(const char *str) { int error; size_t len; u_char val; if (dflag) return (0); val = len = strlen(str); if (len > 255) errx(1, "string %s too long", str); error = record_buf(&val, sizeof(val)); if (error != 0) return (error); return (record_buf(str, len)); } /* From sys/isa/pnp.c */ static char * pnp_eisaformat(uint32_t id) { uint8_t *data; static char idbuf[8]; const char hextoascii[] = "0123456789abcdef"; id = htole32(id); data = (uint8_t *)&id; idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); idbuf[2] = '@' + (data[1] & 0x1f); idbuf[3] = hextoascii[(data[2] >> 4)]; idbuf[4] = hextoascii[(data[2] & 0xf)]; idbuf[5] = hextoascii[(data[3] >> 4)]; idbuf[6] = hextoascii[(data[3] & 0xf)]; idbuf[7] = 0; return (idbuf); } struct pnp_elt { int pe_kind; /* What kind of entry */ #define TYPE_SZ_MASK 0x0f #define TYPE_FLAGGED 0x10 /* all f's is a wildcard */ #define TYPE_INT 0x20 /* Is a number */ #define TYPE_PAIRED 0x40 #define TYPE_LE 0x80 /* Matches <= this value */ #define TYPE_GE 0x100 /* Matches >= this value */ #define TYPE_MASK 0x200 /* Specifies a mask to follow */ #define TYPE_U8 (1 | TYPE_INT) #define TYPE_V8 (1 | TYPE_INT | TYPE_FLAGGED) #define TYPE_G16 (2 | TYPE_INT | TYPE_GE) #define TYPE_L16 (2 | TYPE_INT | TYPE_LE) #define TYPE_M16 (2 | TYPE_INT | TYPE_MASK) #define TYPE_U16 (2 | TYPE_INT) #define TYPE_V16 (2 | TYPE_INT | TYPE_FLAGGED) #define TYPE_U32 (4 | TYPE_INT) #define TYPE_V32 (4 | TYPE_INT | TYPE_FLAGGED) #define TYPE_W32 (4 | TYPE_INT | TYPE_PAIRED) #define TYPE_D 7 #define TYPE_Z 8 #define TYPE_P 9 #define TYPE_E 10 #define TYPE_T 11 int pe_offset; /* Offset within the element */ char * pe_key; /* pnp key name */ TAILQ_ENTRY(pnp_elt) next; /* Link */ }; typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list; /* * this function finds the data from the pnp table, as described by the * the description and creates a new output (new_desc). This output table * is a form that's easier for the agent that's automatically loading the * modules. * * The format output is the simplified string from this routine in the * same basic format as the pnp string, as documented in sys/module.h. * First a string describing the format is output, the a count of the * number of records, then each record. The format string also describes * the length of each entry (though it isn't a fixed length when strings * are present). * * type Output Meaning * I uint32_t Integer equality comparison * J uint32_t Pair of uint16_t fields converted to native * byte order. The two fields both must match. * G uint32_t Greater than or equal to * L uint32_t Less than or equal to * M uint32_t Mask of which fields to test. Fields that * take up space increment the count. This * field must be first, and resets the count. * D string Description of the device this pnp info is for * Z string pnp string must match this * T nothing T fields set pnp values that must be true for * the entire table. * Values are packed the same way that other values are packed in this file. * Strings and int32_t's start on a 32-bit boundary and are padded with 0 * bytes. Objects that are smaller than uint32_t are converted, without * 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; struct pnp_elt *elt; char type[8], key[32]; int off; size_t new_desc_size; FILE *fp; TAILQ_INIT(list); walker = desc; ep = desc + strlen(desc); off = 0; fp = open_memstream(new_desc, &new_desc_size); if (fp == NULL) err(1, "Could not open new memory stream"); if (verbose > 1) printf("Converting %s into a list\n", desc); while (walker < ep) { colon = strchr(walker, ':'); semi = strchr(walker, ';'); if (semi != NULL && semi < colon) goto err; if (colon - walker > sizeof(type)) goto err; strncpy(type, walker, colon - walker); type[colon - walker] = '\0'; if (semi != NULL) { if (semi - colon >= sizeof(key)) goto err; strncpy(key, colon + 1, semi - colon - 1); key[semi - colon - 1] = '\0'; walker = semi + 1; /* Fail safe if we have spaces after ; */ while (walker < ep && isspace(*walker)) walker++; } else { if (strlen(colon + 1) >= sizeof(key)) goto err; strcpy(key, colon + 1); walker = ep; } if (verbose > 1) 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; } /* * Add a node of the appropriate type */ elt = malloc(sizeof(struct pnp_elt) + strlen(key) + 1); TAILQ_INSERT_TAIL(list, elt, next); elt->pe_key = (char *)(elt + 1); elt->pe_offset = off; if (strcmp(type, "U8") == 0) elt->pe_kind = TYPE_U8; else if (strcmp(type, "V8") == 0) elt->pe_kind = TYPE_V8; else if (strcmp(type, "G16") == 0) elt->pe_kind = TYPE_G16; else if (strcmp(type, "L16") == 0) elt->pe_kind = TYPE_L16; else if (strcmp(type, "M16") == 0) elt->pe_kind = TYPE_M16; else if (strcmp(type, "U16") == 0) elt->pe_kind = TYPE_U16; else if (strcmp(type, "V16") == 0) elt->pe_kind = TYPE_V16; else if (strcmp(type, "U32") == 0) elt->pe_kind = TYPE_U32; else if (strcmp(type, "V32") == 0) elt->pe_kind = TYPE_V32; else if (strcmp(type, "W32") == 0) elt->pe_kind = TYPE_W32; else if (strcmp(type, "D") == 0) /* description char * */ elt->pe_kind = TYPE_D; else if (strcmp(type, "Z") == 0) /* char * to match */ elt->pe_kind = TYPE_Z; else if (strcmp(type, "P") == 0) /* Pointer -- ignored */ elt->pe_kind = TYPE_P; else if (strcmp(type, "E") == 0) /* EISA PNP ID, as uint32_t */ elt->pe_kind = TYPE_E; else if (strcmp(type, "T") == 0) elt->pe_kind = TYPE_T; else goto err; /* * Maybe the rounding here needs to be more nuanced and/or somehow * architecture specific. Fortunately, most tables in the system * have sane ordering of types. */ if (elt->pe_kind & TYPE_INT) { elt->pe_offset = roundup2(elt->pe_offset, elt->pe_kind & TYPE_SZ_MASK); off = elt->pe_offset + (elt->pe_kind & TYPE_SZ_MASK); } else if (elt->pe_kind == TYPE_E) { /* Type E stored as Int, displays as string */ elt->pe_offset = roundup2(elt->pe_offset, sizeof(uint32_t)); off = elt->pe_offset + sizeof(uint32_t); } else if (elt->pe_kind == TYPE_T) { /* 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; for (word = strtok_r(key, "/", &ctx); word; word = strtok_r(NULL, "/", &ctx)) { newtype = elt->pe_kind & TYPE_FLAGGED ? 'J' : 'I'; fprintf(fp, "%c:%s;", newtype, word); } } else { char newtype; if (elt->pe_kind & TYPE_FLAGGED) newtype = 'J'; else if (elt->pe_kind & TYPE_GE) newtype = 'G'; else if (elt->pe_kind & TYPE_LE) newtype = 'L'; else if (elt->pe_kind & TYPE_MASK) newtype = 'M'; else if (elt->pe_kind & TYPE_INT) newtype = 'I'; else if (elt->pe_kind == TYPE_D) newtype = 'D'; else if (elt->pe_kind == TYPE_Z || elt->pe_kind == TYPE_E) newtype = 'Z'; else if (elt->pe_kind == TYPE_T) newtype = 'T'; else errx(1, "Impossible type %x\n", elt->pe_kind); fprintf(fp, "%c:%s;", newtype, key); } } if (ferror(fp) != 0) { fclose(fp); errx(1, "Exhausted space converting description %s", desc); } if (fclose(fp) != 0) errx(1, "Failed to close memory stream"); return (0); err: errx(1, "Parse error of description string %s", desc); } static void free_pnp_list(char *new_desc, pnp_list *list) { struct pnp_elt *elt, *elt_tmp; TAILQ_FOREACH_SAFE(elt, list, next, elt_tmp) { TAILQ_REMOVE(list, elt, next); free(elt); } 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) { uint8_t v1; uint16_t v2; uint32_t v4; int value; 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) printf("W32:%#x", value); value = (v4 >> 16) & 0xffff; record_int(value); if (verbose > 1) printf(":%#x;", value); } else if (elt->pe_kind & TYPE_INT) { switch (elt->pe_kind & TYPE_SZ_MASK) { case 1: memcpy(&v1, walker + elt->pe_offset, sizeof(v1)); if ((elt->pe_kind & TYPE_FLAGGED) && v1 == 0xff) value = -1; else 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 value = v4; break; default: errx(1, "Invalid size somehow %#x", elt->pe_kind); } if (verbose > 1) printf("I:%#x;", value); record_int(value); } else if (elt->pe_kind == TYPE_T) { /* 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'; } } if (verbose > 1) printf("%c:%s;", elt->pe_kind == TYPE_E ? 'E' : (elt->pe_kind == TYPE_Z ? 'Z' : 'D'), buffer); record_string(buffer); } } 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; char *new_descr, *walker; void *table; size_t len; int error, i; if (verbose > 1) printf(" pnp info for bus %s format %s %d entries of %d bytes\n", cval, descr, pnp->num_entry, pnp->entry_len); /* * 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; } /* * Walk the list and output things. We've collapsed all the * variant forms of the table down to just ints and strings. */ walker = table; for (i = 0; i < pnp->num_entry; i++) { TAILQ_FOREACH(elt, &list, next) { parse_pnp_entry(ef, elt, walker); } if (verbose > 1) printf("\n"); walker += pnp->entry_len; } /* Now free it */ free_pnp_list(new_descr, &list); free(table); } 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 { record_int(MDT_VERSION); record_string(cval); record_int(mdv.mv_version); record_string(kldname); } break; case MDT_MODULE: if (dflag) { printf(" module %s\n", cval); } else { record_int(MDT_MODULE); record_string(cval); record_string(kldname); } 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); } else { record_pnp_info(ef, cval, &pnp, descr); } break; default: warnx("unknown metadata record %d in file %s", md->md_type, kldname); } if (!error) record_end(); return (error); } static int read_kld(char *filename, char *kldname) { - struct mod_metadata md; + struct 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 * serves as an implicit record boundary between distinct klds * in the stream. Other MDTs only make sense in the context of * a specific MDT_MODULE. * * Some compilers (e.g., GCC 6.4.0 xtoolchain) or binutils * (e.g., GNU binutils 2.32 objcopy/ld.bfd) can reorder * MODULE_METADATA set entries relative to the source ordering. * This is permitted by the C standard; memory layout of * file-scope objects is left implementation-defined. There is * no requirement that source code ordering is retained. * * Handle that here by taking two passes to ensure MDT_MODULE * records are emitted to linker.hints before other MDT records * 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; } /* * 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); } if (error != 0) warnc(error, "error while reading %s", filename); free(p); } while(0); - EF_CLOSE(&ef); + elf_close_file(&ef); return (error); } /* * Create a temp file in directory root, make sure we don't * overflow the buffer for the destination name */ static FILE * maketempfile(char *dest, const char *root) { char *p; int n, fd; p = strrchr(root, '/'); n = p != NULL ? p - root + 1 : 0; if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >= MAXPATHLEN) { errno = ENAMETOOLONG; return (NULL); } fd = mkstemp(dest); if (fd < 0) return (NULL); fchmod(fd, 0644); /* nothing secret in the file */ return (fdopen(fd, "w+")); } static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN]; static void usage(void) { fprintf(stderr, "%s\n", "usage: kldxref [-Rdv] [-f hintsfile] path ..." ); exit(1); } static int compare(const FTSENT *const *a, const FTSENT *const *b) { if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) return (1); if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) return (-1); return (strcmp((*a)->fts_name, (*b)->fts_name)); } int 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; while ((opt = getopt(argc, argv, "Rdf:v")) != -1) { switch (opt) { case 'd': /* no hint file, only print on stdout */ dflag = true; break; case 'f': /* use this name instead of linker.hints */ xref_file = optarg; break; case 'v': verbose++; break; case 'R': /* recurse on directories */ fts_options |= FTS_COMFOLLOW; break; default: usage(); /* NOTREACHED */ } } if (argc - optind < 1) usage(); argc -= optind; argv += optind; if (stat(argv[0], &sb) != 0) err(1, "%s", argv[0]); if ((sb.st_mode & S_IFDIR) == 0 && !dflag) { errno = ENOTDIR; 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); for (;;) { p = fts_read(ftsp); if ((p == NULL || p->fts_info == FTS_D) && fxref) { /* close and rename the current hint file */ fclose(fxref); fxref = NULL; if (reccnt != 0) { rename(tempname, xrefname); } else { /* didn't find any entry, ignore this file */ unlink(tempname); unlink(xrefname); } } if (p == NULL) break; if (p->fts_info == FTS_D && !dflag) { /* visiting a new directory, create a new hint file */ snprintf(xrefname, sizeof(xrefname), "%s/%s", ftsp->fts_path, xref_file); 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.. */ if (p->fts_info != FTS_F) continue; /* * Skip files that generate errors like .debug, .symbol and .pkgsave * by generally skipping all files with 2 dots. */ dot = strchr(p->fts_name, '.'); if (dot && strchr(dot + 1, '.') != NULL) continue; read_kld(p->fts_path, p->fts_name); } fts_close(ftsp); return (0); }