aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2017-08-31 21:22:18 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2017-08-31 21:22:18 +0000
commita0c1f53ee7e8a3cd69053ffdedd3c60c2ee43705 (patch)
treebe13e6291e87fb98cfee2d868b469148cc45930c
downloadsrc-vendor/ctfdump.tar.gz
src-vendor/ctfdump.zip
Import ctfdump from OpenBSD (ISC license)vendor/ctfdump/20170831vendor/ctfdump
For now import it from my own github repository which is a fork of mpi@OpenBSD's version for portability issues. This will be switched to mpi's version once portability issues will have been addressed Discussed with: mpi@ Obtained from: OpenBSD
Notes
Notes: svn path=/vendor/ctfdump/dist/; revision=323071 svn path=/vendor/ctfdump/20170831/; revision=323072; tag=vendor/ctfdump/20170831
-rw-r--r--Makefile11
-rw-r--r--ctfdump.153
-rw-r--r--ctfdump.c638
-rw-r--r--elf.c287
4 files changed, 989 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000000..f051dd7cdd38
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+
+PROG= ctfdump
+SRCS= ctfdump.c elf.c
+
+CFLAGS+= -W -Wall -Wstrict-prototypes -Wno-unused -Wunused-variable
+
+CFLAGS+= -DZLIB
+LDADD+= -lz
+DPADD+= ${LIBZ}
+
+.include <bsd.prog.mk>
diff --git a/ctfdump.1 b/ctfdump.1
new file mode 100644
index 000000000000..a3d72ab12465
--- /dev/null
+++ b/ctfdump.1
@@ -0,0 +1,53 @@
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate$
+.Dt CTFDUMP 1
+.Os
+.Sh NAME
+.Nm ctfdump
+.Nd display CTF information
+.Sh SYNOPSIS
+.Nm ctfdump
+.Op Fl dfhlst
+file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility display CTF information from the
+.Dv \.SUNW_ctf
+section of an
+.Xr elf 5
+file or from a raw CTF file.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Display the object section.
+.It Fl f
+Display the function section.
+.It Fl h
+Dump the CTF header.
+.It Fl l
+Display the label section.
+.It Fl s
+Display the string table.
+.It Fl t
+Display the type section.
+.El
+.Sh EXIT STATUS
+.Ex -std ctfdump
+.Sh SEE ALSO
+.Xr elf 5
diff --git a/ctfdump.c b/ctfdump.c
new file mode 100644
index 000000000000..767d027b1a44
--- /dev/null
+++ b/ctfdump.c
@@ -0,0 +1,638 @@
+/*
+ * 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/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/elf.h>
+#include <sys/mman.h>
+#include <sys/ctf.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef ZLIB
+#include <zlib.h>
+#endif /* ZLIB */
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#ifndef ELF_STRTAB
+#define ELF_STRTAB ".strtab"
+#endif
+#ifndef ELF_CTF
+#define ELF_CTF ".SUNW_ctf"
+#endif
+
+#define DUMP_OBJECT (1 << 0)
+#define DUMP_FUNCTION (1 << 1)
+#define DUMP_HEADER (1 << 2)
+#define DUMP_LABEL (1 << 3)
+#define DUMP_STRTAB (1 << 4)
+#define DUMP_STATISTIC (1 << 5)
+#define DUMP_TYPE (1 << 6)
+
+int dump(const char *, uint8_t);
+int isctf(const char *, size_t);
+__dead2 void usage(void);
+
+int ctf_dump(const char *, size_t, uint8_t);
+uint32_t ctf_dump_type(struct ctf_header *, const char *, off_t,
+ uint32_t, uint32_t);
+const char *ctf_kind2name(uint16_t);
+const char *ctf_enc2name(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 *, const char *, size_t,
+ const Elf_Sym **, size_t *);
+ssize_t elf_getsection(char *, const char *, const char *,
+ size_t, const char **, size_t *);
+
+char *decompress(const char *, size_t, size_t);
+
+int
+main(int argc, char *argv[])
+{
+ const char *filename;
+ uint8_t flags = 0;
+ int ch, error = 0;
+
+ setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
+ switch (ch) {
+ case 'd':
+ flags |= DUMP_OBJECT;
+ break;
+ case 'f':
+ flags |= DUMP_FUNCTION;
+ break;
+ case 'h':
+ flags |= DUMP_HEADER;
+ break;
+ case 'l':
+ flags |= DUMP_LABEL;
+ break;
+ case 's':
+ flags |= DUMP_STRTAB;
+ break;
+ case 't':
+ flags |= DUMP_TYPE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0)
+ usage();
+
+ /* Dump everything by default */
+ if (flags == 0)
+ flags = 0xff;
+
+ while ((filename = *argv++) != NULL)
+ error |= dump(filename, flags);
+
+ return error;
+}
+
+int
+dump(const char *path, uint8_t flags)
+{
+ struct stat st;
+ int fd, error = 1;
+ char *p;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ warn("open");
+ return 1;
+ }
+ if (fstat(fd, &st) == -1) {
+ warn("fstat");
+ return 1;
+ }
+ if ((uintmax_t)st.st_size > SIZE_MAX) {
+ warnx("file too big to fit memory");
+ return 1;
+ }
+
+ 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)) {
+ error = ctf_dump(p, st.st_size, flags);
+ }
+
+ munmap(p, st.st_size);
+ 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;
+ size_t i;
+
+ for (i = *idx + 1; i < nsymb; i++) {
+ st = &symtab[i];
+
+ if (ELF_ST_TYPE(st->st_info) != type)
+ continue;
+
+ *idx = i;
+ return strtab + st->st_name;
+ }
+
+ return NULL;
+}
+
+int
+elf_dump(char *p, size_t filesize, uint8_t flags)
+{
+ Elf_Ehdr *eh = (Elf_Ehdr *)p;
+ Elf_Shdr *sh;
+ const char *shstab;
+ size_t i, shstabsz;
+
+ /* Find section header string table location and size. */
+ if (elf_getshstab(p, filesize, &shstab, &shstabsz))
+ return 1;
+
+ /* Find symbol table location and number of symbols. */
+ if (elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb) == -1)
+ warnx("symbol table not found");
+
+ /* Find string table location and size. */
+ if (elf_getsection(p, ELF_STRTAB, shstab, shstabsz, &strtab,
+ &strtabsz) == -1)
+ warnx("string 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 (!isctf(p + sh->sh_offset, sh->sh_size))
+ break;
+
+ return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
+ }
+
+ warnx("%s section not found", ELF_CTF);
+ return 1;
+}
+
+int
+isctf(const char *p, size_t filesize)
+{
+ struct ctf_header *cth = (struct ctf_header *)p;
+ size_t dlen;
+
+ if (filesize < sizeof(struct ctf_header)) {
+ warnx("file too small to be CTF");
+ return 0;
+ }
+
+ if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
+ return 0;
+
+ dlen = cth->cth_stroff + cth->cth_strlen;
+ if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
+ warnx("bogus file size");
+ return 0;
+ }
+
+ if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
+ (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
+ warnx("wrongly aligned offset");
+ return 0;
+ }
+
+ if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
+ (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
+ warnx("truncated file");
+ return 0;
+ }
+
+ if ((cth->cth_lbloff > cth->cth_objtoff) ||
+ (cth->cth_objtoff > cth->cth_funcoff) ||
+ (cth->cth_funcoff > cth->cth_typeoff) ||
+ (cth->cth_typeoff > cth->cth_stroff)) {
+ warnx("corrupted file");
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ctf_dump(const char *p, size_t size, uint8_t flags)
+{
+ struct ctf_header *cth = (struct ctf_header *)p;
+ off_t dlen = cth->cth_stroff + cth->cth_strlen;
+ char *data;
+
+ if (cth->cth_flags & CTF_F_COMPRESS) {
+ data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
+ if (data == NULL)
+ return 1;
+ } else {
+ data = (char *)p + sizeof(*cth);
+ }
+
+ if (flags & DUMP_HEADER) {
+ printf(" cth_magic = 0x%04x\n", cth->cth_magic);
+ printf(" cth_version = %d\n", cth->cth_version);
+ printf(" cth_flags = 0x%02x\n", cth->cth_flags);
+ printf(" cth_parlabel = %s\n",
+ ctf_off2name(cth, data, dlen, cth->cth_parname));
+ printf(" cth_parname = %s\n",
+ ctf_off2name(cth, data, dlen, cth->cth_parname));
+ printf(" cth_lbloff = %d\n", cth->cth_lbloff);
+ printf(" cth_objtoff = %d\n", cth->cth_objtoff);
+ printf(" cth_funcoff = %d\n", cth->cth_funcoff);
+ printf(" cth_typeoff = %d\n", cth->cth_typeoff);
+ printf(" cth_stroff = %d\n", cth->cth_stroff);
+ printf(" cth_strlen = %d\n", cth->cth_strlen);
+ printf("\n");
+ }
+
+ if (flags & DUMP_LABEL) {
+ uint32_t lbloff = cth->cth_lbloff;
+ struct ctf_lblent *ctl;
+
+ while (lbloff < cth->cth_objtoff) {
+ ctl = (struct ctf_lblent *)(data + lbloff);
+
+ printf(" %5u %s\n", ctl->ctl_typeidx,
+ ctf_off2name(cth, data, dlen, ctl->ctl_label));
+
+ lbloff += sizeof(*ctl);
+ }
+ printf("\n");
+ }
+
+ if (flags & DUMP_OBJECT) {
+ uint32_t objtoff = cth->cth_objtoff;
+ size_t idx = 0, i = 0;
+ uint16_t *dsp;
+ const char *s;
+ int l;
+
+ while (objtoff < cth->cth_funcoff) {
+ dsp = (uint16_t *)(data + objtoff);
+
+ l = printf(" [%zu] %u", i++, *dsp);
+ if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
+ printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
+ else
+ printf("\n");
+
+ objtoff += sizeof(*dsp);
+ }
+ printf("\n");
+ }
+
+ if (flags & DUMP_FUNCTION) {
+ uint16_t *fsp, kind, vlen;
+ size_t idx = 0, i = -1;
+ const char *s;
+ int l;
+
+ fsp = (uint16_t *)(data + cth->cth_funcoff);
+ while (fsp < (uint16_t *)(data + cth->cth_typeoff)) {
+ kind = CTF_INFO_KIND(*fsp);
+ vlen = CTF_INFO_VLEN(*fsp);
+ s = elf_idx2sym(&idx, STT_FUNC);
+ fsp++;
+ i++;
+
+ if (kind == CTF_K_UNKNOWN && vlen == 0)
+ continue;
+
+ l = printf(" [%zu] FUNC ", i);
+ if (s != NULL)
+ printf("(%s)", s);
+ printf(" returns: %u args: (", *fsp++);
+ while (vlen-- > 0)
+ printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
+ printf(")\n");
+ }
+ printf("\n");
+ }
+
+ if (flags & DUMP_TYPE) {
+ uint32_t idx = 1, offset = cth->cth_typeoff;
+
+ while (offset < cth->cth_stroff) {
+ offset += ctf_dump_type(cth, data, dlen, offset, idx++);
+ }
+ printf("\n");
+ }
+
+ if (flags & DUMP_STRTAB) {
+ uint32_t offset = 0;
+ const char *str;
+
+ while (offset < cth->cth_strlen) {
+ str = ctf_off2name(cth, data, dlen, offset);
+
+ printf(" [%u] ", offset);
+ if (strcmp(str, "(anon)"))
+ offset += printf("%s\n", str);
+ else {
+ printf("\\0\n");
+ offset++;
+ }
+ }
+ printf("\n");
+ }
+
+ if (cth->cth_flags & CTF_F_COMPRESS)
+ free(data);
+
+ return 0;
+}
+
+uint32_t
+ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
+ uint32_t offset, uint32_t idx)
+{
+ const char *p = data + offset;
+ const struct ctf_type *ctt = (struct ctf_type *)p;
+ const struct ctf_array *cta;
+ uint16_t *argp, i, kind, vlen, root;
+ uint32_t eob, toff;
+ uint64_t size;
+ const char *name, *kname;
+
+ kind = CTF_INFO_KIND(ctt->ctt_info);
+ vlen = CTF_INFO_VLEN(ctt->ctt_info);
+ root = CTF_INFO_ISROOT(ctt->ctt_info);
+ name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
+
+ if (root)
+ printf(" <%u> ", idx);
+ else
+ printf(" [%u] ", idx);
+
+ if ((kname = ctf_kind2name(kind)) != NULL)
+ printf("%s %s", kname, name);
+
+ if (ctt->ctt_size <= CTF_MAX_SIZE) {
+ size = ctt->ctt_size;
+ toff = sizeof(struct ctf_stype);
+ } else {
+ size = CTF_TYPE_LSIZE(ctt);
+ toff = sizeof(struct ctf_type);
+ }
+
+ switch (kind) {
+ case CTF_K_UNKNOWN:
+ case CTF_K_FORWARD:
+ break;
+ case CTF_K_INTEGER:
+ eob = *((uint32_t *)(p + toff));
+ toff += sizeof(uint32_t);
+ printf(" encoding=%s offset=%u bits=%u",
+ ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
+ CTF_INT_BITS(eob));
+ break;
+ case CTF_K_FLOAT:
+ eob = *((uint32_t *)(p + toff));
+ toff += sizeof(uint32_t);
+ printf(" encoding=0x%x offset=%u bits=%u",
+ CTF_FP_ENCODING(eob), CTF_FP_OFFSET(eob), CTF_FP_BITS(eob));
+ break;
+ case CTF_K_ARRAY:
+ cta = (struct ctf_array *)(p + toff);
+ printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
+ cta->cta_index, cta->cta_nelems);
+ toff += sizeof(struct ctf_array);
+ break;
+ case CTF_K_FUNCTION:
+ argp = (uint16_t *)(p + toff);
+ printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
+ for (i = 1; i < vlen; i++) {
+ argp++;
+ printf(", %u", *argp);
+ }
+ printf(")");
+ toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ printf(" (%lu bytes)\n", size);
+
+ if (size < CTF_LSTRUCT_THRESH) {
+ for (i = 0; i < vlen; i++) {
+ struct ctf_member *ctm;
+
+ ctm = (struct ctf_member *)(p + toff);
+ toff += sizeof(struct ctf_member);
+
+ printf("\t%s type=%u off=%u\n",
+ ctf_off2name(cth, data, dlen,
+ ctm->ctm_name),
+ ctm->ctm_type, ctm->ctm_offset);
+ }
+ } else {
+ for (i = 0; i < vlen; i++) {
+ struct ctf_lmember *ctlm;
+
+ ctlm = (struct ctf_lmember *)(p + toff);
+ toff += sizeof(struct ctf_lmember);
+
+ printf("\t%s type=%u off=%zu\n",
+ ctf_off2name(cth, data, dlen,
+ ctlm->ctlm_name),
+ ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
+ }
+ }
+ break;
+ case CTF_K_ENUM:
+ printf("\n");
+ for (i = 0; i < vlen; i++) {
+ struct ctf_enum *cte;
+
+ cte = (struct ctf_enum *)(p + toff);
+ toff += sizeof(struct ctf_enum);
+
+ printf("\t%s = %d\n",
+ ctf_off2name(cth, data, dlen, cte->cte_name),
+ cte->cte_value);
+ }
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ printf(" refers to %u", ctt->ctt_type);
+ break;
+ default:
+ errx(1, "incorrect type %u at offset %u", kind, offset);
+ }
+
+ printf("\n");
+
+ return toff;
+}
+
+const char *
+ctf_kind2name(uint16_t kind)
+{
+ static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
+ "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
+ "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
+
+ if (kind >= nitems(kind_name))
+ return NULL;
+
+ return kind_name[kind];
+}
+
+const char *
+ctf_enc2name(uint16_t enc)
+{
+ static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
+ "BOOL", "SIGNED BOOL" };
+ static char invalid[7];
+
+ if (enc == CTF_INT_VARARGS)
+ return "VARARGS";
+
+ if (enc > 0 && enc < nitems(enc_name))
+ return enc_name[enc - 1];
+
+ snprintf(invalid, sizeof(invalid), "0x%x", enc);
+ return invalid;
+}
+
+const char *
+ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
+ uint32_t offset)
+{
+ const char *name;
+
+ if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
+ return "external";
+
+ if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
+ return "exceeds strlab";
+
+ if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
+ return "invalid";
+
+ name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
+ if (*name == '\0')
+ return "(anon)";
+
+ return name;
+}
+
+char *
+decompress(const char *buf, size_t size, size_t len)
+{
+#ifdef ZLIB
+ z_stream stream;
+ char *data;
+ int error;
+
+ data = malloc(len);
+ if (data == NULL) {
+ warn(NULL);
+ return NULL;
+ }
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_in = (void *)buf;
+ stream.avail_in = size;
+ stream.next_out = (uint8_t *)data;
+ stream.avail_out = len;
+
+ if ((error = inflateInit(&stream)) != Z_OK) {
+ warnx("zlib inflateInit failed: %s", zError(error));
+ goto exit;
+ }
+
+ if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
+ warnx("zlib inflate failed: %s", zError(error));
+ inflateEnd(&stream);
+ goto exit;
+ }
+
+ if ((error = inflateEnd(&stream)) != Z_OK) {
+ warnx("zlib inflateEnd failed: %s", zError(error));
+ goto exit;
+ }
+
+ if (stream.total_out != len) {
+ warnx("decompression failed: %zu != %zu",
+ stream.total_out, len);
+ goto exit;
+ }
+
+ return data;
+
+exit:
+ free(data);
+#endif /* ZLIB */
+ return NULL;
+}
+
+__dead2 void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
+ getprogname());
+ exit(1);
+}
diff --git a/elf.c b/elf.c
new file mode 100644
index 000000000000..d6b84f08f746
--- /dev/null
+++ b/elf.c
@@ -0,0 +1,287 @@
+/*
+ * 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/param.h>
+#include <sys/elf.h>
+
+#include <machine/reloc.h>
+
+#include <assert.h>
+#include <err.h>
+#include <string.h>
+
+#define ELF_SYMTAB ".symtab"
+#define Elf_RelA __CONCAT(__CONCAT(Elf,__ELF_WORD_SIZE),_Rela)
+
+static int elf_reloc_size(unsigned long);
+static void elf_reloc_apply(const char *, 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] != ELF_CLASS) {
+ 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] > ELFDATA2MSB) {
+ warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
+ return 0;
+ }
+ if (eh->e_shoff > filesize) {
+ warnx("bogus section table offset 0x%lx", (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;
+
+ sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
+ 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, const char *shstab, size_t shstabsz,
+ const Elf_Sym **symtab, size_t *nsymb)
+{
+ Elf_Ehdr *eh = (Elf_Ehdr *)p;
+ Elf_Shdr *sh;
+ size_t snlen;
+ ssize_t i;
+
+ snlen = strlen(ELF_SYMTAB);
+
+ for (i = 0; i < eh->e_shnum; i++) {
+ sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
+
+ if (sh->sh_type != SHT_SYMTAB)
+ continue;
+
+ if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
+ 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);
+
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+ssize_t
+elf_getsection(char *p, 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, 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++) {
+ 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, sname, snlen) == 0) {
+ sidx = i;
+ sdata = p + sh->sh_offset;
+ ssz = sh->sh_size;
+ elf_reloc_apply(p, 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, 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 i, j;
+ uint64_t value;
+ int rsize;
+
+ /* Find symbol table location and number of symbols. */
+ symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb);
+ if (symtabidx == -1) {
+ warnx("symbol table not found");
+ return;
+ }
+
+ /* Apply possible relocation. */
+ for (i = 0; i < eh->e_shnum; i++) {
+ sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
+
+ if (sh->sh_size == 0)
+ continue;
+
+ if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
+ 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;
+ 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;
+ 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;
+ }
+ }
+}