The branch stable/14 has been updated by jhb:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=01aec6e1fd68511b6e0a204b74afac86c3d4edf5

commit 01aec6e1fd68511b6e0a204b74afac86c3d4edf5
Author:     John Baldwin <[email protected]>
AuthorDate: 2023-12-12 23:43:00 +0000
Commit:     John Baldwin <[email protected]>
CommitDate: 2024-01-18 21:29:23 +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
    
    (cherry picked from commit 0299afdff145e5d861797fe9c2de8b090c456fba)
---
 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   |  36 +--
 usr.sbin/kldxref/elf.c        | 674 ++++++++++++++++++++++++++++++++++++++
 usr.sbin/kldxref/kldxref.c    | 196 +++++++-----
 13 files changed, 1685 insertions(+), 926 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;
*** 2717 LINES SKIPPED ***

Reply via email to