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 ***