1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-18 02:19:39 +00:00

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
This commit is contained in:
John Baldwin 2023-12-12 15:43:00 -08:00
parent c40fa3dc98
commit 0299afdff1
13 changed files with 1687 additions and 927 deletions

View File

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

View File

@ -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;
rel_off = rela_off = 0;
/*
* 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;
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);
error = EFTYPE;
goto out;
}
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);
}
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);
}
return (0);
}
static int
ef_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);
if (rel_off == 0 && rela_off == 0) {
warnx("%s: no ELF relocation table found\n", ef->ef_name);
error = EFTYPE;
goto out;
}
r = read(ef->ef_fd, dest, len);
if (r != -1 && (size_t)r == len)
return (0);
else
return (EIO);
}
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;
}
static int
ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
{
int error;
/*
* 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;
}
*ptr = malloc(len);
if (*ptr == NULL)
return (errno);
error = ef_read(ef, offset, len, *ptr);
if (error != 0)
free(*ptr);
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;
}
}
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;
}
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;
case PT_DYNAMIC:
phdyn = 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");
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);
}

View File

@ -35,71 +35,276 @@
#ifndef _EF_H_
#define _EF_H_
#define EFT_KLD 1
#define EFT_KERNEL 2
#include <sys/linker_set.h>
#include <stdbool.h>
#define EF_RELOC_REL 1
#define EF_RELOC_RELA 2
#define EF_GET_TYPE(ef) \
(ef)->ef_ops->get_type((ef)->ef_ef)
#define EF_CLOSE(ef) \
(ef)->ef_ops->close((ef)->ef_ef)
#define EF_READ(ef, offset, len, dest) \
(ef)->ef_ops->read((ef)->ef_ef, offset, len, dest)
#define EF_READ_ENTRY(ef, offset, len, ptr) \
(ef)->ef_ops->read_entry((ef)->ef_ef, offset, len, ptr)
#define EF_SEG_READ(ef, offset, len, dest) \
(ef)->ef_ops->seg_read((ef)->ef_ef, offset, len, dest)
#define EF_SEG_READ_REL(ef, offset, len, dest) \
(ef)->ef_ops->seg_read_rel((ef)->ef_ef, offset, len, dest)
#define EF_SEG_READ_STRING(ef, offset, len, dest) \
(ef)->ef_ops->seg_read_string((ef)->ef_ef, offset, len, dest)
#define EF_SEG_READ_ENTRY(ef, offset, len, ptr) \
(ef)->ef_ops->seg_read_entry((ef)->kf_ef, offset, len, ptr)
#define EF_SEG_READ_ENTRY_REL(ef, offset, len, ptr) \
(ef)->ef_ops->seg_read_entry_rel((ef)->ef_ef, offset, len, ptr)
#define EF_SEG_READ_REL(ef, address, len, dest) \
(ef)->ef_ops->seg_read_rel((ef)->ef_ef, address, len, dest)
#define EF_SEG_READ_STRING(ef, address, len, dest) \
(ef)->ef_ops->seg_read_string((ef)->ef_ef, address, len, dest)
#define EF_SYMADDR(ef, symidx) \
(ef)->ef_ops->symaddr((ef)->ef_ef, symidx)
#define EF_LOOKUP_SET(ef, name, startp, stopp, countp) \
(ef)->ef_ops->lookup_set((ef)->ef_ef, name, startp, stopp, countp)
#define EF_LOOKUP_SYMBOL(ef, name, sym) \
(ef)->ef_ops->lookup_symbol((ef)->ef_ef, name, sym)
/* XXX, should have a different name. */
typedef struct ef_file *elf_file_t;
/* FreeBSD's headers define additional typedef's for ELF structures. */
typedef Elf64_Size GElf_Size;
typedef Elf64_Hashelt GElf_Hashelt;
struct elf_file;
struct elf_file_ops {
int (*get_type)(elf_file_t ef);
int (*close)(elf_file_t ef);
int (*read)(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
int (*read_entry)(elf_file_t ef, Elf_Off offset, size_t len,
void **ptr);
int (*seg_read)(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
int (*seg_read_rel)(elf_file_t ef, Elf_Off offset, size_t len,
void (*close)(elf_file_t ef);
int (*seg_read_rel)(elf_file_t ef, GElf_Addr address, size_t len,
void *dest);
int (*seg_read_string)(elf_file_t, Elf_Off offset, size_t len,
int (*seg_read_string)(elf_file_t ef, GElf_Addr address, size_t len,
char *dest);
int (*seg_read_entry)(elf_file_t ef, Elf_Off offset, size_t len,
void**ptr);
int (*seg_read_entry_rel)(elf_file_t ef, Elf_Off offset, size_t len,
void**ptr);
Elf_Addr (*symaddr)(elf_file_t ef, Elf_Size symidx);
int (*lookup_set)(elf_file_t ef, const char *name, long *startp,
long *stopp, long *countp);
int (*lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym** sym);
GElf_Addr (*symaddr)(elf_file_t ef, GElf_Size symidx);
int (*lookup_set)(elf_file_t ef, const char *name, GElf_Addr *startp,
GElf_Addr *stopp, long *countp);
};
typedef int (elf_reloc_t)(struct elf_file *ef, const void *reldata,
Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len,
void *dest);
struct elf_reloc_data {
unsigned char class;
unsigned char data;
GElf_Half machine;
elf_reloc_t *reloc;
};
#define ELF_RELOC(_class, _data, _machine, _reloc) \
static struct elf_reloc_data __CONCAT(elf_reloc_data_, __LINE__) = { \
.class = (_class), \
.data = (_data), \
.machine = (_machine), \
.reloc = (_reloc) \
}; \
DATA_SET(elf_reloc, __CONCAT(elf_reloc_data_, __LINE__))
struct elf_file {
elf_file_t ef_ef;
struct elf_file_ops *ef_ops;
const char *ef_filename;
Elf *ef_elf;
elf_reloc_t *ef_reloc;
GElf_Ehdr ef_hdr;
size_t ef_pointer_size;
int ef_fd;
};
#define elf_class(ef) ((ef)->ef_hdr.e_ident[EI_CLASS])
#define elf_encoding(ef) ((ef)->ef_hdr.e_ident[EI_DATA])
/*
* "Generic" versions of module metadata structures.
*/
struct Gmod_depend {
int md_ver_minimum;
int md_ver_preferred;
int md_ver_maximum;
};
struct Gmod_version {
int mv_version;
};
struct Gmod_metadata {
int md_version; /* structure version MDTV_* */
int md_type; /* type of entry MDT_* */
GElf_Addr md_data; /* specific data */
GElf_Addr md_cval; /* common string label */
};
struct Gmod_pnp_match_info
{
GElf_Addr descr; /* Description of the table */
GElf_Addr bus; /* Name of the bus for this table */
GElf_Addr table; /* Pointer to pnp table */
int entry_len; /* Length of each entry in the table (may be */
/* longer than descr describes). */
int num_entry; /* Number of entries in the table */
};
__BEGIN_DECLS
int ef_open(const char *filename, struct elf_file *ef, int verbose);
int ef_obj_open(const char *filename, struct elf_file *ef, int verbose);
int ef_reloc(struct elf_file *ef, const void *reldata, int reltype,
Elf_Off relbase, Elf_Off dataoff, size_t len, void *dest);
/*
* Attempt to parse an open ELF file as either an executable or DSO
* (ef_open) or an object file (ef_obj_open). On success, these
* routines initialize the 'ef_ef' and 'ef_ops' members of 'ef'.
*/
int ef_open(struct elf_file *ef, int verbose);
int ef_obj_open(struct elf_file *ef, int verbose);
/*
* Direct operations on an ELF file regardless of type. Many of these
* use libelf.
*/
/*
* Open an ELF file with libelf. Populates fields other than ef_ef
* and ef_ops in '*efile'.
*/
int elf_open_file(struct elf_file *efile, const char *filename,
int verbose);
/* Close an ELF file. */
void elf_close_file(struct elf_file *efile);
/* Is an ELF file the same architecture as hdr? */
bool elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr);
/* The size of a single object of 'type'. */
size_t elf_object_size(struct elf_file *efile, Elf_Type type);
/* The size of a pointer in architecture of 'efile'. */
size_t elf_pointer_size(struct elf_file *efile);
/*
* Read and convert an array of a data type from an ELF file. This is
* a wrapper around gelf_xlatetom() which reads an array of raw ELF
* objects from the file and converts them into host structures using
* native endianness. The data is returned in a dynamically-allocated
* buffer.
*/
int elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset,
size_t len, void **out);
/* Reads "raw" data from an ELF file without any translation. */
int elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst,
size_t len);
/*
* A wrapper around elf_read_raw_data which returns the data in a
* dynamically-allocated buffer.
*/
int elf_read_raw_data_alloc(struct elf_file *efile, off_t offset,
size_t len, void **out);
/*
* Read relocated data from an ELF file and return it in a
* dynamically-allocated buffer. Note that no translation
* (byte-swapping for endianness, 32-vs-64) is performed on the
* returned data, but any ELF relocations which affect the contents
* are applied to the returned data. The address parameter gives the
* address of the data buffer if the ELF file were loaded into memory
* rather than a direct file offset.
*/
int elf_read_relocated_data(struct elf_file *efile, GElf_Addr address,
size_t len, void **buf);
/*
* Read the program headers from an ELF file and return them in a
* dynamically-allocated array of GElf_Phdr objects.
*/
int elf_read_phdrs(struct elf_file *efile, size_t *nphdrp,
GElf_Phdr **phdrp);
/*
* Read the section headers from an ELF file and return them in a
* dynamically-allocated array of GElf_Shdr objects.
*/
int elf_read_shdrs(struct elf_file *efile, size_t *nshdrp,
GElf_Shdr **shdrp);
/*
* Read the dynamic table from a section of an ELF file into a
* dynamically-allocated array of GElf_Dyn objects.
*/
int elf_read_dynamic(struct elf_file *efile, int section_index, long *ndynp,
GElf_Dyn **dynp);
/*
* Read a symbol table from a section of an ELF file into a
* dynamically-allocated array of GElf_Sym objects.
*/
int elf_read_symbols(struct elf_file *efile, int section_index,
long *nsymp, GElf_Sym **symp);
/*
* Read a string table described by a section header of an ELF file
* into a dynamically-allocated buffer.
*/
int elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
long *strcnt, char **strtab);
/*
* Read a table of relocation objects from a section of an ELF file
* into a dynamically-allocated array of GElf_Rel objects.
*/
int elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
GElf_Rel **relp);
/*
* Read a table of relocation-with-addend objects from a section of an
* ELF file into a dynamically-allocated array of GElf_Rela objects.
*/
int elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
GElf_Rela **relap);
/*
* Read a string from an ELF file and return it in the provided
* buffer. If the string is longer than the buffer, this fails with
* EFAULT. The address parameter gives the address of the data buffer
* if the ELF file were loaded into memory rather than a direct file
* offset.
*/
int elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
size_t len);
/* Return the address extracted from a target pointer stored at 'p'. */
GElf_Addr elf_address_from_pointer(struct elf_file *efile, const void *p);
/*
* Read a linker set and return an array of addresses extracted from the
* relocated pointers in the linker set.
*/
int elf_read_linker_set(struct elf_file *efile, const char *name,
GElf_Addr **buf, long *countp);
/*
* Read and convert a target 'struct mod_depend' into a host
* 'struct Gmod_depend'.
*/
int elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
struct Gmod_depend *mdp);
/*
* Read and convert a target 'struct mod_version' into a host
* 'struct Gmod_version'.
*/
int elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
struct Gmod_version *mdv);
/*
* Read and convert a target 'struct mod_metadata' into a host
* 'struct Gmod_metadata'.
*/
int elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
struct Gmod_metadata *md);
/*
* Read and convert a target 'struct mod_pnp_match_info' into a host
* 'struct Gmod_pnp_match_info'.
*/
int elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
struct Gmod_pnp_match_info *pnp);
/*
* Apply relocations to the values obtained from the file. `relbase' is the
* target relocation address of the section, and `dataoff/len' is the region
* that is to be relocated, and has been copied to *dest
*/
int elf_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest);
__END_DECLS
#endif /* _EF_H_*/

View File

@ -25,12 +25,11 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <gelf.h>
#include "ef.h"
@ -39,28 +38,29 @@
* target relocation address of the section, and `dataoff/len' is the region
* that is to be relocated, and has been copied to *dest
*/
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
static int
ef_aarch64_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf_Addr *where, addend;
Elf_Size rtype;
const Elf_Rela *rela;
char *where;
Elf64_Addr addend;
GElf_Size rtype;
const GElf_Rela *rela;
if (reltype != EF_RELOC_RELA)
if (reltype != ELF_T_RELA)
return (EINVAL);
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset);
rela = (const GElf_Rela *)reldata;
where = (char *)dest - dataoff + rela->r_offset;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
switch(rtype) {
case R_AARCH64_RELATIVE:
*where = relbase + addend;
le64enc(where, relbase + addend);
break;
case R_AARCH64_ABS64:
break;
@ -70,3 +70,5 @@ ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
}
return (0);
}
ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_AARCH64, ef_aarch64_reloc);

View File

@ -27,11 +27,11 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <gelf.h>
#include "ef.h"
@ -40,48 +40,48 @@
* target relocation address of the section, and `dataoff' is the target
* relocation address of the data in `dest'.
*/
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
static int
ef_amd64_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf64_Addr *where, val;
Elf32_Addr *where32, val32;
Elf_Addr addend, addr;
Elf_Size rtype, symidx;
const Elf_Rel *rel;
const Elf_Rela *rela;
char *where;
GElf_Addr val;
GElf_Addr addend, addr;
GElf_Size rtype, symidx;
const GElf_Rel *rel;
const GElf_Rela *rela;
switch (reltype) {
case EF_RELOC_REL:
rel = (const Elf_Rel *)reldata;
where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff);
case ELF_T_REL:
rel = (const GElf_Rel *)reldata;
where = (char *)dest + relbase + rel->r_offset - dataoff;
addend = 0;
rtype = ELF_R_TYPE(rel->r_info);
symidx = ELF_R_SYM(rel->r_info);
rtype = GELF_R_TYPE(rel->r_info);
symidx = GELF_R_SYM(rel->r_info);
break;
case EF_RELOC_RELA:
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff);
case ELF_T_RELA:
rela = (const GElf_Rela *)reldata;
where = (char *)dest + relbase + rela->r_offset - dataoff;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
symidx = GELF_R_SYM(rela->r_info);
break;
default:
return (EINVAL);
}
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
if (reltype == EF_RELOC_REL) {
if (reltype == ELF_T_REL) {
/* Addend is 32 bit on 32 bit relocs */
switch (rtype) {
case R_X86_64_PC32:
case R_X86_64_32S:
addend = *(Elf32_Addr *)where;
addend = le32dec(where);
break;
default:
addend = *where;
addend = le64dec(where);
break;
}
}
@ -92,25 +92,26 @@ ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
case R_X86_64_64: /* S + A */
addr = EF_SYMADDR(ef, symidx);
val = addr + addend;
*where = val;
le64enc(where, val);
break;
case R_X86_64_32S: /* S + A sign extend */
addr = EF_SYMADDR(ef, symidx);
val32 = (Elf32_Addr)(addr + addend);
where32 = (Elf32_Addr *)where;
*where32 = val32;
val = (Elf32_Addr)(addr + addend);
le32enc(where, val);
break;
case R_X86_64_GLOB_DAT: /* S */
addr = EF_SYMADDR(ef, symidx);
*where = addr;
le64enc(where, addr);
break;
case R_X86_64_RELATIVE: /* B + A */
addr = (Elf_Addr)addend + relbase;
addr = addend + relbase;
val = addr;
*where = val;
le64enc(where, val);
break;
default:
warnx("unhandled relocation type %d", (int)rtype);
}
return (0);
}
ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_X86_64, ef_amd64_reloc);

View File

@ -27,11 +27,11 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <gelf.h>
#include "ef.h"
@ -40,57 +40,59 @@
* target relocation address of the section, and `dataoff' is the target
* relocation address of the data in `dest'.
*/
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *_dest)
static int
ef_i386_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf_Addr *where, addr, addend;
Elf_Size rtype, symidx;
const Elf_Rel *rel;
const Elf_Rela *rela;
char *dest = _dest;
char *where;
GElf_Addr addr, addend;
GElf_Size rtype, symidx;
const GElf_Rel *rel;
const GElf_Rela *rela;
switch (reltype) {
case EF_RELOC_REL:
rel = (const Elf_Rel *)reldata;
where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff);
case ELF_T_REL:
rel = (const GElf_Rel *)reldata;
where = (char *)dest + relbase + rel->r_offset - dataoff;
addend = 0;
rtype = ELF_R_TYPE(rel->r_info);
symidx = ELF_R_SYM(rel->r_info);
rtype = GELF_R_TYPE(rel->r_info);
symidx = GELF_R_SYM(rel->r_info);
break;
case EF_RELOC_RELA:
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff);
case ELF_T_RELA:
rela = (const GElf_Rela *)reldata;
where = (char *)dest + relbase + rela->r_offset - dataoff;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
symidx = GELF_R_SYM(rela->r_info);
break;
default:
return (EINVAL);
}
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
if (reltype == EF_RELOC_REL)
addend = *where;
if (reltype == ELF_T_REL)
addend = le32dec(where);
switch (rtype) {
case R_386_RELATIVE: /* A + B */
addr = (Elf_Addr)addend + relbase;
*where = addr;
addr = addend + relbase;
le32enc(where, addr);
break;
case R_386_32: /* S + A - P */
addr = EF_SYMADDR(ef, symidx);
addr += addend;
*where = addr;
le32enc(where, addr);
break;
case R_386_GLOB_DAT: /* S */
addr = EF_SYMADDR(ef, symidx);
*where = addr;
le32enc(where, addr);
break;
default:
warnx("unhandled relocation type %d", (int)rtype);
}
return (0);
}
ELF_RELOC(ELFCLASS32, ELFDATA2LSB, EM_386, ef_i386_reloc);

View File

@ -30,11 +30,11 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <gelf.h>
#include "ef.h"
@ -43,55 +43,78 @@
* target relocation address of the section, and `dataoff' is the target
* relocation address of the data in `dest'.
*/
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
static int
ef_mips_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf_Addr *where, val;
const Elf_Rel *rel;
const Elf_Rela *rela;
Elf_Addr addend, addr;
Elf_Size rtype, symidx;
char *where;
GElf_Addr val;
const GElf_Rel *rel;
const GElf_Rela *rela;
GElf_Addr addend, addr;
GElf_Size rtype, symidx;
switch (reltype) {
case EF_RELOC_REL:
rel = (const Elf_Rel *)reldata;
where = (Elf_Addr *)((char *)dest + relbase + rel->r_offset -
dataoff);
case ELF_T_REL:
rel = (const GElf_Rel *)reldata;
where = (char *)dest + relbase + rel->r_offset - dataoff;
addend = 0;
rtype = ELF_R_TYPE(rel->r_info);
symidx = ELF_R_SYM(rel->r_info);
rtype = GELF_R_TYPE(rel->r_info);
symidx = GELF_R_SYM(rel->r_info);
break;
case EF_RELOC_RELA:
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *)((char *)dest + relbase + rela->r_offset -
dataoff);
case ELF_T_RELA:
rela = (const GElf_Rela *)reldata;
where = (char *)dest + relbase + rela->r_offset - dataoff;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
symidx = GELF_R_SYM(rela->r_info);
break;
default:
return (EINVAL);
}
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
if (reltype == EF_RELOC_REL)
if (reltype == ELF_T_REL) {
if (elf_class(ef) == ELFCLASS64) {
if (elf_encoding(ef) == ELFDATA2LSB)
addend = le64dec(where);
else
addend = be64dec(where);
} else {
if (elf_encoding(ef) == ELFDATA2LSB)
addend = le32dec(where);
else
addend = be32dec(where);
}
addend = *where;
}
switch (rtype) {
#ifdef __LP64__
case R_MIPS_64: /* S + A */
#else
case R_MIPS_32: /* S + A */
#endif
addr = EF_SYMADDR(ef, symidx);
val = addr + addend;
*where = val;
if (elf_encoding(ef) == ELFDATA2LSB)
le64enc(where, val);
else
be64enc(where, val);
break;
case R_MIPS_32: /* S + A */
addr = EF_SYMADDR(ef, symidx);
val = addr + addend;
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);

View File

@ -1,40 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2003 Jake Burkholder.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include "ef.h"
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
{
return (0);
}

View File

@ -35,48 +35,42 @@
*/
#include <sys/param.h>
#include <sys/linker.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <machine/elf.h>
#include "ef.h"
typedef struct {
void *addr;
Elf_Off size;
char *addr;
GElf_Off size;
int flags;
int sec; /* Original section */
char *name;
} Elf_progent;
typedef struct {
Elf_Rel *rel;
int nrel;
GElf_Rel *rel;
long nrel;
int sec;
} Elf_relent;
typedef struct {
Elf_Rela *rela;
int nrela;
GElf_Rela *rela;
long nrela;
int sec;
} Elf_relaent;
struct ef_file {
char *ef_name;
int ef_fd;
Elf_Ehdr ef_hdr;
struct elf_file *ef_efile;
caddr_t address;
Elf_Off size;
Elf_Shdr *e_shdr;
char *address;
GElf_Off size;
Elf_progent *progtab;
int nprogtab;
@ -87,7 +81,7 @@ struct ef_file {
Elf_relent *reltab;
int nrel;
Elf_Sym *ddbsymtab; /* The symbol table we are using */
GElf_Sym *ddbsymtab; /* The symbol table we are using */
long ddbsymcnt; /* Number of symbols */
caddr_t ddbstrtab; /* String table */
long ddbstrcnt; /* number of bytes in string table */
@ -98,54 +92,31 @@ struct ef_file {
int ef_verbose;
};
static int ef_obj_get_type(elf_file_t ef);
static int ef_obj_close(elf_file_t ef);
static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len,
void* dest);
static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
void **ptr);
static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len,
void *dest);
static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
void *dest);
static int ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset,
static void ef_obj_close(elf_file_t ef);
static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address,
size_t len, void *dest);
static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address,
size_t len, char *dest);
static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
void **ptr);
static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset,
size_t len, void **ptr);
static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx);
static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp,
long *stopp, long *countp);
static int ef_obj_lookup_symbol(elf_file_t ef, const char* name,
Elf_Sym** sym);
static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx);
static int ef_obj_lookup_set(elf_file_t ef, const char *name,
GElf_Addr *startp, GElf_Addr *stopp, long *countp);
static int ef_obj_lookup_symbol(elf_file_t ef, const char *name,
GElf_Sym **sym);
static struct elf_file_ops ef_obj_file_ops = {
.get_type = ef_obj_get_type,
.close = ef_obj_close,
.read = ef_obj_read,
.read_entry = ef_obj_read_entry,
.seg_read = ef_obj_seg_read,
.seg_read_rel = ef_obj_seg_read_rel,
.seg_read_string = ef_obj_seg_read_string,
.seg_read_entry = ef_obj_seg_read_entry,
.seg_read_entry_rel = ef_obj_seg_read_entry_rel,
.symaddr = ef_obj_symaddr,
.lookup_set = ef_obj_lookup_set,
.lookup_symbol = ef_obj_lookup_symbol
};
static int
ef_obj_get_type(elf_file_t __unused ef)
ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
{
return (EFT_KLD);
}
static int
ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
{
Elf_Sym *symp;
GElf_Sym *symp;
const char *strp;
int i;
@ -160,102 +131,58 @@ ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
}
static int
ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
long *countp)
ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
GElf_Addr *stopp, long *countp)
{
int i;
for (i = 0; i < ef->nprogtab; i++) {
if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
strcmp(ef->progtab[i].name + 4, name) == 0) {
*startp = (char *)ef->progtab[i].addr - ef->address;
*stopp = (char *)ef->progtab[i].addr +
ef->progtab[i].size - ef->address;
*countp = (*stopp - *startp) / sizeof(void *);
*startp = ef->progtab[i].addr - ef->address;
*stopp = ef->progtab[i].addr + ef->progtab[i].size -
ef->address;
*countp = (*stopp - *startp) /
elf_pointer_size(ef->ef_efile);
return (0);
}
}
return (ESRCH);
}
static Elf_Addr
ef_obj_symaddr(elf_file_t ef, Elf_Size symidx)
static GElf_Addr
ef_obj_symaddr(elf_file_t ef, GElf_Size symidx)
{
const Elf_Sym *sym;
const GElf_Sym *sym;
if (symidx >= (size_t) ef->ddbsymcnt)
if (symidx >= (size_t)ef->ddbsymcnt)
return (0);
sym = ef->ddbsymtab + symidx;
if (sym->st_shndx != SHN_UNDEF)
return (sym->st_value - (Elf_Addr)ef->address);
return (sym->st_value - (GElf_Addr)ef->address);
return (0);
}
static int
ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
{
ssize_t r;
if (offset != (Elf_Off)-1) {
if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
return (EIO);
}
r = read(ef->ef_fd, dest, len);
if (r != -1 && (size_t)r == len)
return (0);
else
return (EIO);
}
static int
ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
{
int error;
*ptr = malloc(len);
if (*ptr == NULL)
return (errno);
error = ef_obj_read(ef, offset, len, *ptr);
if (error != 0)
free(*ptr);
return (error);
}
static int
ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
{
if (offset + len > ef->size) {
if (ef->ef_verbose)
warnx("ef_obj_seg_read(%s): bad offset/len (%lx:%ld)",
ef->ef_name, (long)offset, (long)len);
return (EFAULT);
}
bcopy(ef->address + offset, dest, len);
return (0);
}
static int
ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
{
char *memaddr;
Elf_Rel *r;
Elf_Rela *a;
Elf_Off secbase, dataoff;
GElf_Rel *r;
GElf_Rela *a;
GElf_Addr secbase, dataoff;
int error, i, sec;
if (offset + len > ef->size) {
if (address + len > ef->size) {
if (ef->ef_verbose)
warnx("ef_obj_seg_read_rel(%s): bad offset/len (%lx:%ld)",
ef->ef_name, (long)offset, (long)len);
ef->ef_name, (long)address, (long)len);
return (EFAULT);
}
bcopy(ef->address + offset, dest, len);
bcopy(ef->address + address, dest, len);
/* Find out which section contains the data. */
memaddr = ef->address + offset;
memaddr = ef->address + address;
sec = -1;
secbase = dataoff = 0;
for (i = 0; i < ef->nprogtab; i++) {
@ -280,7 +207,7 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
continue;
for (r = ef->reltab[i].rel;
r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) {
error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secbase,
error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase,
dataoff, len, dest);
if (error != 0)
return (error);
@ -291,8 +218,8 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
continue;
for (a = ef->relatab[i].rela;
a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) {
error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA,
secbase, dataoff, len, dest);
error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase,
dataoff, len, dest);
if (error != 0)
return (error);
}
@ -301,117 +228,65 @@ ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
}
static int
ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest)
ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
{
if (offset >= ef->size) {
if (address >= ef->size) {
if (ef->ef_verbose)
warnx("ef_obj_seg_read_string(%s): bad offset (%lx)",
ef->ef_name, (long)offset);
warnx("ef_obj_seg_read_string(%s): bad address (%lx)",
ef->ef_name, (long)address);
return (EFAULT);
}
if (ef->size - offset < len)
len = ef->size - offset;
if (ef->size - address < len)
len = ef->size - address;
if (strnlen(ef->address + offset, len) == len)
if (strnlen(ef->address + address, len) == len)
return (EFAULT);
memcpy(dest, ef->address + offset, len);
memcpy(dest, ef->address + address, len);
return (0);
}
static int
ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
{
int error;
*ptr = malloc(len);
if (*ptr == NULL)
return (errno);
error = ef_obj_seg_read(ef, offset, len, *ptr);
if (error != 0)
free(*ptr);
return (error);
}
static int
ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
void **ptr)
{
int error;
*ptr = malloc(len);
if (*ptr == NULL)
return (errno);
error = ef_obj_seg_read_rel(ef, offset, len, *ptr);
if (error != 0)
free(*ptr);
return (error);
}
int
ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
ef_obj_open(struct elf_file *efile, int verbose)
{
elf_file_t ef;
Elf_Ehdr *hdr;
Elf_Shdr *shdr;
Elf_Sym *es;
GElf_Ehdr *hdr;
GElf_Shdr *shdr;
GElf_Sym *es;
char *mapbase;
void *vtmp;
size_t mapsize, alignmask, max_addralign;
int error, fd, pb, ra, res, rl;
int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex;
size_t i, mapsize, alignmask, max_addralign, nshdr;
int error, pb, ra, rl;
int j, nsym, symstrindex, symtabindex;
if (filename == NULL)
return (EINVAL);
if ((fd = open(filename, O_RDONLY)) == -1)
return (errno);
hdr = &efile->ef_hdr;
if (hdr->e_type != ET_REL || hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
return (EFTYPE);
ef = calloc(1, sizeof(*ef));
if (ef == NULL) {
close(fd);
if (ef == NULL)
return (errno);
}
efile->ef_ef = ef;
efile->ef_ops = &ef_obj_file_ops;
ef->ef_verbose = verbose;
ef->ef_fd = fd;
ef->ef_name = strdup(filename);
ef->ef_name = strdup(efile->ef_filename);
ef->ef_efile = efile;
hdr = (Elf_Ehdr *)&ef->ef_hdr;
res = read(fd, hdr, sizeof(*hdr));
error = EFTYPE;
if (res != sizeof(*hdr))
goto out;
if (!IS_ELF(*hdr))
goto out;
if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
hdr->e_ident[EI_VERSION] != EV_CURRENT ||
hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH ||
hdr->e_type != ET_REL)
goto out;
nbytes = hdr->e_shnum * hdr->e_shentsize;
if (nbytes == 0 || hdr->e_shoff == 0 ||
hdr->e_shentsize != sizeof(Elf_Shdr))
goto out;
if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) {
printf("ef_read_entry failed\n");
error = elf_read_shdrs(efile, &nshdr, &shdr);
if (error != 0) {
shdr = NULL;
goto out;
}
ef->e_shdr = shdr = vtmp;
/* Scan the section header for information and table sizing. */
/* Scan the section headers for information and table sizing. */
nsym = 0;
symtabindex = -1;
symstrindex = -1;
for (i = 0; i < hdr->e_shnum; i++) {
for (i = 0; i < nshdr; i++) {
switch (shdr[i].sh_type) {
case SHT_PROGBITS:
case SHT_NOBITS:
@ -434,16 +309,16 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
}
if (ef->nprogtab == 0) {
warnx("%s: file has no contents", filename);
warnx("%s: file has no contents", ef->ef_name);
goto out;
}
if (nsym != 1) {
warnx("%s: file has no valid symbol table", filename);
warnx("%s: file has no valid symbol table", ef->ef_name);
goto out;
}
if (symstrindex < 0 || symstrindex > hdr->e_shnum ||
if (symstrindex < 0 || symstrindex > nshdr ||
shdr[symstrindex].sh_type != SHT_STRTAB) {
warnx("%s: file has invalid symbol strings", filename);
warnx("%s: file has invalid symbol strings", ef->ef_name);
goto out;
}
@ -462,29 +337,24 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
goto out;
}
ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset,
shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) {
printf("ef_read_entry failed\n");
if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt,
&ef->ddbsymtab) != 0) {
printf("elf_read_symbols failed\n");
goto out;
}
ef->ddbstrcnt = shdr[symstrindex].sh_size;
if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset,
shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) {
printf("ef_read_entry failed\n");
if (elf_read_string_table(efile, &shdr[symstrindex], &ef->ddbstrcnt,
&ef->ddbstrtab) != 0) {
printf("elf_read_string_table failed\n");
goto out;
}
/* Do we have a string table for the section names? */
shstrindex = -1;
if (hdr->e_shstrndx != 0 &&
shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
shstrindex = hdr->e_shstrndx;
ef->shstrcnt = shdr[shstrindex].sh_size;
if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset,
shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) {
printf("ef_read_entry failed\n");
if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx],
&ef->shstrcnt, &ef->shstrtab) != 0) {
printf("elf_read_string_table failed\n");
goto out;
}
}
@ -493,7 +363,7 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
alignmask = 0;
max_addralign = 0;
mapsize = 0;
for (i = 0; i < hdr->e_shnum; i++) {
for (i = 0; i < nshdr; i++) {
switch (shdr[i].sh_type) {
case SHT_PROGBITS:
case SHT_NOBITS:
@ -523,19 +393,19 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
rl = 0;
ra = 0;
alignmask = 0;
for (i = 0; i < hdr->e_shnum; i++) {
for (i = 0; i < nshdr; i++) {
switch (shdr[i].sh_type) {
case SHT_PROGBITS:
case SHT_NOBITS:
alignmask = shdr[i].sh_addralign - 1;
mapbase += alignmask;
mapbase = (char *)((uintptr_t)mapbase & ~alignmask);
mapbase = (char *)((uintptr_t)mapbase & ~alignmask);
ef->progtab[pb].addr = (void *)(uintptr_t)mapbase;
if (shdr[i].sh_type == SHT_PROGBITS) {
ef->progtab[pb].name = "<<PROGBITS>>";
if (ef_obj_read(ef, shdr[i].sh_offset,
shdr[i].sh_size,
ef->progtab[pb].addr) != 0) {
if (elf_read_raw_data(efile,
shdr[i].sh_offset, ef->progtab[pb].addr,
shdr[i].sh_size) != 0) {
printf("failed to read progbits\n");
goto out;
}
@ -554,30 +424,25 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
es = &ef->ddbsymtab[j];
if (es->st_shndx != i)
continue;
es->st_value += (Elf_Addr)ef->progtab[pb].addr;
es->st_value += (GElf_Addr)ef->progtab[pb].addr;
}
mapbase += shdr[i].sh_size;
pb++;
break;
case SHT_REL:
ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
ef->reltab[rl].sec = shdr[i].sh_info;
if (ef_obj_read_entry(ef, shdr[i].sh_offset,
shdr[i].sh_size, (void**)&ef->reltab[rl].rel) !=
0) {
printf("ef_read_entry failed\n");
if (elf_read_rel(efile, i, &ef->reltab[rl].nrel,
&ef->reltab[rl].rel) != 0) {
printf("elf_read_rel failed\n");
goto out;
}
rl++;
break;
case SHT_RELA:
ef->relatab[ra].nrela =
shdr[i].sh_size / sizeof(Elf_Rela);
ef->relatab[ra].sec = shdr[i].sh_info;
if (ef_obj_read_entry(ef, shdr[i].sh_offset,
shdr[i].sh_size, (void**)&ef->relatab[ra].rela) !=
0) {
printf("ef_read_entry failed\n");
if (elf_read_rela(efile, i, &ef->relatab[ra].nrela,
&ef->relatab[ra].rela) != 0) {
printf("elf_read_rela failed\n");
goto out;
}
ra++;
@ -586,21 +451,19 @@ ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
}
error = 0;
out:
free(shdr);
if (error != 0)
ef_obj_close(ef);
return (error);
}
static int
static void
ef_obj_close(elf_file_t ef)
{
int i;
close(ef->ef_fd);
if (ef->ef_name)
free(ef->ef_name);
if (ef->e_shdr != NULL)
free(ef->e_shdr);
if (ef->size != 0)
free(ef->address);
if (ef->nprogtab != 0)
@ -626,6 +489,4 @@ ef_obj_close(elf_file_t ef)
ef->ef_efile->ef_ops = NULL;
ef->ef_efile->ef_ef = NULL;
free(ef);
return (0);
}

View File

@ -27,63 +27,67 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <gelf.h>
#include "ef.h"
#ifdef __powerpc64__
#define PRI_ELF_SIZE PRIu64
#else
#define PRI_ELF_SIZE PRIu32
#endif
/*
* Apply relocations to the values obtained from the file. `relbase' is the
* target relocation address of the section, and `dataoff/len' is the region
* that is to be relocated, and has been copied to *dest
*/
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
ef_ppc_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf_Addr *where, addend;
Elf32_Addr *where32;
Elf_Size rtype, symidx;
const Elf_Rela *rela;
char *where;
GElf_Addr addend, val;
GElf_Size rtype, symidx;
const GElf_Rela *rela;
if (reltype != EF_RELOC_RELA)
if (reltype != ELF_T_RELA)
return (EINVAL);
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset);
where32 = (Elf32_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset);
rela = (const GElf_Rela *)reldata;
where = (char *)dest - dataoff + rela->r_offset;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
symidx = GELF_R_SYM(rela->r_info);
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
switch (rtype) {
case R_PPC_RELATIVE: /* word32|doubleword64 B + A */
*where = relbase + addend;
val = relbase + addend;
if (elf_class(ef) == ELFCLASS64) {
if (elf_encoding(ef) == ELFDATA2LSB)
le64enc(where, val);
else
be64enc(where, val);
}
break;
case R_PPC_ADDR32: /* word32 S + A */
*where32 = EF_SYMADDR(ef, symidx) + addend;
val = EF_SYMADDR(ef, symidx) + addend;
be32enc(where, val);
break;
#ifdef __powerpc64__
case R_PPC64_ADDR64: /* doubleword64 S + A */
*where = EF_SYMADDR(ef, symidx) + addend;
val = EF_SYMADDR(ef, symidx) + addend;
if (elf_encoding(ef) == ELFDATA2LSB)
le64enc(where, val);
else
be64enc(where, val);
break;
#endif
default:
warnx("unhandled relocation type %" PRI_ELF_SIZE, rtype);
warnx("unhandled relocation type %d", (int)rtype);
}
return (0);
}
ELF_RELOC(ELFCLASS32, ELFDATA2MSB, EM_PPC, ef_ppc_reloc);
ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_PPC64, ef_ppc_reloc);
ELF_RELOC(ELFCLASS64, ELFDATA2MSB, EM_PPC64, ef_ppc_reloc);

View File

@ -30,50 +30,51 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <machine/elf.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <gelf.h>
#include "ef.h"
int
ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
Elf_Off dataoff, size_t len, void *dest)
ef_riscv_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
Elf_Addr *where;
const Elf_Rela *rela;
Elf_Addr addend, addr;
Elf_Size rtype, symidx;
char *where;
const GElf_Rela *rela;
GElf_Addr addend, addr;
GElf_Size rtype, symidx;
switch (reltype) {
case EF_RELOC_RELA:
rela = (const Elf_Rela *)reldata;
where = (Elf_Addr *)((char *)dest + relbase + rela->r_offset -
dataoff);
case ELF_T_RELA:
rela = (const GElf_Rela *)reldata;
where = (char *)dest + relbase + rela->r_offset - dataoff;
addend = rela->r_addend;
rtype = ELF_R_TYPE(rela->r_info);
symidx = ELF_R_SYM(rela->r_info);
rtype = GELF_R_TYPE(rela->r_info);
symidx = GELF_R_SYM(rela->r_info);
break;
default:
return (EINVAL);
}
if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
if (where < (char *)dest || where >= (char *)dest + len)
return (0);
switch (rtype) {
case R_RISCV_64: /* S + A */
addr = EF_SYMADDR(ef, symidx) + addend;
*where = addr;
le64enc(where, addr);
break;
case R_RISCV_RELATIVE: /* B + A */
addr = addend + relbase;
*where = addr;
le64enc(where, addr);
break;
default:
warnx("unhandled relocation type %d", (int)rtype);
}
return (0);
}
ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_RISCV, ef_riscv_reloc);

674
usr.sbin/kldxref/elf.c Normal file
View File

@ -0,0 +1,674 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2023 John Baldwin <jhb@FreeBSD.org>
*
* This software was developed by SRI International and the University
* of Cambridge Computer Laboratory (Department of Computer Science
* and Technology) under Defense Advanced Research Projects Agency
* (DARPA) contract HR0011-18-C-0016 ("ECATS"), as part of the DARPA
* SSITH research programme and under DARPA Contract No. HR001122S0003
* ("MTSS").
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ef.h"
SET_DECLARE(elf_reloc, struct elf_reloc_data);
static elf_reloc_t *
elf_find_reloc(const GElf_Ehdr *hdr)
{
struct elf_reloc_data **erd;
SET_FOREACH(erd, elf_reloc) {
if (hdr->e_ident[EI_CLASS] == (*erd)->class &&
hdr->e_ident[EI_DATA] == (*erd)->data &&
hdr->e_machine == (*erd)->machine)
return ((*erd)->reloc);
}
return (NULL);
}
int
elf_open_file(struct elf_file *efile, const char *filename, int verbose)
{
int error;
memset(efile, 0, sizeof(*efile));
efile->ef_filename = filename;
efile->ef_fd = open(filename, O_RDONLY);
if (efile->ef_fd == -1) {
if (verbose)
warn("open(%s)", filename);
return (errno);
}
efile->ef_elf = elf_begin(efile->ef_fd, ELF_C_READ, NULL);
if (efile->ef_elf == NULL) {
if (verbose)
warnx("elf_begin(%s): %s", filename, elf_errmsg(0));
elf_close_file(efile);
return (EINVAL);
}
if (elf_kind(efile->ef_elf) != ELF_K_ELF) {
if (verbose)
warnx("%s: not an ELF file", filename);
elf_close_file(efile);
return (EINVAL);
}
if (gelf_getehdr(efile->ef_elf, &efile->ef_hdr) == NULL) {
if (verbose)
warnx("gelf_getehdr(%s): %s", filename, elf_errmsg(0));
elf_close_file(efile);
return (EINVAL);
}
efile->ef_reloc = elf_find_reloc(&efile->ef_hdr);
if (efile->ef_reloc == NULL) {
if (verbose)
warnx("%s: unsupported architecture", filename);
elf_close_file(efile);
return (EFTYPE);
}
error = ef_open(efile, verbose);
if (error != 0) {
error = ef_obj_open(efile, verbose);
if (error != 0) {
if (verbose)
warnc(error, "%s: not a valid DSO or object file",
filename);
elf_close_file(efile);
return (error);
}
}
efile->ef_pointer_size = elf_object_size(efile, ELF_T_ADDR);
return (0);
}
void
elf_close_file(struct elf_file *efile)
{
if (efile->ef_ops != NULL) {
EF_CLOSE(efile);
}
if (efile->ef_elf != NULL) {
elf_end(efile->ef_elf);
efile->ef_elf = NULL;
}
if (efile->ef_fd > 0) {
close(efile->ef_fd);
efile->ef_fd = -1;
}
}
bool
elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr)
{
if (efile->ef_hdr.e_ident[EI_CLASS] != hdr->e_ident[EI_CLASS] ||
efile->ef_hdr.e_ident[EI_DATA] != hdr->e_ident[EI_DATA] ||
efile->ef_hdr.e_machine != hdr->e_machine)
return (false);
return (true);
}
size_t
elf_object_size(struct elf_file *efile, Elf_Type type)
{
return (gelf_fsize(efile->ef_elf, type, 1, efile->ef_hdr.e_version));
}
/*
* The number of objects of 'type' in region of the file of size
* 'file_size'.
*/
static size_t
elf_object_count(struct elf_file *efile, Elf_Type type, size_t file_size)
{
return (file_size / elf_object_size(efile, type));
}
int
elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len)
{
ssize_t nread;
if (offset != (off_t)-1) {
if (lseek(efile->ef_fd, offset, SEEK_SET) == -1)
return (EIO);
}
nread = read(efile->ef_fd, dst, len);
if (nread == -1)
return (errno);
if (nread != len)
return (EIO);
return (0);
}
int
elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len,
void **out)
{
void *buf;
int error;
buf = malloc(len);
if (buf == NULL)
return (ENOMEM);
error = elf_read_raw_data(efile, offset, buf, len);
if (error != 0) {
free(buf);
return (error);
}
*out = buf;
return (0);
}
int
elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len,
void **out)
{
Elf_Data dst, src;
void *buf;
int error;
buf = malloc(len);
if (buf == NULL)
return (ENOMEM);
error = elf_read_raw_data(efile, offset, buf, len);
if (error != 0) {
free(buf);
return (error);
}
memset(&dst, 0, sizeof(dst));
memset(&src, 0, sizeof(src));
src.d_buf = buf;
src.d_size = len;
src.d_type = type;
src.d_version = efile->ef_hdr.e_version;
dst.d_buf = buf;
dst.d_size = len;
dst.d_version = EV_CURRENT;
if (gelf_xlatetom(efile->ef_elf, &dst, &src, elf_encoding(efile)) ==
NULL) {
free(buf);
return (ENXIO);
}
if (dst.d_size != len)
warnx("elf_read_data: translation of type %u size mismatch",
type);
*out = buf;
return (0);
}
int
elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len,
void **buf)
{
int error;
void *p;
p = malloc(len);
if (p == NULL)
return (ENOMEM);
error = EF_SEG_READ_REL(efile, address, len, p);
if (error != 0) {
free(p);
return (error);
}
*buf = p;
return (0);
}
int
elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp)
{
GElf_Phdr *phdr;
size_t nphdr, i;
int error;
if (elf_getphdrnum(efile->ef_elf, &nphdr) == -1)
return (EFTYPE);
phdr = calloc(nphdr, sizeof(*phdr));
if (phdr == NULL)
return (ENOMEM);
for (i = 0; i < nphdr; i++) {
if (gelf_getphdr(efile->ef_elf, i, &phdr[i]) == NULL) {
error = EFTYPE;
goto out;
}
}
*nphdrp = nphdr;
*phdrp = phdr;
return (0);
out:
free(phdr);
return (error);
}
int
elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp)
{
GElf_Shdr *shdr;
Elf_Scn *scn;
size_t nshdr, i;
int error;
if (elf_getshdrnum(efile->ef_elf, &nshdr) == -1)
return (EFTYPE);
shdr = calloc(nshdr, sizeof(*shdr));
if (shdr == NULL)
return (ENOMEM);
for (i = 0; i < nshdr; i++) {
scn = elf_getscn(efile->ef_elf, i);
if (scn == NULL) {
error = EFTYPE;
goto out;
}
if (gelf_getshdr(scn, &shdr[i]) == NULL) {
error = EFTYPE;
goto out;
}
}
*nshdrp = nshdr;
*shdrp = shdr;
return (0);
out:
free(shdr);
return (error);
}
int
elf_read_dynamic(struct elf_file *efile, int section_index, long *ndynp,
GElf_Dyn **dynp)
{
GElf_Shdr shdr;
Elf_Scn *scn;
Elf_Data *data;
GElf_Dyn *dyn;
long i, ndyn;
scn = elf_getscn(efile->ef_elf, section_index);
if (scn == NULL)
return (EINVAL);
if (gelf_getshdr(scn, &shdr) == NULL)
return (EINVAL);
data = elf_getdata(scn, NULL);
if (data == NULL)
return (EINVAL);
ndyn = elf_object_count(efile, ELF_T_DYN, shdr.sh_size);
dyn = calloc(ndyn, sizeof(*dyn));
if (dyn == NULL)
return (ENOMEM);
for (i = 0; i < ndyn; i++) {
if (gelf_getdyn(data, i, &dyn[i]) == NULL) {
free(dyn);
return (EINVAL);
}
}
*ndynp = ndyn;
*dynp = dyn;
return (0);
}
int
elf_read_symbols(struct elf_file *efile, int section_index, long *nsymp,
GElf_Sym **symp)
{
GElf_Shdr shdr;
Elf_Scn *scn;
Elf_Data *data;
GElf_Sym *sym;
long i, nsym;
scn = elf_getscn(efile->ef_elf, section_index);
if (scn == NULL)
return (EINVAL);
if (gelf_getshdr(scn, &shdr) == NULL)
return (EINVAL);
data = elf_getdata(scn, NULL);
if (data == NULL)
return (EINVAL);
nsym = elf_object_count(efile, ELF_T_SYM, shdr.sh_size);
sym = calloc(nsym, sizeof(*sym));
if (sym == NULL)
return (ENOMEM);
for (i = 0; i < nsym; i++) {
if (gelf_getsym(data, i, &sym[i]) == NULL) {
free(sym);
return (EINVAL);
}
}
*nsymp = nsym;
*symp = sym;
return (0);
}
int
elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
long *strcnt, char **strtab)
{
int error;
if (shdr->sh_type != SHT_STRTAB)
return (EINVAL);
error = elf_read_raw_data_alloc(efile, shdr->sh_offset, shdr->sh_size,
(void **)strtab);
if (error != 0)
return (error);
*strcnt = shdr->sh_size;
return (0);
}
int
elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
GElf_Rel **relp)
{
GElf_Shdr shdr;
Elf_Scn *scn;
Elf_Data *data;
GElf_Rel *rel;
long i, nrel;
scn = elf_getscn(efile->ef_elf, section_index);
if (scn == NULL)
return (EINVAL);
if (gelf_getshdr(scn, &shdr) == NULL)
return (EINVAL);
data = elf_getdata(scn, NULL);
if (data == NULL)
return (EINVAL);
nrel = elf_object_count(efile, ELF_T_REL, shdr.sh_size);
rel = calloc(nrel, sizeof(*rel));
if (rel == NULL)
return (ENOMEM);
for (i = 0; i < nrel; i++) {
if (gelf_getrel(data, i, &rel[i]) == NULL) {
free(rel);
return (EINVAL);
}
}
*nrelp = nrel;
*relp = rel;
return (0);
}
int
elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
GElf_Rela **relap)
{
GElf_Shdr shdr;
Elf_Scn *scn;
Elf_Data *data;
GElf_Rela *rela;
long i, nrela;
scn = elf_getscn(efile->ef_elf, section_index);
if (scn == NULL)
return (EINVAL);
if (gelf_getshdr(scn, &shdr) == NULL)
return (EINVAL);
data = elf_getdata(scn, NULL);
if (data == NULL)
return (EINVAL);
nrela = elf_object_count(efile, ELF_T_RELA, shdr.sh_size);
rela = calloc(nrela, sizeof(*rela));
if (rela == NULL)
return (ENOMEM);
for (i = 0; i < nrela; i++) {
if (gelf_getrela(data, i, &rela[i]) == NULL) {
free(rela);
return (EINVAL);
}
}
*nrelap = nrela;
*relap = rela;
return (0);
}
size_t
elf_pointer_size(struct elf_file *efile)
{
return (efile->ef_pointer_size);
}
int
elf_int(struct elf_file *efile, const void *p)
{
if (elf_encoding(efile) == ELFDATA2LSB)
return (le32dec(p));
else
return (be32dec(p));
}
GElf_Addr
elf_address_from_pointer(struct elf_file *efile, const void *p)
{
switch (elf_class(efile)) {
case ELFCLASS32:
if (elf_encoding(efile) == ELFDATA2LSB)
return (le32dec(p));
else
return (be32dec(p));
case ELFCLASS64:
if (elf_encoding(efile) == ELFDATA2LSB)
return (le64dec(p));
else
return (be64dec(p));
default:
__builtin_unreachable();
}
}
int
elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
size_t len)
{
return (EF_SEG_READ_STRING(efile, address, len, dst));
}
int
elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **bufp,
long *countp)
{
GElf_Addr *buf, start, stop;
char *p;
void *raw;
long i, count;
int error;
error = EF_LOOKUP_SET(efile, name, &start, &stop, &count);
if (error != 0)
return (error);
error = elf_read_relocated_data(efile, start,
count * elf_pointer_size(efile), &raw);
if (error != 0)
return (error);
buf = calloc(count, sizeof(*buf));
if (buf == NULL) {
free(raw);
return (ENOMEM);
}
p = raw;
for (i = 0; i < count; i++) {
buf[i] = elf_address_from_pointer(efile, p);
p += elf_pointer_size(efile);
}
free(raw);
*bufp = buf;
*countp = count;
return (0);
}
int
elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
struct Gmod_depend *mdp)
{
int *p;
int error;
error = elf_read_relocated_data(efile, addr, sizeof(int) * 3,
(void **)&p);
if (error != 0)
return (error);
memset(mdp, 0, sizeof(*mdp));
mdp->md_ver_minimum = elf_int(efile, p);
mdp->md_ver_preferred = elf_int(efile, p + 1);
mdp->md_ver_maximum = elf_int(efile, p + 2);
free(p);
return (0);
}
int
elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
struct Gmod_version *mdv)
{
int error, value;
error = EF_SEG_READ_REL(efile, addr, sizeof(int), &value);
if (error != 0)
return (error);
memset(mdv, 0, sizeof(*mdv));
mdv->mv_version = elf_int(efile, &value);
return (0);
}
int
elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
struct Gmod_metadata *md)
{
char *p;
size_t len, offset, pointer_size;
int error;
pointer_size = elf_pointer_size(efile);
len = 2 * sizeof(int);
len = roundup(len, pointer_size);
len += 2 * pointer_size;
error = elf_read_relocated_data(efile, addr, len, (void **)&p);
if (error != 0)
return (error);
memset(md, 0, sizeof(*md));
offset = 0;
md->md_version = elf_int(efile, p + offset);
offset += sizeof(int);
md->md_type = elf_int(efile, p + offset);
offset += sizeof(int);
offset = roundup(offset, pointer_size);
md->md_data = elf_address_from_pointer(efile, p + offset);
offset += pointer_size;
md->md_cval = elf_address_from_pointer(efile, p + offset);
free(p);
return (0);
}
int
elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
struct Gmod_pnp_match_info *pnp)
{
char *p;
size_t len, offset, pointer_size;
int error;
pointer_size = elf_pointer_size(efile);
len = 3 * pointer_size;
len = roundup(len, pointer_size);
len += 2 * sizeof(int);
error = elf_read_relocated_data(efile, addr, len, (void **)&p);
if (error != 0)
return (error);
memset(pnp, 0, sizeof(*pnp));
offset = 0;
pnp->descr = elf_address_from_pointer(efile, p + offset);
offset += pointer_size;
pnp->bus = elf_address_from_pointer(efile, p + offset);
offset += pointer_size;
pnp->table = elf_address_from_pointer(efile, p + offset);
offset += pointer_size;
offset = roundup(offset, pointer_size);
pnp->entry_len = elf_int(efile, p + offset);
offset += sizeof(int);
pnp->num_entry = elf_int(efile, p + offset);
free(p);
return (0);
}
int
elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype,
GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
{
return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len,
dest));
}

View File

@ -32,27 +32,24 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/exec.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <sys/reboot.h>
#include <sys/linker.h>
#include <sys/stat.h>
#include <sys/module.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <gelf.h>
#include <libelf.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <machine/elf.h>
#include "ef.h"
@ -63,6 +60,9 @@ static bool dflag; /* do not create a hint file, only write on stdout */
static int verbose;
static FILE *fxref; /* current hints file */
static int byte_order;
static GElf_Ehdr ehdr;
static char *ehdr_filename;
static const char *xref_file = "linker.hints";
@ -80,6 +80,19 @@ 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)
{
@ -92,11 +105,24 @@ static int
record_end(void)
{
if (recpos == 0)
if (recpos == 0) {
/*
* Pretend to have written a record in debug mode so
* the architecture check works.
*/
if (dflag)
reccnt++;
return (0);
}
if (reccnt == 0) {
/* File version record. */
write_int(1);
}
reccnt++;
intalign();
fwrite(&recpos, sizeof(recpos), 1, fxref);
write_int(recpos);
return (fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0);
}
@ -112,14 +138,21 @@ record_buf(const void *buf, size_t size)
}
/*
* An int is stored in host order and aligned
* An int is stored in target byte order and aligned
*/
static int
record_int(int val)
{
char buf[4];
assert(byte_order != ELFDATANONE);
if (byte_order == ELFDATA2LSB)
le32enc(buf, val);
else
be32enc(buf, val);
intalign();
return (record_buf(&val, sizeof(val)));
return (record_buf(buf, sizeof(buf)));
}
/*
@ -227,7 +260,8 @@ typedef TAILQ_HEAD(pnp_head, pnp_elt) pnp_list;
* sign extension to uint32_t to simplify parsing downstream.
*/
static int
parse_pnp_list(const char *desc, char **new_desc, pnp_list *list)
parse_pnp_list(struct elf_file *ef, const char *desc, char **new_desc,
pnp_list *list)
{
const char *walker, *ep;
const char *colon, *semi;
@ -274,7 +308,7 @@ parse_pnp_list(const char *desc, char **new_desc, pnp_list *list)
printf("Found type %s for name %s\n", type, key);
/* Skip pointer place holders */
if (strcmp(type, "P") == 0) {
off += sizeof(void *);
off += elf_pointer_size(ef);
continue;
}
@ -333,8 +367,8 @@ parse_pnp_list(const char *desc, char **new_desc, pnp_list *list)
/* doesn't actually consume space in the table */
off = elt->pe_offset;
} else {
elt->pe_offset = roundup2(elt->pe_offset, sizeof(void *));
off = elt->pe_offset + sizeof(void *);
elt->pe_offset = roundup2(elt->pe_offset, elf_pointer_size(ef));
off = elt->pe_offset + elf_pointer_size(ef);
}
if (elt->pe_kind & TYPE_PAIRED) {
char *word, *ctx, newtype;
@ -392,6 +426,24 @@ free_pnp_list(char *new_desc, pnp_list *list)
free(new_desc);
}
static uint16_t
parse_16(const void *p)
{
if (byte_order == ELFDATA2LSB)
return (le16dec(p));
else
return (be16dec(p));
}
static uint32_t
parse_32(const void *p)
{
if (byte_order == ELFDATA2LSB)
return (le32dec(p));
else
return (be32dec(p));
}
static void
parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker)
{
@ -402,7 +454,7 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker)
char buffer[1024];
if (elt->pe_kind == TYPE_W32) {
memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
v4 = parse_32(walker + elt->pe_offset);
value = v4 & 0xffff;
record_int(value);
if (verbose > 1)
@ -421,14 +473,14 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker)
value = v1;
break;
case 2:
memcpy(&v2, walker + elt->pe_offset, sizeof(v2));
v2 = parse_16(walker + elt->pe_offset);
if ((elt->pe_kind & TYPE_FLAGGED) && v2 == 0xffff)
value = -1;
else
value = v2;
break;
case 4:
memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
v4 = parse_32(walker + elt->pe_offset);
if ((elt->pe_kind & TYPE_FLAGGED) && v4 == 0xffffffff)
value = -1;
else
@ -444,16 +496,17 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker)
/* Do nothing */
} else { /* E, Z or D -- P already filtered */
if (elt->pe_kind == TYPE_E) {
memcpy(&v4, walker + elt->pe_offset, sizeof(v4));
v4 = parse_32(walker + elt->pe_offset);
strcpy(buffer, pnp_eisaformat(v4));
} else {
char *ptr;
GElf_Addr address;
ptr = *(char **)(walker + elt->pe_offset);
address = elf_address_from_pointer(ef, walker +
elt->pe_offset);
buffer[0] = '\0';
if (ptr != NULL) {
EF_SEG_READ_STRING(ef, (Elf_Off)ptr,
sizeof(buffer), buffer);
if (address != 0) {
elf_read_string(ef, address, buffer,
sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
}
}
@ -466,7 +519,7 @@ parse_pnp_entry(struct elf_file *ef, struct pnp_elt *elt, const char *walker)
static void
record_pnp_info(struct elf_file *ef, const char *cval,
struct mod_pnp_match_info *pnp, const char *descr)
struct Gmod_pnp_match_info *pnp, const char *descr)
{
pnp_list list;
struct pnp_elt *elt;
@ -483,17 +536,15 @@ record_pnp_info(struct elf_file *ef, const char *cval,
* Parse descr to weed out the chaff and to create a list
* of offsets to output.
*/
parse_pnp_list(descr, &new_descr, &list);
parse_pnp_list(ef, descr, &new_descr, &list);
record_int(MDT_PNP_INFO);
record_string(cval);
record_string(new_descr);
record_int(pnp->num_entry);
len = pnp->num_entry * pnp->entry_len;
table = malloc(len);
error = EF_SEG_READ_REL(ef, (Elf_Off)pnp->table, len, table);
error = elf_read_relocated_data(ef, pnp->table, len, &table);
if (error != 0) {
free_pnp_list(new_descr, &list);
free(table);
return;
}
@ -517,29 +568,29 @@ record_pnp_info(struct elf_file *ef, const char *cval,
}
static int
parse_entry(struct mod_metadata *md, const char *cval,
parse_entry(struct Gmod_metadata *md, const char *cval,
struct elf_file *ef, const char *kldname)
{
struct mod_depend mdp;
struct mod_version mdv;
struct mod_pnp_match_info pnp;
struct Gmod_depend mdp;
struct Gmod_version mdv;
struct Gmod_pnp_match_info pnp;
char descr[1024];
Elf_Off data;
GElf_Addr data;
int error;
data = (Elf_Off)md->md_data;
data = md->md_data;
error = 0;
record_start();
switch (md->md_type) {
case MDT_DEPEND:
if (!dflag)
break;
check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp));
check(elf_read_mod_depend(ef, data, &mdp));
printf(" depends on %s.%d (%d,%d)\n", cval,
mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
break;
case MDT_VERSION:
check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv));
check(elf_read_mod_version(ef, data, &mdv));
if (dflag) {
printf(" interface %s.%d\n", cval, mdv.mv_version);
} else {
@ -559,9 +610,8 @@ parse_entry(struct mod_metadata *md, const char *cval,
}
break;
case MDT_PNP_INFO:
check(EF_SEG_READ_REL(ef, data, sizeof(pnp), &pnp));
check(EF_SEG_READ_STRING(ef, (Elf_Off)pnp.descr, sizeof(descr), descr));
descr[sizeof(descr) - 1] = '\0';
check(elf_read_mod_pnp_match_info(ef, data, &pnp));
check(elf_read_string(ef, pnp.descr, descr, sizeof(descr)));
if (dflag) {
printf(" pnp info for bus %s format %s %d entries of %d bytes\n",
cval, descr, pnp.num_entry, pnp.entry_len);
@ -580,34 +630,35 @@ parse_entry(struct mod_metadata *md, const char *cval,
static int
read_kld(char *filename, char *kldname)
{
struct mod_metadata md;
struct Gmod_metadata md;
struct elf_file ef;
void **p;
int error, eftype;
long start, finish, entries, i;
GElf_Addr *p;
int error;
long entries, i;
char cval[MAXMODNAME + 1];
if (verbose || dflag)
printf("%s\n", filename);
error = ef_open(filename, &ef, verbose);
if (error != 0) {
error = ef_obj_open(filename, &ef, verbose);
if (error != 0) {
if (verbose)
warnc(error, "elf_open(%s)", filename);
return (error);
}
}
eftype = EF_GET_TYPE(&ef);
if (eftype != EFT_KLD && eftype != EFT_KERNEL) {
EF_CLOSE(&ef);
return (0);
error = elf_open_file(&ef, filename, verbose);
if (error != 0)
return (error);
if (reccnt == 0) {
ehdr = ef.ef_hdr;
byte_order = elf_encoding(&ef);
free(ehdr_filename);
ehdr_filename = strdup(filename);
} else if (!elf_compatible(&ef, &ehdr)) {
warnx("%s does not match architecture of %s",
filename, ehdr_filename);
elf_close_file(&ef);
return (EINVAL);
}
do {
check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish,
&entries));
check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries,
(void *)&p));
check(elf_read_linker_set(&ef, MDT_SETNAME, &p, &entries));
/*
* Do a first pass to find MDT_MODULE. It is required to be
* ordered first in the output linker.hints stream because it
@ -627,16 +678,16 @@ read_kld(char *filename, char *kldname)
* in the same kld.
*/
for (i = 0; i < entries; i++) {
check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md),
&md));
check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval,
sizeof(cval), cval));
check(elf_read_mod_metadata(&ef, p[i], &md));
check(elf_read_string(&ef, md.md_cval, cval,
sizeof(cval)));
if (md.md_type == MDT_MODULE) {
parse_entry(&md, cval, &ef, kldname);
break;
}
}
if (error != 0) {
free(p);
warnc(error, "error while reading %s", filename);
break;
}
@ -645,10 +696,9 @@ read_kld(char *filename, char *kldname)
* Second pass for all !MDT_MODULE entries.
*/
for (i = 0; i < entries; i++) {
check(EF_SEG_READ_REL(&ef, (Elf_Off)p[i], sizeof(md),
&md));
check(EF_SEG_READ_STRING(&ef, (Elf_Off)md.md_cval,
sizeof(cval), cval));
check(elf_read_mod_metadata(&ef, p[i], &md));
check(elf_read_string(&ef, md.md_cval, cval,
sizeof(cval)));
if (md.md_type != MDT_MODULE)
parse_entry(&md, cval, &ef, kldname);
}
@ -656,7 +706,7 @@ read_kld(char *filename, char *kldname)
warnc(error, "error while reading %s", filename);
free(p);
} while(0);
EF_CLOSE(&ef);
elf_close_file(&ef);
return (error);
}
@ -714,7 +764,7 @@ main(int argc, char *argv[])
FTS *ftsp;
FTSENT *p;
char *dot = NULL;
int opt, fts_options, ival;
int opt, fts_options;
struct stat sb;
fts_options = FTS_PHYSICAL;
@ -750,6 +800,9 @@ main(int argc, char *argv[])
err(1, "%s", argv[0]);
}
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "unsupported libelf");
ftsp = fts_open(argv, fts_options, compare);
if (ftsp == NULL)
exit(1);
@ -777,8 +830,7 @@ main(int argc, char *argv[])
fxref = maketempfile(tempname, ftsp->fts_path);
if (fxref == NULL)
err(1, "can't create %s", tempname);
ival = 1;
fwrite(&ival, sizeof(ival), 1, fxref);
byte_order = ELFDATANONE;
reccnt = 0;
}
/* skip non-files.. */