git: 0299afdff145 - main - kldxref: Make use of libelf to be a portable cross tool
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 12 Dec 2023 23:45:07 UTC
The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=0299afdff145e5d861797fe9c2de8b090c456fba commit 0299afdff145e5d861797fe9c2de8b090c456fba Author: John Baldwin <jhb@FreeBSD.org> AuthorDate: 2023-12-12 23:43:00 +0000 Commit: John Baldwin <jhb@FreeBSD.org> CommitDate: 2023-12-12 23:43:00 +0000 kldxref: Make use of libelf to be a portable cross tool This allows kldxref to operate on kernel objects from any architecture, not just the native architecture. In particular, this will permit generating linker.hints files as part of a cross-arch release build. - elf.c is a new file that includes various wrappers around libelf including routines to read ELF data structures such as program and section headers and ELF relocations into the "generic" forms described in <gelf.h>. This file also provides routines for converting a linker set into an array of addresses (GElf_Addr) as well as reading architecture-specific mod_* structures and converting them into "generic" Gmod_* forms where pointers are replaced with addresses. - The various architecture-specific reloc handlers now use GElf_* types for most values (including GElf_Rel and GElf_Rela for relocation structures) and use routines from <sys/endian.h> to read and write target values. A new linker set matches reloc handlers to specific ELF (class, encoding, machine) tuples. - The bits of kldxref.c that write out linker.hints now use the encoding (ELFDATA2[LM]SB) of the first file encountered in a directory to set the endianness of the output file. Input files with a different architecture in the same directory are skipped with a warning. In addition, the initial version record for the file must be deferred until the first record is finished since the architecture of the output file is not known until then. - Various places that used 'sizeof(void *)' throughout now use 'elf_pointer_size()' to determine the size of a pointer in the target architecture. Tested by: amd64 binary on both amd64 and i386 /boot/kernel Reviewed by: imp Sponsored by: DARPA Differential Revision: https://reviews.freebsd.org/D42966 --- usr.sbin/kldxref/Makefile | 9 +- usr.sbin/kldxref/ef.c | 730 ++++++++++++++++++++---------------------- usr.sbin/kldxref/ef.h | 291 ++++++++++++++--- usr.sbin/kldxref/ef_aarch64.c | 32 +- usr.sbin/kldxref/ef_amd64.c | 65 ++-- usr.sbin/kldxref/ef_i386.c | 56 ++-- usr.sbin/kldxref/ef_mips.c | 79 +++-- usr.sbin/kldxref/ef_nop.c | 40 --- usr.sbin/kldxref/ef_obj.c | 341 ++++++-------------- usr.sbin/kldxref/ef_powerpc.c | 62 ++-- usr.sbin/kldxref/ef_riscv.c | 35 +- usr.sbin/kldxref/elf.c | 674 ++++++++++++++++++++++++++++++++++++++ usr.sbin/kldxref/kldxref.c | 196 +++++++----- 13 files changed, 1685 insertions(+), 925 deletions(-) diff --git a/usr.sbin/kldxref/Makefile b/usr.sbin/kldxref/Makefile index 3d4ce7163b48..6e0a7244328b 100644 --- a/usr.sbin/kldxref/Makefile +++ b/usr.sbin/kldxref/Makefile @@ -2,14 +2,11 @@ PACKAGE= runtime PROG= kldxref MAN= kldxref.8 -SRCS= kldxref.c ef.c ef_obj.c +SRCS= kldxref.c ef.c ef_obj.c elf.c +SRCS+= ef_aarch64.c ef_amd64.c ef_i386.c ef_mips.c ef_powerpc.c ef_riscv.c WARNS?= 2 -.if exists(ef_${MACHINE_CPUARCH}.c) -SRCS+= ef_${MACHINE_CPUARCH}.c -.else -SRCS+= ef_nop.c -.endif +LIBADD= elf .include <bsd.prog.mk> diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c index 72e023e30783..aa9123d7f540 100644 --- a/usr.sbin/kldxref/ef.c +++ b/usr.sbin/kldxref/ef.c @@ -33,16 +33,13 @@ */ #include <sys/param.h> -#include <sys/linker.h> #include <err.h> #include <errno.h> -#include <fcntl.h> +#include <gelf.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <machine/elf.h> #include "ef.h" @@ -50,76 +47,52 @@ struct ef_file { char *ef_name; struct elf_file *ef_efile; - Elf_Phdr *ef_ph; - int ef_fd; - int ef_type; - Elf_Ehdr ef_hdr; + GElf_Phdr *ef_ph; void *ef_fpage; /* First block of the file */ int ef_fplen; /* length of first block */ - Elf_Dyn *ef_dyn; /* Symbol table etc. */ - Elf_Hashelt ef_nbuckets; - Elf_Hashelt ef_nchains; - Elf_Hashelt *ef_buckets; - Elf_Hashelt *ef_chains; - Elf_Hashelt *ef_hashtab; - Elf_Off ef_stroff; + GElf_Hashelt ef_nbuckets; + GElf_Hashelt ef_nchains; + GElf_Hashelt *ef_buckets; + GElf_Hashelt *ef_chains; + GElf_Hashelt *ef_hashtab; caddr_t ef_strtab; - int ef_strsz; - Elf_Off ef_symoff; - Elf_Sym *ef_symtab; + long ef_strsz; + GElf_Sym *ef_symtab; int ef_nsegs; - Elf_Phdr *ef_segs[MAXSEGS]; + GElf_Phdr *ef_segs[MAXSEGS]; int ef_verbose; - Elf_Rel *ef_rel; /* relocation table */ - int ef_relsz; /* number of entries */ - Elf_Rela *ef_rela; /* relocation table */ - int ef_relasz; /* number of entries */ + GElf_Rel *ef_rel; /* relocation table */ + long ef_relsz; /* number of entries */ + GElf_Rela *ef_rela; /* relocation table */ + long ef_relasz; /* number of entries */ }; -static void ef_print_phdr(Elf_Phdr *); -static Elf_Off ef_get_offset(elf_file_t, Elf_Off); -static int ef_parse_dynamic(elf_file_t); +static void ef_print_phdr(GElf_Phdr *); +static GElf_Off ef_get_offset(elf_file_t, GElf_Addr); -static int ef_get_type(elf_file_t ef); -static int ef_close(elf_file_t ef); -static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); -static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); +static void ef_close(elf_file_t ef); -static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, +static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest); -static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, - void *dest); -static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, +static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest); -static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); -static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, - void **ptr); - -static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx); -static int ef_lookup_set(elf_file_t ef, const char *name, long *startp, - long *stopp, long *countp); + +static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx); +static int ef_lookup_set(elf_file_t ef, const char *name, + GElf_Addr *startp, GElf_Addr *stopp, long *countp); static int ef_lookup_symbol(elf_file_t ef, const char *name, - Elf_Sym **sym); + GElf_Sym **sym); static struct elf_file_ops ef_file_ops = { - .get_type = ef_get_type, .close = ef_close, - .read = ef_read, - .read_entry = ef_read_entry, - .seg_read = ef_seg_read, .seg_read_rel = ef_seg_read_rel, .seg_read_string = ef_seg_read_string, - .seg_read_entry = ef_seg_read_entry, - .seg_read_entry_rel = ef_seg_read_entry_rel, .symaddr = ef_symaddr, .lookup_set = ef_lookup_set, - .lookup_symbol = ef_lookup_symbol }; static void -ef_print_phdr(Elf_Phdr *phdr) +ef_print_phdr(GElf_Phdr *phdr) { if ((phdr->p_flags & PF_W) == 0) { @@ -133,53 +106,29 @@ ef_print_phdr(Elf_Phdr *phdr) } } -static Elf_Off -ef_get_offset(elf_file_t ef, Elf_Off off) +static GElf_Off +ef_get_offset(elf_file_t ef, GElf_Addr addr) { - Elf_Phdr *ph; + GElf_Phdr *ph; int i; for (i = 0; i < ef->ef_nsegs; i++) { ph = ef->ef_segs[i]; - if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { - return (ph->p_offset + (off - ph->p_vaddr)); + if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) { + return (ph->p_offset + (addr - ph->p_vaddr)); } } return (0); } -static int -ef_get_type(elf_file_t ef) -{ - - return (ef->ef_type); -} - /* - * next three functions copied from link_elf.c + * next two functions copied from link_elf.c */ -static unsigned long -elf_hash(const char *name) -{ - unsigned long h, g; - const unsigned char *p; - - h = 0; - p = (const unsigned char *)name; - while (*p != '\0') { - h = (h << 4) + *p++; - if ((g = h & 0xf0000000) != 0) - h ^= g >> 24; - h &= ~g; - } - return (h); -} - static int -ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) +ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) { unsigned long hash, symnum; - Elf_Sym *symp; + GElf_Sym *symp; char *strp; /* First, search hashed global symbols */ @@ -205,7 +154,7 @@ ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) if (strcmp(name, strp) == 0) { if (symp->st_shndx != SHN_UNDEF || (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { + GELF_ST_TYPE(symp->st_info) == STT_FUNC)) { *sym = symp; return (0); } else @@ -219,10 +168,10 @@ ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym) } static int -ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, - long *countp) +ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, + GElf_Addr *stopp, long *countp) { - Elf_Sym *sym; + GElf_Sym *sym; char *setsym; int error, len; @@ -246,258 +195,340 @@ ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, *stopp = sym->st_value; /* and the number of entries */ - *countp = (*stopp - *startp) / sizeof(void *); + *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile); out: free(setsym); return (error); } -static Elf_Addr -ef_symaddr(elf_file_t ef, Elf_Size symidx) +static GElf_Addr +ef_symaddr(elf_file_t ef, GElf_Size symidx) { - const Elf_Sym *sym; + const GElf_Sym *sym; if (symidx >= ef->ef_nchains) return (0); sym = ef->ef_symtab + symidx; - if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && + if (GELF_ST_BIND(sym->st_info) == STB_LOCAL && sym->st_shndx != SHN_UNDEF && sym->st_value != 0) return (sym->st_value); return (0); } static int -ef_parse_dynamic(elf_file_t ef) +ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) { - Elf_Dyn *dp; - Elf_Hashelt hashhdr[2]; + GElf_Shdr *shdr; + GElf_Dyn *dyn, *dp; + size_t i, ndyn, nshdr, nsym; int error; - Elf_Off rel_off; - Elf_Off rela_off; + GElf_Off hash_off, sym_off, str_off; + GElf_Off rel_off; + GElf_Off rela_off; int rel_sz; int rela_sz; - int rel_entry; - int rela_entry; + int dynamic_idx; + + /* + * The kernel linker parses the PT_DYNAMIC segment to find + * various important tables. The gelf API of libelf is + * section-oriented and requires extracting data from sections + * instead of segments (program headers). As a result, + * iterate over section headers to read various tables after + * parsing values from PT_DYNAMIC. + */ + error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr); + if (error != 0) + return (EFTYPE); + dyn = NULL; + + /* Find section for .dynamic. */ + dynamic_idx = -1; + for (i = 0; i < nshdr; i++) { + if (shdr[i].sh_type == SHT_DYNAMIC) { + if (shdr[i].sh_offset != phdyn->p_offset || + shdr[i].sh_size != phdyn->p_filesz) { + warnx(".dynamic section doesn't match phdr"); + error = EFTYPE; + goto out; + } + if (dynamic_idx != -1) { + warnx("multiple SHT_DYNAMIC sections"); + error = EFTYPE; + goto out; + } + dynamic_idx = i; + } + } + + error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn); + if (error != 0) + goto out; - rel_off = rela_off = 0; + hash_off = rel_off = rela_off = sym_off = str_off = 0; rel_sz = rela_sz = 0; - rel_entry = rela_entry = 0; - for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { + for (i = 0; i < ndyn; i++) { + dp = &dyn[i]; + if (dp->d_tag == DT_NULL) + break; + switch (dp->d_tag) { case DT_HASH: - error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), - sizeof(hashhdr), hashhdr); - if (error != 0) { - warnx("can't read hash header (%jx)", - (uintmax_t)ef_get_offset(ef, dp->d_un.d_ptr)); - return (error); - } - ef->ef_nbuckets = hashhdr[0]; - ef->ef_nchains = hashhdr[1]; - error = ef_read_entry(ef, -1, - (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt), - (void **)&ef->ef_hashtab); - if (error != 0) { - warnx("can't read hash table"); - return (error); - } - ef->ef_buckets = ef->ef_hashtab; - ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; + if (hash_off != 0) + warnx("second DT_HASH entry ignored"); + else + hash_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_STRTAB: - ef->ef_stroff = dp->d_un.d_ptr; - break; - case DT_STRSZ: - ef->ef_strsz = dp->d_un.d_val; + if (str_off != 0) + warnx("second DT_STRTAB entry ignored"); + else + str_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_SYMTAB: - ef->ef_symoff = dp->d_un.d_ptr; + if (sym_off != 0) + warnx("second DT_SYMTAB entry ignored"); + else + sym_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_SYMENT: - if (dp->d_un.d_val != sizeof(Elf_Sym)) - return (EFTYPE); + if (dp->d_un.d_val != elf_object_size(ef->ef_efile, + ELF_T_SYM)) { + error = EFTYPE; + goto out; + } break; case DT_REL: if (rel_off != 0) warnx("second DT_REL entry ignored"); - rel_off = dp->d_un.d_ptr; + else + rel_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_RELSZ: if (rel_sz != 0) warnx("second DT_RELSZ entry ignored"); - rel_sz = dp->d_un.d_val; + else + rel_sz = dp->d_un.d_val; break; case DT_RELENT: - if (rel_entry != 0) - warnx("second DT_RELENT entry ignored"); - rel_entry = dp->d_un.d_val; + if (dp->d_un.d_val != elf_object_size(ef->ef_efile, + ELF_T_REL)) { + error = EFTYPE; + goto out; + } break; case DT_RELA: if (rela_off != 0) warnx("second DT_RELA entry ignored"); - rela_off = dp->d_un.d_ptr; + else + rela_off = ef_get_offset(ef, dp->d_un.d_ptr); break; case DT_RELASZ: if (rela_sz != 0) - warnx("second DT_RELASZ entry ignored"); - rela_sz = dp->d_un.d_val; + warnx("second DT_RELSZ entry ignored"); + else + rela_sz = dp->d_un.d_val; break; case DT_RELAENT: - if (rela_entry != 0) - warnx("second DT_RELAENT entry ignored"); - rela_entry = dp->d_un.d_val; + if (dp->d_un.d_val != elf_object_size(ef->ef_efile, + ELF_T_RELA)) { + error = EFTYPE; + goto out; + } break; } } - if (ef->ef_symoff == 0) { + if (hash_off == 0) { + warnx("%s: no .hash section found\n", ef->ef_name); + error = EFTYPE; + goto out; + } + if (sym_off == 0) { warnx("%s: no .dynsym section found\n", ef->ef_name); - return (EFTYPE); + error = EFTYPE; + goto out; } - if (ef->ef_stroff == 0) { + if (str_off == 0) { warnx("%s: no .dynstr section found\n", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), - ef->ef_nchains * sizeof(Elf_Sym), - (void **)&ef->ef_symtab) != 0) { - if (ef->ef_verbose) - warnx("%s: can't load .dynsym section (0x%jx)", - ef->ef_name, (uintmax_t)ef->ef_symoff); - return (EIO); - } - if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, - (void **)&ef->ef_strtab) != 0) { - warnx("can't load .dynstr section"); - return (EIO); - } - if (rel_off != 0) { - if (rel_entry == 0) { - warnx("%s: no DT_RELENT for DT_REL", ef->ef_name); - return (EFTYPE); - } - if (rel_entry != sizeof(Elf_Rel)) { - warnx("%s: inconsistent DT_RELENT value", - ef->ef_name); - return (EFTYPE); - } - if (rel_sz % rel_entry != 0) { - warnx("%s: inconsistent values for DT_RELSZ and " - "DT_RELENT", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz, - (void **)&ef->ef_rel) != 0) { - warnx("%s: cannot load DT_REL section", ef->ef_name); - return (EIO); - } - ef->ef_relsz = rel_sz / rel_entry; - if (ef->ef_verbose) - warnx("%s: %d REL entries", ef->ef_name, - ef->ef_relsz); + error = EFTYPE; + goto out; } - if (rela_off != 0) { - if (rela_entry == 0) { - warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name); - return (EFTYPE); - } - if (rela_entry != sizeof(Elf_Rela)) { - warnx("%s: inconsistent DT_RELAENT value", - ef->ef_name); - return (EFTYPE); - } - if (rela_sz % rela_entry != 0) { - warnx("%s: inconsistent values for DT_RELASZ and " - "DT_RELAENT", ef->ef_name); - return (EFTYPE); - } - if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz, - (void **)&ef->ef_rela) != 0) { - warnx("%s: cannot load DT_RELA section", ef->ef_name); - return (EIO); - } - ef->ef_relasz = rela_sz / rela_entry; - if (ef->ef_verbose) - warnx("%s: %d RELA entries", ef->ef_name, - ef->ef_relasz); + if (rel_off == 0 && rela_off == 0) { + warnx("%s: no ELF relocation table found\n", ef->ef_name); + error = EFTYPE; + goto out; } - return (0); -} -static int -ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - ssize_t r; + for (i = 0; i < nshdr; i++) { + switch (shdr[i].sh_type) { + case SHT_HASH: + if (shdr[i].sh_offset != hash_off) { + warnx("%s: ignoring SHT_HASH at different offset from DT_HASH", + ef->ef_name); + break; + } - if (offset != (Elf_Off)-1) { - if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) - return (EIO); - } + /* + * libelf(3) mentions ELF_T_HASH, but it is + * not defined. + */ + if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) { + warnx("hash section too small"); + error = EFTYPE; + goto out; + } + error = elf_read_data(ef->ef_efile, ELF_T_WORD, + shdr[i].sh_offset, shdr[i].sh_size, + (void **)&ef->ef_hashtab); + if (error != 0) { + warnc(error, "can't read hash table"); + goto out; + } + ef->ef_nbuckets = ef->ef_hashtab[0]; + ef->ef_nchains = ef->ef_hashtab[1]; + if ((2 + ef->ef_nbuckets + ef->ef_nchains) * + sizeof(*ef->ef_hashtab) != shdr[i].sh_size) { + warnx("inconsistent hash section size"); + error = EFTYPE; + goto out; + } - r = read(ef->ef_fd, dest, len); - if (r != -1 && (size_t)r == len) - return (0); - else - return (EIO); -} + ef->ef_buckets = ef->ef_hashtab + 2; + ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; + break; + case SHT_DYNSYM: + if (shdr[i].sh_offset != sym_off) { + warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB", + ef->ef_name); + break; + } + error = elf_read_symbols(ef->ef_efile, i, &nsym, + &ef->ef_symtab); + if (error != 0) { + if (ef->ef_verbose) + warnx("%s: can't load .dynsym section (0x%jx)", + ef->ef_name, (uintmax_t)sym_off); + goto out; + } + break; + case SHT_STRTAB: + if (shdr[i].sh_offset != str_off) + break; + error = elf_read_string_table(ef->ef_efile, + &shdr[i], &ef->ef_strsz, &ef->ef_strtab); + if (error != 0) { + warnx("can't load .dynstr section"); + error = EIO; + goto out; + } + break; + case SHT_REL: + if (shdr[i].sh_offset != rel_off) + break; + if (shdr[i].sh_size != rel_sz) { + warnx("%s: size mismatch for DT_REL section", + ef->ef_name); + error = EFTYPE; + goto out; + } + error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz, + &ef->ef_rel); + if (error != 0) { + warnx("%s: cannot load DT_REL section", + ef->ef_name); + goto out; + } + break; + case SHT_RELA: + if (shdr[i].sh_offset != rela_off) + break; + if (shdr[i].sh_size != rela_sz) { + warnx("%s: size mismatch for DT_RELA section", + ef->ef_name); + error = EFTYPE; + goto out; + } + error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz, + &ef->ef_rela); + if (error != 0) { + warnx("%s: cannot load DT_RELA section", + ef->ef_name); + goto out; + } + break; + } + } -static int -ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; + if (ef->ef_hashtab == NULL) { + warnx("%s: did not find a symbol hash table", ef->ef_name); + error = EFTYPE; + goto out; + } + if (ef->ef_symtab == NULL) { + warnx("%s: did not find a dynamic symbol table", ef->ef_name); + error = EFTYPE; + goto out; + } + if (nsym != ef->ef_nchains) { + warnx("%s: symbol count mismatch", ef->ef_name); + error = EFTYPE; + goto out; + } + if (ef->ef_strtab == NULL) { + warnx("%s: did not find a dynamic string table", ef->ef_name); + error = EFTYPE; + goto out; + } + if (rel_off != 0 && ef->ef_rel == NULL) { + warnx("%s: did not find a DT_REL relocation table", + ef->ef_name); + error = EFTYPE; + goto out; + } + if (rela_off != 0 && ef->ef_rela == NULL) { + warnx("%s: did not find a DT_RELA relocation table", + ef->ef_name); + error = EFTYPE; + goto out; + } - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); + error = 0; +out: + free(dyn); + free(shdr); return (error); } static int -ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) +ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) { - Elf_Off ofs; - - ofs = ef_get_offset(ef, offset); - if (ofs == 0) { - if (ef->ef_verbose) - warnx("ef_seg_read(%s): zero offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); - return (EFAULT); - } - return (ef_read(ef, ofs, len, dest)); -} - -static int -ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) -{ - Elf_Off ofs; - const Elf_Rela *a; - const Elf_Rel *r; + GElf_Off ofs; + const GElf_Rela *a; + const GElf_Rel *r; int error; - ofs = ef_get_offset(ef, offset); + ofs = ef_get_offset(ef, address); if (ofs == 0) { if (ef->ef_verbose) warnx("ef_seg_read_rel(%s): zero offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); + ef->ef_name, (uintmax_t)address, (uintmax_t)ofs); return (EFAULT); } - if ((error = ef_read(ef, ofs, len, dest)) != 0) + error = elf_read_raw_data(ef->ef_efile, ofs, dest, len); + if (error != 0) return (error); for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) { - error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len, - dest); + error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address, + len, dest); if (error != 0) return (error); } for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) { - error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len, - dest); + error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address, + len, dest); if (error != 0) return (error); } @@ -505,168 +536,115 @@ ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) } static int -ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest) +ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) { - Elf_Off ofs; - ssize_t r; + GElf_Off ofs; + int error; - ofs = ef_get_offset(ef, offset); - if (ofs == 0 || ofs == (Elf_Off)-1) { + ofs = ef_get_offset(ef, address); + if (ofs == 0 || ofs == (GElf_Off)-1) { if (ef->ef_verbose) warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)", - ef->ef_name, (uintmax_t)offset, (uintmax_t)ofs); + ef->ef_name, (uintmax_t)address, (uintmax_t)ofs); return (EFAULT); } - r = pread(ef->ef_fd, dest, len, ofs); - if (r < 0) - return (errno); + error = elf_read_raw_data(ef->ef_efile, ofs, dest, len); + if (error != 0) + return (error); if (strnlen(dest, len) == len) return (EFAULT); return (0); } -static int -ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_seg_read(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - -static int -ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) -{ - int error; - - *ptr = malloc(len); - if (*ptr == NULL) - return (errno); - error = ef_seg_read_rel(ef, offset, len, *ptr); - if (error != 0) - free(*ptr); - return (error); -} - int -ef_open(const char *filename, struct elf_file *efile, int verbose) +ef_open(struct elf_file *efile, int verbose) { elf_file_t ef; - Elf_Ehdr *hdr; - int fd; + GElf_Ehdr *hdr; + size_t i, nphdr, nsegs; int error; - int phlen, res; - int nsegs; - Elf_Phdr *phdr, *phdyn, *phlimit; + GElf_Phdr *phdr, *phdyn; - if (filename == NULL) - return (EINVAL); - if ((fd = open(filename, O_RDONLY)) == -1) - return (errno); + hdr = &efile->ef_hdr; + if (hdr->e_phnum == 0 || + hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) || + hdr->e_shnum == 0 || hdr->e_shoff == 0 || + hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR)) + return (EFTYPE); ef = malloc(sizeof(*ef)); - if (ef == NULL) { - close(fd); + if (ef == NULL) return (errno); - } efile->ef_ef = ef; efile->ef_ops = &ef_file_ops; bzero(ef, sizeof(*ef)); ef->ef_verbose = verbose; - ef->ef_fd = fd; - ef->ef_name = strdup(filename); + ef->ef_name = strdup(efile->ef_filename); ef->ef_efile = efile; - hdr = (Elf_Ehdr *)&ef->ef_hdr; - do { - res = read(fd, hdr, sizeof(*hdr)); - error = EFTYPE; - if (res != sizeof(*hdr)) - break; - if (!IS_ELF(*hdr)) - break; - if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || - hdr->e_ident[EI_DATA] != ELF_TARG_DATA || - hdr->e_ident[EI_VERSION] != EV_CURRENT || - hdr->e_version != EV_CURRENT || - hdr->e_machine != ELF_TARG_MACH || - hdr->e_phentsize != sizeof(Elf_Phdr)) - break; - phlen = hdr->e_phnum * sizeof(Elf_Phdr); - if (ef_read_entry(ef, hdr->e_phoff, phlen, - (void **)&ef->ef_ph) != 0) - break; - phdr = ef->ef_ph; - phlimit = phdr + hdr->e_phnum; - nsegs = 0; - phdyn = NULL; - while (phdr < phlimit) { - if (verbose > 1) - ef_print_phdr(phdr); - switch (phdr->p_type) { - case PT_LOAD: - if (nsegs < MAXSEGS) - ef->ef_segs[nsegs] = phdr; - nsegs++; - break; - case PT_PHDR: - break; - case PT_DYNAMIC: - phdyn = phdr; - break; - } - phdr++; - } + + error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph); + if (error != 0) { + phdr = NULL; + goto out; + } + + error = EFTYPE; + nsegs = 0; + phdyn = NULL; + phdr = ef->ef_ph; + for (i = 0; i < nphdr; i++, phdr++) { if (verbose > 1) - printf("\n"); - if (phdyn == NULL) { - warnx("Skipping %s: not dynamically-linked", - filename); + ef_print_phdr(phdr); + switch (phdr->p_type) { + case PT_LOAD: + if (nsegs < MAXSEGS) + ef->ef_segs[nsegs] = phdr; + nsegs++; break; - } else if (nsegs > MAXSEGS) { - warnx("%s: too many segments", filename); + case PT_PHDR: break; - } - ef->ef_nsegs = nsegs; - if (ef_read_entry(ef, phdyn->p_offset, - phdyn->p_filesz, (void **)&ef->ef_dyn) != 0) { - printf("ef_read_entry failed\n"); + case PT_DYNAMIC: + phdyn = phdr; break; } *** 2714 LINES SKIPPED ***