1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 07:27:59 -08:00

Use elf(3) api instead of an ad-hoc elf parser.

Ok mpi@
This commit is contained in:
sunil 2019-05-14 03:16:55 +00:00
parent 24b86205f8
commit 9b214edac7
3 changed files with 113 additions and 427 deletions

View File

@ -1,11 +1,11 @@
PROG= ctfdump
SRCS= ctfdump.c elf.c
SRCS= ctfdump.c
CFLAGS+= -W -Wall -Wstrict-prototypes -Wno-unused -Wunused-variable
CFLAGS+= -DZLIB
LDADD+= -lz
DPADD+= ${LIBZ}
LDADD+= -lelf -lz
DPADD+= ${LIBELF} ${LIBZ}
.include <bsd.prog.mk>

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ctfdump.c,v 1.22 2019/03/16 16:35:03 sunil Exp $ */
/* $OpenBSD: ctfdump.c,v 1.23 2019/05/14 03:16:55 sunil Exp $ */
/*
* Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
@ -21,9 +21,10 @@
#include <sys/mman.h>
#include <sys/ctf.h>
#include <elf.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <locale.h>
#include <stdio.h>
#include <stdint.h>
@ -60,19 +61,9 @@ const char *ctf_fpenc2name(uint16_t);
const char *ctf_off2name(struct ctf_header *, const char *, off_t,
uint32_t);
int elf_dump(char *, size_t, uint8_t);
const char *elf_idx2sym(size_t *, uint8_t);
/* elf.c */
int iself(const char *, size_t);
int elf_getshstab(const char *, size_t, const char **, size_t *);
ssize_t elf_getsymtab(const char *, size_t filesize, const char *,
size_t, const Elf_Sym **, size_t *, const char **,
size_t *);
ssize_t elf_getsection(char *, size_t, const char *, const char *,
size_t, const char **, size_t *);
char *decompress(const char *, size_t, off_t);
int elf_dump(uint8_t);
const char *elf_idx2sym(size_t *, uint8_t);
int
main(int argc, char *argv[])
@ -121,119 +112,162 @@ main(int argc, char *argv[])
if (flags == 0)
flags = 0xff;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "elf_version: %s", elf_errmsg(-1));
while ((filename = *argv++) != NULL)
error |= dump(filename, flags);
return error;
}
Elf *e;
Elf_Scn *scnsymtab;
size_t strtabndx, strtabsz, nsymb;
int
dump(const char *path, uint8_t flags)
{
struct stat st;
int fd, error = 1;
char *p;
struct stat st;
char *p;
int fd, error = 1;
fd = open(path, O_RDONLY);
if (fd == -1) {
warn("open");
return 1;
}
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
warnx("elf_begin: %s", elf_errmsg(-1));
goto done;
}
if (elf_kind(e) == ELF_K_ELF) {
error = elf_dump(flags);
elf_end(e);
goto done;
}
elf_end(e);
if (fstat(fd, &st) == -1) {
warn("fstat");
close(fd);
return 1;
goto done;
}
if ((uintmax_t)st.st_size > SIZE_MAX) {
warnx("file too big to fit memory");
close(fd);
return 1;
goto done;
}
p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (p == MAP_FAILED)
err(1, "mmap");
if (iself(p, st.st_size)) {
error = elf_dump(p, st.st_size, flags);
} else if (isctf(p, st.st_size)) {
if (isctf(p, st.st_size))
error = ctf_dump(p, st.st_size, flags);
}
munmap(p, st.st_size);
close(fd);
done:
close(fd);
return error;
}
const char *strtab;
const Elf_Sym *symtab;
size_t strtabsz, nsymb;
const char *
elf_idx2sym(size_t *idx, uint8_t type)
{
const Elf_Sym *st;
GElf_Sym sym;
Elf_Data *data;
char *name;
size_t i;
if (strtab == NULL)
if (scnsymtab == NULL || strtabndx == 0)
return NULL;
for (i = *idx + 1; i < nsymb; i++) {
st = &symtab[i];
data = NULL;
while ((data = elf_rawdata(scnsymtab, data)) != NULL) {
for (i = *idx + 1; i < nsymb; i++) {
if (gelf_getsym(data, i, &sym) != &sym)
continue;
if (GELF_ST_TYPE(sym.st_info) != type)
continue;
if (sym.st_name >= strtabsz)
break;
if ((name = elf_strptr(e, strtabndx,
sym.st_name)) == NULL)
continue;
if (ELF_ST_TYPE(st->st_info) != type)
continue;
if (st->st_name >= strtabsz)
break;
*idx = i;
return strtab + st->st_name;
*idx = i;
return name;
}
}
return NULL;
}
int
elf_dump(char *p, size_t filesize, uint8_t flags)
elf_dump(uint8_t flags)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
Elf_Shdr *sh;
const char *shstab;
size_t i, shstabsz;
GElf_Shdr shdr;
Elf_Scn *scn, *scnctf;
Elf_Data *data;
char *name;
size_t shstrndx;
int error = 1;
/* Find section header string table location and size. */
if (elf_getshstab(p, filesize, &shstab, &shstabsz))
return 1;
/* Find symbol table and associated string table. */
if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb,
&strtab, &strtabsz) == -1)
warnx("symbol table not found");
/* Find CTF section and dump it. */
for (i = 0; i < eh->e_shnum; i++) {
sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
if ((sh->sh_link >= eh->e_shnum) ||
(sh->sh_name >= shstabsz))
continue;
if (strncmp(shstab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
continue;
if ((sh->sh_offset + sh->sh_size) > filesize)
continue;
if (!isctf(p + sh->sh_offset, sh->sh_size))
break;
return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
if (elf_getshdrstrndx(e, &shstrndx) != 0) {
warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
return error;
}
warnx("%s section not found", ELF_CTF);
return 1;
scn = scnctf = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr: %s", elf_errmsg(-1));
return error;
}
if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
warnx("elf_strptr: %s", elf_errmsg(-1));
return error;
}
if (strcmp(name, ELF_CTF) == 0)
scnctf = scn;
if (strcmp(name, ELF_SYMTAB) == 0 &&
shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
scnsymtab = scn;
nsymb = shdr.sh_size / shdr.sh_entsize;
}
if (strcmp(name, ELF_STRTAB) == 0 &&
shdr.sh_type == SHT_STRTAB) {
strtabndx = elf_ndxscn(scn);
strtabsz = shdr.sh_size;
}
}
if (scnctf == NULL) {
warnx("%s section not found", ELF_CTF);
return error;
}
if (scnsymtab == NULL)
warnx("symbol table not found");
data = NULL;
while ((data = elf_rawdata(scnctf, data)) != NULL) {
if (data->d_buf == NULL) {
warnx("%s section size is zero", ELF_CTF);
return error;
}
if (isctf(data->d_buf, data->d_size))
error |= ctf_dump(data->d_buf, data->d_size, flags);
}
return error;
}
int

View File

@ -1,348 +0,0 @@
/* $OpenBSD: elf.c,v 1.8 2017/11/14 09:14:50 mpi Exp $ */
/*
* Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <machine/reloc.h>
#include <assert.h>
#include <elf.h>
#include <err.h>
#include <string.h>
static int elf_reloc_size(unsigned long);
static void elf_reloc_apply(const char *, size_t, const char *, size_t,
ssize_t, char *, size_t);
int
iself(const char *p, size_t filesize)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
if (filesize < (off_t)sizeof(Elf_Ehdr)) {
warnx("file too small to be ELF");
return 0;
}
if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh))
return 0;
if (eh->e_ident[EI_CLASS] != ELFCLASS) {
warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
return 0;
}
if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
return 0;
}
if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
return 0;
}
if (eh->e_shoff > filesize) {
warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff);
return 0;
}
if (eh->e_shentsize < sizeof(Elf_Shdr)) {
warnx("bogus section header size %u", eh->e_shentsize);
return 0;
}
if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
warnx("bogus section header count %u", eh->e_shnum);
return 0;
}
if (eh->e_shstrndx >= eh->e_shnum) {
warnx("bogus string table index %u", eh->e_shstrndx);
return 0;
}
return 1;
}
int
elf_getshstab(const char *p, size_t filesize, const char **shstab,
size_t *shstabsize)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
Elf_Shdr *sh;
size_t shoff;
shoff = eh->e_shoff + eh->e_shstrndx * eh->e_shentsize;
if (shoff > (filesize - sizeof(*sh))) {
warnx("unexpected string table size");
return -1;
}
sh = (Elf_Shdr *)(p + shoff);
if (sh->sh_type != SHT_STRTAB) {
warnx("unexpected string table type");
return -1;
}
if (sh->sh_offset > filesize) {
warnx("bogus string table offset");
return -1;
}
if (sh->sh_size > filesize - sh->sh_offset) {
warnx("bogus string table size");
return -1;
}
if (shstab != NULL)
*shstab = p + sh->sh_offset;
if (shstabsize != NULL)
*shstabsize = sh->sh_size;
return 0;
}
ssize_t
elf_getsymtab(const char *p, size_t filesize, const char *shstab,
size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab,
size_t *strtabsz)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
Elf_Shdr *sh, *symsh;
size_t snlen, shoff;
ssize_t i;
snlen = strlen(ELF_SYMTAB);
symsh = NULL;
for (i = 0; i < eh->e_shnum; i++) {
shoff = eh->e_shoff + i * eh->e_shentsize;
if (shoff > (filesize - sizeof(*sh)))
continue;
sh = (Elf_Shdr *)(p + shoff);
if (sh->sh_type != SHT_SYMTAB)
continue;
if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
continue;
if (sh->sh_offset > filesize)
continue;
if (sh->sh_size > (filesize - sh->sh_offset))
continue;
if (sh->sh_entsize == 0)
continue;
if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
if (symtab != NULL)
*symtab = (Elf_Sym *)(p + sh->sh_offset);
if (nsymb != NULL)
*nsymb = (sh->sh_size / sh->sh_entsize);
symsh = sh;
break;
}
}
if (symsh == NULL || (symsh->sh_link >= eh->e_shnum))
return -1;
shoff = eh->e_shoff + symsh->sh_link * eh->e_shentsize;
if (shoff > (filesize - sizeof(*sh)))
return -1;
sh = (Elf_Shdr *)(p + shoff);
if ((sh->sh_offset + sh->sh_size) > filesize)
return -1;
if (strtab != NULL)
*strtab = p + sh->sh_offset;
if (strtabsz != NULL)
*strtabsz = sh->sh_size;
return i;
}
ssize_t
elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab,
size_t shstabsz, const char **psdata, size_t *pssz)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
Elf_Shdr *sh;
char *sdata = NULL;
size_t snlen, shoff, ssz = 0;
ssize_t sidx, i;
snlen = strlen(sname);
if (snlen == 0)
return -1;
/* Find the given section. */
for (i = 0; i < eh->e_shnum; i++) {
shoff = eh->e_shoff + i * eh->e_shentsize;
if (shoff > (filesize - sizeof(*sh)))
continue;
sh = (Elf_Shdr *)(p + shoff);
if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
continue;
if (sh->sh_offset > filesize)
continue;
if (sh->sh_size > (filesize - sh->sh_offset))
continue;
if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
sidx = i;
sdata = p + sh->sh_offset;
ssz = sh->sh_size;
elf_reloc_apply(p, filesize, shstab, shstabsz, sidx,
sdata, ssz);
break;
}
}
if (sdata == NULL)
return -1;
if (psdata != NULL)
*psdata = sdata;
if (pssz != NULL)
*pssz = ssz;
return sidx;
}
static int
elf_reloc_size(unsigned long type)
{
switch (type) {
#ifdef R_X86_64_64
case R_X86_64_64:
return sizeof(uint64_t);
#endif
#ifdef R_X86_64_32
case R_X86_64_32:
return sizeof(uint32_t);
#endif
#ifdef RELOC_32
case RELOC_32:
return sizeof(uint32_t);
#endif
default:
break;
}
return -1;
}
#define ELF_WRITE_RELOC(buf, val, rsize) \
do { \
if (rsize == 4) { \
uint32_t v32 = val; \
memcpy(buf, &v32, sizeof(v32)); \
} else { \
uint64_t v64 = val; \
memcpy(buf, &v64, sizeof(v64)); \
} \
} while (0)
static void
elf_reloc_apply(const char *p, size_t filesize, const char *shstab,
size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz)
{
Elf_Ehdr *eh = (Elf_Ehdr *)p;
Elf_Shdr *sh;
Elf_Rel *rel = NULL;
Elf_RelA *rela = NULL;
const Elf_Sym *symtab, *sym;
ssize_t symtabidx;
size_t nsymb, rsym, rtyp, roff;
size_t shoff, i, j;
uint64_t value;
int rsize;
/* Find symbol table location and number of symbols. */
symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab,
&nsymb, NULL, NULL);
if (symtabidx == -1) {
warnx("symbol table not found");
return;
}
/* Apply possible relocation. */
for (i = 0; i < eh->e_shnum; i++) {
shoff = eh->e_shoff + i * eh->e_shentsize;
if (shoff > (filesize - sizeof(*sh)))
continue;
sh = (Elf_Shdr *)(p + shoff);
if (sh->sh_size == 0)
continue;
if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
continue;
if (sh->sh_offset > filesize)
continue;
if (sh->sh_size > (filesize - sh->sh_offset))
continue;
switch (sh->sh_type) {
case SHT_RELA:
rela = (Elf_RelA *)(p + sh->sh_offset);
for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
rsym = ELF_R_SYM(rela[j].r_info);
rtyp = ELF_R_TYPE(rela[j].r_info);
roff = rela[j].r_offset;
if (rsym >= nsymb)
continue;
if (roff >= filesize)
continue;
sym = &symtab[rsym];
value = sym->st_value + rela[j].r_addend;
rsize = elf_reloc_size(rtyp);
if (rsize == -1 || roff + rsize >= ssz)
continue;
ELF_WRITE_RELOC(sdata + roff, value, rsize);
}
break;
case SHT_REL:
rel = (Elf_Rel *)(p + sh->sh_offset);
for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
rsym = ELF_R_SYM(rel[j].r_info);
rtyp = ELF_R_TYPE(rel[j].r_info);
roff = rel[j].r_offset;
if (rsym >= nsymb)
continue;
if (roff >= filesize)
continue;
sym = &symtab[rsym];
value = sym->st_value;
rsize = elf_reloc_size(rtyp);
if (rsize == -1 || roff + rsize >= ssz)
continue;
ELF_WRITE_RELOC(sdata + roff, value, rsize);
}
break;
default:
continue;
}
}
}