summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2017-08-11 14:21:25 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2017-08-11 14:21:25 +0000
commit0f635dd347f73e5f59e4a251b28bc5cc9b42cb00 (patch)
treeace885b812c0db57a2e2aeb02915ca915cfb4db4
parenta36ce483bfa196e35c97bf7a746d9bae9f4e7b11 (diff)
Import a tool for generating CTF data section (SUNW_ctf) based on DWARF
information. ctfconv(1) support multiple CUs in order to work on binaries. ctfstrip(1) works like strip(1) but also insert a .SUNW_ctf section inside a binary. ok deraadt@, kettenis@, jasper@
-rw-r--r--usr.bin/ctfconv/Makefile19
-rw-r--r--usr.bin/ctfconv/ctfconv.157
-rw-r--r--usr.bin/ctfconv/ctfconv.c452
-rwxr-xr-xusr.bin/ctfconv/ctfstrip34
-rw-r--r--usr.bin/ctfconv/ctfstrip.151
-rw-r--r--usr.bin/ctfconv/dw.c662
-rw-r--r--usr.bin/ctfconv/dw.h102
-rw-r--r--usr.bin/ctfconv/dwarf.h972
-rw-r--r--usr.bin/ctfconv/elf.c284
-rw-r--r--usr.bin/ctfconv/generate.c461
-rw-r--r--usr.bin/ctfconv/hash.c242
-rw-r--r--usr.bin/ctfconv/hash.h30
-rw-r--r--usr.bin/ctfconv/itype.h102
-rw-r--r--usr.bin/ctfconv/parse.c1308
-rw-r--r--usr.bin/ctfconv/pool.c103
-rw-r--r--usr.bin/ctfconv/pool.h66
-rw-r--r--usr.bin/ctfconv/xmalloc.c82
-rw-r--r--usr.bin/ctfconv/xmalloc.h28
18 files changed, 5055 insertions, 0 deletions
diff --git a/usr.bin/ctfconv/Makefile b/usr.bin/ctfconv/Makefile
new file mode 100644
index 00000000000..41aae50dcea
--- /dev/null
+++ b/usr.bin/ctfconv/Makefile
@@ -0,0 +1,19 @@
+
+PROG= ctfconv
+SRCS= ctfconv.c parse.c elf.c dw.c generate.c hash.c xmalloc.c \
+ pool.c
+
+CFLAGS+= -W -Wall -Wstrict-prototypes -Wno-unused -Wunused-variable \
+ -Wno-unused-parameter
+
+CFLAGS+= -DZLIB
+LDADD+= -lz
+DPADD+= ${LIBZ}
+
+MAN= ctfconv.1 ctfstrip.1
+
+afterinstall:
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/ctfstrip ${DESTDIR}${BINDIR}/ctfstrip
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ctfconv/ctfconv.1 b/usr.bin/ctfconv/ctfconv.1
new file mode 100644
index 00000000000..d1ceed6451d
--- /dev/null
+++ b/usr.bin/ctfconv/ctfconv.1
@@ -0,0 +1,57 @@
+.\"
+.\" 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: August 11 2017 $
+.Dt CTFCONV 1
+.Os
+.Sh NAME
+.Nm ctfconv
+.Nd generates a raw CTF section from debug data
+.Sh SYNOPSIS
+.Nm ctfconv
+.Op Fl d
+.Fl l Ar label
+.Fl o Ar outfile
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility parses DWARF debug sections of
+.Ar file
+to generate
+.Dv CTF
+data.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Display types as if they would be dumped from a
+.Dv \.SUNW_ctf
+section by
+.Xr ctfdump 1 .
+.It Fl l Ar label
+Set the
+.Dv CTF
+label as
+.Ar label .
+.It Fl o Ar outfile
+Write the raw section in
+.Ar outfile .
+.El
+.Sh EXIT STATUS
+.Ex -std ctfconv
+.Sh SEE ALSO
+.Xr ctfdump 1 ,
+.Xr ctfstrip 1
diff --git a/usr.bin/ctfconv/ctfconv.c b/usr.bin/ctfconv/ctfconv.c
new file mode 100644
index 00000000000..c793698d6d1
--- /dev/null
+++ b/usr.bin/ctfconv/ctfconv.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2016-2017 Martin Pieuchot
+ *
+ * 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/exec_elf.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/ctf.h>
+
+#include <assert.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>
+
+#include "itype.h"
+#include "xmalloc.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#define DEBUG_ABBREV ".debug_abbrev"
+#define DEBUG_INFO ".debug_info"
+#define DEBUG_LINE ".debug_line"
+#define DEBUG_STR ".debug_str"
+
+__dead void usage(void);
+int convert(const char *);
+int generate(const char *, const char *, int);
+int elf_convert(char *, size_t);
+void elf_sort(void);
+void dump_type(struct itype *);
+void dump_func(struct itype *, int *);
+void dump_obj(struct itype *, int *);
+
+/* 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 *);
+
+/* parse.c */
+void dwarf_parse(const char *, size_t, const char *, size_t);
+
+const char *ctf_enc2name(unsigned short);
+
+/* lists of parsed types and functions */
+struct itype_queue itypeq = TAILQ_HEAD_INITIALIZER(itypeq);
+struct itype_queue ifuncq = TAILQ_HEAD_INITIALIZER(ifuncq);
+struct itype_queue iobjq = TAILQ_HEAD_INITIALIZER(iobjq);
+
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-d] -l label -o outfile file\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *filename, *label = NULL, *outfile = NULL;
+ int dump = 0;
+ int ch, error = 0;
+ struct itype *it;
+
+ setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "dl:o:")) != -1) {
+ switch (ch) {
+ case 'd':
+ dump = 1; /* ctfdump(1) like SUNW_ctf sections */
+ break;
+ case 'l':
+ if (label != NULL)
+ usage();
+ label = optarg;
+ break;
+ case 'o':
+ if (outfile != NULL)
+ usage();
+ outfile = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (!dump && (outfile == NULL || label == NULL))
+ usage();
+
+ filename = *argv;
+ error = convert(filename);
+ if (error != 0)
+ return error;
+
+ if (dump) {
+ int fidx = -1, oidx = -1;
+
+ TAILQ_FOREACH(it, &iobjq, it_symb)
+ dump_obj(it, &oidx);
+ printf("\n");
+
+ TAILQ_FOREACH(it, &ifuncq, it_symb)
+ dump_func(it, &fidx);
+ printf("\n");
+
+ TAILQ_FOREACH(it, &itypeq, it_next) {
+ if (it->it_flags & (ITF_FUNC|ITF_OBJ))
+ continue;
+
+ dump_type(it);
+ }
+ }
+
+ if (outfile != NULL) {
+ error = generate(outfile, label, 1);
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+convert(const char *path)
+{
+ struct stat st;
+ int fd, error = 1;
+ char *p;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ warn("open %s", path);
+ return 1;
+ }
+ if (fstat(fd, &st) == -1) {
+ warn("fstat %s", path);
+ 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|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (p == MAP_FAILED)
+ err(1, "mmap");
+
+ if (iself(p, st.st_size))
+ error = elf_convert(p, st.st_size);
+
+ munmap(p, st.st_size);
+ close(fd);
+
+ return error;
+}
+
+const char *dstrbuf;
+size_t dstrlen;
+const char *strtab;
+const Elf_Sym *symtab;
+size_t strtabsz, nsymb;
+
+int
+elf_convert(char *p, size_t filesize)
+{
+ const char *shstab;
+ const char *infobuf, *abbuf;
+ size_t infolen, ablen;
+ size_t 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 abbreviation location and size. */
+ if (elf_getsection(p, DEBUG_ABBREV, shstab, shstabsz, &abbuf,
+ &ablen) == -1) {
+ warnx("%s section not found", DEBUG_ABBREV);
+ return 1;
+ }
+
+ if (elf_getsection(p, DEBUG_INFO, shstab, shstabsz, &infobuf,
+ &infolen) == -1) {
+ warnx("%s section not found", DEBUG_INFO);
+ return 1;
+ }
+
+ /* Find string table location and size. */
+ if (elf_getsection(p, DEBUG_STR, shstab, shstabsz, &dstrbuf,
+ &dstrlen) == -1)
+ warnx("%s section not found", DEBUG_STR);
+
+ dwarf_parse(infobuf, infolen, abbuf, ablen);
+
+ /* Sort functions */
+ elf_sort();
+
+ return 0;
+}
+
+void
+elf_sort(void)
+{
+ struct itype *it, tmp;
+ size_t i;
+
+ memset(&tmp, 0, sizeof(tmp));
+ for (i = 0; i < nsymb; i++) {
+ const Elf_Sym *st = &symtab[i];
+ char *sname;
+
+ if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON)
+ continue;
+
+ switch (ELF_ST_TYPE(st->st_info)) {
+ case STT_FUNC:
+ tmp.it_flags = ITF_FUNC;
+ break;
+ case STT_OBJECT:
+ tmp.it_flags = ITF_OBJ;
+ break;
+ default:
+ continue;
+ }
+
+ /*
+ * Skip local suffix
+ *
+ * FIXME: only skip local copies.
+ */
+ sname = xstrdup(strtab + st->st_name);
+ strlcpy(tmp.it_name, strtok(sname, "."), ITNAME_MAX);
+ it = RB_FIND(isymb_tree, &isymbt, &tmp);
+ strlcpy(tmp.it_name, (strtab + st->st_name), ITNAME_MAX);
+ free(sname);
+
+ if (it == NULL) {
+ /* Insert 'unknown' entry to match symbol order. */
+ it = it_dup(&tmp);
+ it->it_refp = it;
+#ifdef DEBUG
+ warnx("symbol not found: %s", it_name(it));
+#endif
+ }
+
+ if (it->it_flags & ITF_INSERTED) {
+#ifdef DEBUG
+ warnx("%s: already inserted", it_name(it));
+#endif
+ it = it_dup(it);
+ }
+
+ /* Save symbol index for dump. */
+ it->it_ref = i;
+
+ it->it_flags |= ITF_INSERTED;
+ if (it->it_flags & ITF_FUNC)
+ TAILQ_INSERT_TAIL(&ifuncq, it, it_symb);
+ else
+ TAILQ_INSERT_TAIL(&iobjq, it, it_symb);
+ }
+}
+
+const char *
+type_name(struct itype *it)
+{
+ const char *name;
+
+ name = it_name(it);
+ if (name == NULL)
+ return "(anon)";
+
+ return name;
+}
+
+/* Display parsed types a la ctfdump(1) */
+void
+dump_type(struct itype *it)
+{
+ struct imember *im;
+
+#ifdef DEBUG
+ switch (it->it_type) {
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ case CTF_K_ARRAY:
+ case CTF_K_FUNCTION:
+ if (it->it_refp == NULL) {
+ printf("unresolved: %s type=%d\n", it_name(it),
+ it->it_type);
+ return;
+ }
+ default:
+ break;
+ }
+#endif
+
+ switch (it->it_type) {
+ case CTF_K_FLOAT:
+ case CTF_K_INTEGER:
+ printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n",
+ it->it_idx,
+ (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT",
+ it_name(it), ctf_enc2name(it->it_enc), it->it_size);
+ break;
+ case CTF_K_POINTER:
+ printf(" <%u> POINTER %s refers to %u\n", it->it_idx,
+ type_name(it), it->it_refp->it_idx);
+ break;
+ case CTF_K_TYPEDEF:
+ printf(" <%u> TYPEDEF %s refers to %u\n",
+ it->it_idx, it_name(it), it->it_refp->it_idx);
+ break;
+ case CTF_K_VOLATILE:
+ printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx,
+ type_name(it), it->it_refp->it_idx);
+ break;
+ case CTF_K_CONST:
+ printf(" <%u> CONST %s refers to %u\n", it->it_idx,
+ type_name(it), it->it_refp->it_idx);
+ break;
+ case CTF_K_RESTRICT:
+ printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx,
+ it_name(it), it->it_refp->it_idx);
+ break;
+ case CTF_K_ARRAY:
+ printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n",
+ it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx,
+ it->it_nelems);
+ printf("\n");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ printf(" [%u] %s %s (%u bytes)\n", it->it_idx,
+ (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION",
+ type_name(it), it->it_size);
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ printf("\t%s type=%u off=%zd\n",
+ (im->im_flags & ITM_ANON) ? "unknown" : im->im_name,
+ im->im_refp->it_idx, im->im_off);
+ }
+ printf("\n");
+ break;
+ case CTF_K_ENUM:
+ printf(" [%u] ENUM %s\n\n", it->it_idx, type_name(it));
+ break;
+ case CTF_K_FUNCTION:
+ printf(" [%u] FUNCTION (%s) returns: %u args: (",
+ it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon",
+ it->it_refp->it_idx);
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ printf("%u%s", im->im_refp->it_idx,
+ TAILQ_NEXT(im, im_next) ? ", " : "");
+ }
+ printf(")\n");
+ break;
+ default:
+ assert(0 == 1);
+ }
+}
+
+void
+dump_func(struct itype *it, int *idx)
+{
+ struct imember *im;
+
+ (*idx)++;
+
+ if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0)
+ return;
+
+ printf(" [%u] FUNC (%s) returns: %u args: (", (*idx),
+ (it_name(it) != NULL) ? it_name(it) : "unknown",
+ it->it_refp->it_idx);
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ printf("%u%s", im->im_refp->it_idx,
+ TAILQ_NEXT(im, im_next) ? ", " : "");
+ }
+ printf(")\n");
+}
+
+void
+dump_obj(struct itype *it, int *idx)
+{
+ int l;
+
+ (*idx)++;
+
+ l = printf(" [%u] %u", (*idx), it->it_refp->it_idx);
+ printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref);
+}
+
+const char *
+ctf_enc2name(unsigned short 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;
+}
diff --git a/usr.bin/ctfconv/ctfstrip b/usr.bin/ctfconv/ctfstrip
new file mode 100755
index 00000000000..2966b9c08ac
--- /dev/null
+++ b/usr.bin/ctfconv/ctfstrip
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Turn off Strict Bourne shell mode.
+set +o sh
+
+USAGE="usage: ${0##*/} [-S] [-o outfile] file"
+
+while getopts "o:S" opt; do
+ case $opt in
+ S) STRIPFLAG=-g;;
+ o) OUTFILE="${OPTARG}";;
+ *) print -u2 "${USAGE}"; exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [[ $# == 0 ]]; then
+ print -u2 "${USAGE}";
+ exit 1
+fi
+
+LABEL="unknown"
+TMPFILE=$1.rawctf
+
+# Extract kernel verison
+if [[ "$1" == bsd* ]]; then
+ LABEL=`what $1 |tr -d '\n'|awk -F"$1 " '{ print $2 '\n' }'`
+fi
+
+ctfconv -o ${TMPFILE} -l "${LABEL}" $1 || exit 2
+
+objcopy --add-section .SUNW_ctf=${TMPFILE} ${STRIPFLAG} $1 ${OUTFILE}
+
+rm -f ${TMPFILE}
diff --git a/usr.bin/ctfconv/ctfstrip.1 b/usr.bin/ctfconv/ctfstrip.1
new file mode 100644
index 00000000000..e0a8502e2a8
--- /dev/null
+++ b/usr.bin/ctfconv/ctfstrip.1
@@ -0,0 +1,51 @@
+.\"
+.\" Copyright (c) 2017 Martin Pieuchot
+.\"
+.\" 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: August 11 2017 $
+.Dt CTFSTRIP 1
+.Os
+.Sh NAME
+.Nm ctfstrip
+.Nd insert a CTF section
+.Sh SYNOPSIS
+.Nm ctfstrip
+.Op Fl S
+.Op Fl o Ar outfile
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility generates and inserts a
+.Dv .SUNW_ctf
+section in a
+.Xr elf 5
+file based on the content of its exisiting DWARF debug sections.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl S
+Remove debug sections.
+.It Fl o Ar outfile
+Put the converted output in
+.Ar outfile
+instead of replacing
+.Ar file .
+.El
+.Sh EXIT STATUS
+.Ex -std ctfstrip
+.Sh SEE ALSO
+.Xr ctfconvert 1 ,
+.Xr strip 1 ,
+.Xr elf 5
diff --git a/usr.bin/ctfconv/dw.c b/usr.bin/ctfconv/dw.c
new file mode 100644
index 00000000000..d0340e09780
--- /dev/null
+++ b/usr.bin/ctfconv/dw.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (c) 2016 Martin Pieuchot
+ * Copyright (c) 2014 Matthew Dempsky <matthew@dempsky.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/queue.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dw.h"
+#include "dwarf.h"
+#include "pool.h"
+
+#ifndef NOPOOL
+struct pool dcu_pool, die_pool, dav_pool, dab_pool, dat_pool;
+#endif /* NOPOOL */
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+static int dw_read_u8(struct dwbuf *, uint8_t *);
+static int dw_read_u16(struct dwbuf *, uint16_t *);
+static int dw_read_u32(struct dwbuf *, uint32_t *);
+static int dw_read_u64(struct dwbuf *, uint64_t *);
+
+static int dw_read_sleb128(struct dwbuf *, int64_t *);
+static int dw_read_uleb128(struct dwbuf *, uint64_t *);
+
+static int dw_read_bytes(struct dwbuf *, void *, size_t);
+static int dw_read_string(struct dwbuf *, const char **);
+static int dw_read_buf(struct dwbuf *, struct dwbuf *, size_t);
+
+static int dw_skip_bytes(struct dwbuf *, size_t);
+
+static int dw_read_filename(struct dwbuf *, const char **, const char **,
+ uint8_t, uint64_t);
+
+
+static int dw_attr_parse(struct dwbuf *, struct dwattr *, uint8_t,
+ struct dwaval_queue *);
+static void dw_attr_purge(struct dwaval_queue *);
+static int dw_die_parse(struct dwbuf *, size_t, uint8_t,
+ struct dwabbrev_queue *, struct dwdie_queue *);
+static void dw_die_purge(struct dwdie_queue *);
+
+static int
+dw_read_bytes(struct dwbuf *d, void *v, size_t n)
+{
+ if (d->len < n)
+ return -1;
+ memcpy(v, d->buf, n);
+ d->buf += n;
+ d->len -= n;
+ return 0;
+}
+
+static int
+dw_read_u8(struct dwbuf *d, uint8_t *v)
+{
+ return dw_read_bytes(d, v, sizeof(*v));
+}
+
+static int
+dw_read_u16(struct dwbuf *d, uint16_t *v)
+{
+ return dw_read_bytes(d, v, sizeof(*v));
+}
+
+static int
+dw_read_u32(struct dwbuf *d, uint32_t *v)
+{
+ return dw_read_bytes(d, v, sizeof(*v));
+}
+
+static int
+dw_read_u64(struct dwbuf *d, uint64_t *v)
+{
+ return dw_read_bytes(d, v, sizeof(*v));
+}
+
+/* Read a DWARF LEB128 (little-endian base-128) value. */
+static inline int
+dw_read_leb128(struct dwbuf *d, uint64_t *v, int signextend)
+{
+ unsigned int shift = 0;
+ uint64_t res = 0;
+ uint8_t x;
+
+ while (shift < 64 && !dw_read_u8(d, &x)) {
+ res |= (uint64_t)(x & 0x7f) << shift;
+ shift += 7;
+ if ((x & 0x80) == 0) {
+ if (signextend && shift < 64 && (x & 0x40) != 0)
+ res |= ~(uint64_t)0 << shift;
+ *v = res;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int
+dw_read_sleb128(struct dwbuf *d, int64_t *v)
+{
+ return dw_read_leb128(d, (uint64_t *)v, 1);
+}
+
+static int
+dw_read_uleb128(struct dwbuf *d, uint64_t *v)
+{
+ return dw_read_leb128(d, v, 0);
+}
+
+/* Read a NUL terminated string. */
+static int
+dw_read_string(struct dwbuf *d, const char **s)
+{
+ const char *end = memchr(d->buf, '\0', d->len);
+ size_t n;
+
+ if (end == NULL)
+ return -1;
+
+ n = end - d->buf + 1;
+ *s = d->buf;
+ d->buf += n;
+ d->len -= n;
+ return 0;
+}
+
+static int
+dw_read_buf(struct dwbuf *d, struct dwbuf *v, size_t n)
+{
+ if (d->len < n)
+ return -1;
+ v->buf = d->buf;
+ v->len = n;
+ d->buf += n;
+ d->len -= n;
+ return 0;
+}
+
+static int
+dw_skip_bytes(struct dwbuf *d, size_t n)
+{
+ if (d->len < n)
+ return -1;
+ d->buf += n;
+ d->len -= n;
+ return 0;
+}
+
+static int
+dw_read_filename(struct dwbuf *names, const char **outdirname,
+ const char **outbasename, uint8_t opcode_base, uint64_t file)
+{
+ struct dwbuf dirnames;
+ const char *basename = NULL, *dirname = NULL;
+ uint64_t mtime, size, dummy, dir = 0;
+ const char *name;
+ size_t i;
+
+ if (file == 0)
+ return -1;
+
+ /* Skip over opcode table. */
+ for (i = 1; i < opcode_base; i++) {
+ if (dw_read_uleb128(names, &dummy))
+ return -1;
+ }
+
+ /* Skip over directory name table for now. */
+ dirnames = *names;
+ for (;;) {
+ if (dw_read_string(names, &name))
+ return -1;
+ if (*name == '\0')
+ break;
+ }
+
+ /* Locate file entry. */
+ for (i = 0; i < file; i++) {
+ if (dw_read_string(names, &basename) || *basename == '\0' ||
+ dw_read_uleb128(names, &dir) ||
+ dw_read_uleb128(names, &mtime) ||
+ dw_read_uleb128(names, &size))
+ return -1;
+ }
+
+ for (i = 0; i < dir; i++) {
+ if (!dw_read_string(&dirnames, &dirname) || *dirname == '\0')
+ return -1;
+ }
+
+ *outdirname = dirname;
+ *outbasename = basename;
+
+ return 0;
+}
+
+
+const char *
+dw_tag2name(uint64_t tag)
+{
+ static const char *dw_tags[] = { DW_TAG_NAMES };
+
+ if (tag <= nitems(dw_tags))
+ return dw_tags[tag - 1];
+
+ if (tag == DW_TAG_lo_user)
+ return "DW_TAG_lo_user";
+ if (tag == DW_TAG_hi_user)
+ return "DW_TAG_hi_user";
+
+ return NULL;
+}
+
+const char *
+dw_at2name(uint64_t at)
+{
+ static const char *dw_attrs[] = { DW_AT_NAMES };
+
+ if (at <= nitems(dw_attrs))
+ return dw_attrs[at - 1];
+
+ if (at == DW_AT_lo_user)
+ return "DW_AT_lo_user";
+ if (at == DW_AT_hi_user)
+ return "DW_AT_hi_user";
+
+ return NULL;
+}
+
+const char *
+dw_form2name(uint64_t form)
+{
+ static const char *dw_forms[] = { DW_FORM_NAMES };
+
+ if (form <= nitems(dw_forms))
+ return dw_forms[form - 1];
+
+ if (form == DW_FORM_GNU_ref_alt)
+ return "DW_FORM_GNU_ref_alt";
+ if (form == DW_FORM_GNU_strp_alt)
+ return "DW_FORM_GNU_strp_alt";
+
+ return NULL;
+}
+
+const char *
+dw_op2name(uint8_t op)
+{
+ static const char *dw_ops[] = { DW_OP_NAMES };
+
+ if (op <= nitems(dw_ops))
+ return dw_ops[op - 1];
+
+ if (op == DW_OP_lo_user)
+ return "DW_OP_lo_user";
+ if (op == DW_OP_hi_user)
+ return "DW_OP_hi_user";
+
+ return NULL;
+}
+
+static int
+dw_attr_parse(struct dwbuf *dwbuf, struct dwattr *dat, uint8_t psz,
+ struct dwaval_queue *davq)
+{
+ struct dwaval *dav;
+ uint64_t form = dat->dat_form;
+ int error = 0, i = 0;
+
+ while (form == DW_FORM_indirect) {
+ /* XXX loop prevention not strict enough? */
+ if (dw_read_uleb128(dwbuf, &form) || (++i > 3))
+ return ELOOP;
+ }
+
+ dav = pzalloc(&dav_pool, sizeof(*dav));
+ if (dav == NULL)
+ return ENOMEM;
+
+ dav->dav_dat = dat;
+
+ switch (form) {
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ if (psz == sizeof(uint32_t))
+ error = dw_read_u32(dwbuf, &dav->dav_u32);
+ else
+ error = dw_read_u64(dwbuf, &dav->dav_u64);
+ break;
+ case DW_FORM_block1:
+ error = dw_read_u8(dwbuf, &dav->dav_u8);
+ if (error == 0)
+ error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u8);
+ break;
+ case DW_FORM_block2:
+ error = dw_read_u16(dwbuf, &dav->dav_u16);
+ if (error == 0)
+ error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u16);
+ break;
+ case DW_FORM_block4:
+ error = dw_read_u32(dwbuf, &dav->dav_u32);
+ if (error == 0)
+ error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u32);
+ break;
+ case DW_FORM_block:
+ error = dw_read_uleb128(dwbuf, &dav->dav_u64);
+ if (error == 0)
+ error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u64);
+ break;
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ error = dw_read_u8(dwbuf, &dav->dav_u8);
+ break;
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ error = dw_read_u16(dwbuf, &dav->dav_u16);
+ break;
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ error = dw_read_u32(dwbuf, &dav->dav_u32);
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ error = dw_read_u64(dwbuf, &dav->dav_u64);
+ break;
+ case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ error = dw_read_uleb128(dwbuf, &dav->dav_u64);
+ break;
+ case DW_FORM_sdata:
+ error = dw_read_sleb128(dwbuf, &dav->dav_s64);
+ break;
+ case DW_FORM_string:
+ error = dw_read_string(dwbuf, &dav->dav_str);
+ break;
+ case DW_FORM_strp:
+ error = dw_read_u32(dwbuf, &dav->dav_u32);
+ break;
+ case DW_FORM_flag_present:
+ dav->dav_u8 = 1;
+ break;
+ default:
+ error = ENOENT;
+ break;
+ }
+
+ if (error) {
+ pfree(&dav_pool, dav);
+ return error;
+ }
+
+ SIMPLEQ_INSERT_TAIL(davq, dav, dav_next);
+ return 0;
+}
+
+static void
+dw_attr_purge(struct dwaval_queue *davq)
+{
+ struct dwaval *dav;
+
+ while ((dav = SIMPLEQ_FIRST(davq)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(davq, dav_next);
+ pfree(&dav_pool, dav);
+ }
+
+ SIMPLEQ_INIT(davq);
+}
+
+static int
+dw_die_parse(struct dwbuf *dwbuf, size_t nextoff, uint8_t psz,
+ struct dwabbrev_queue *dabq, struct dwdie_queue *dieq)
+{
+ struct dwdie *die;
+ struct dwabbrev *dab;
+ struct dwattr *dat;
+ uint64_t code;
+ size_t doff;
+ uint8_t lvl = 0;
+ int error;
+
+
+ while (dwbuf->len > 0) {
+ doff = nextoff - dwbuf->len;
+ if (dw_read_uleb128(dwbuf, &code))
+ return -1;
+
+ if (code == 0) {
+ lvl--;
+ continue;
+ }
+
+ SIMPLEQ_FOREACH(dab, dabq, dab_next) {
+ if (dab->dab_code == code)
+ break;
+ }
+ if (dab == NULL)
+ return ESRCH;
+
+ die = pmalloc(&die_pool, sizeof(*die));
+ if (die == NULL)
+ return ENOMEM;
+
+ die->die_lvl = lvl;
+ die->die_dab = dab;
+ die->die_offset = doff;
+ SIMPLEQ_INIT(&die->die_avals);
+
+ SIMPLEQ_FOREACH(dat, &dab->dab_attrs, dat_next) {
+ error = dw_attr_parse(dwbuf, dat, psz, &die->die_avals);
+ if (error != 0) {
+ dw_attr_purge(&die->die_avals);
+ return error;
+ }
+ }
+
+ if (dab->dab_children == DW_CHILDREN_yes)
+ lvl++;
+
+ SIMPLEQ_INSERT_TAIL(dieq, die, die_next);
+ }
+
+ return 0;
+}
+
+static void
+dw_die_purge(struct dwdie_queue *dieq)
+{
+ struct dwdie *die;
+
+ while ((die = SIMPLEQ_FIRST(dieq)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(dieq, die_next);
+ dw_attr_purge(&die->die_avals);
+ pfree(&die_pool, die);
+ }
+
+ SIMPLEQ_INIT(dieq);
+}
+
+int
+dw_ab_parse(struct dwbuf *abseg, struct dwabbrev_queue *dabq)
+{
+ struct dwabbrev *dab;
+ uint64_t code, tag;
+ uint8_t children;
+
+ if (abseg->len == 0)
+ return EINVAL;
+
+ for (;;) {
+ if (dw_read_uleb128(abseg, &code) || (code == 0))
+ break;
+
+ if (dw_read_uleb128(abseg, &tag) ||
+ dw_read_u8(abseg, &children))
+ return -1;
+
+ dab = pmalloc(&dab_pool, sizeof(*dab));
+ if (dab == NULL)
+ return ENOMEM;
+
+ dab->dab_code = code;
+ dab->dab_tag = tag;
+ dab->dab_children = children;
+ SIMPLEQ_INIT(&dab->dab_attrs);
+
+ SIMPLEQ_INSERT_TAIL(dabq, dab, dab_next);
+
+ for (;;) {
+ struct dwattr *dat;
+ uint64_t attr = 0, form = 0;
+
+ if (dw_read_uleb128(abseg, &attr) ||
+ dw_read_uleb128(abseg, &form))
+ return -1;
+
+ if ((attr == 0) && (form == 0))
+ break;
+
+ dat = pmalloc(&dat_pool, sizeof(*dat));
+ if (dat == NULL)
+ return ENOMEM;
+
+ dat->dat_attr = attr;
+ dat->dat_form = form;
+
+ SIMPLEQ_INSERT_TAIL(&dab->dab_attrs, dat, dat_next);
+ }
+ }
+
+ return 0;
+}
+
+void
+dw_dabq_purge(struct dwabbrev_queue *dabq)
+{
+ struct dwabbrev *dab;
+
+ while ((dab = SIMPLEQ_FIRST(dabq)) != NULL) {
+ struct dwattr *dat;
+
+ SIMPLEQ_REMOVE_HEAD(dabq, dab_next);
+ while ((dat = SIMPLEQ_FIRST(&dab->dab_attrs)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&dab->dab_attrs, dat_next);
+ pfree(&dat_pool, dat);
+ }
+
+ pfree(&dab_pool, dab);
+ }
+
+ SIMPLEQ_INIT(dabq);
+}
+
+int
+dw_cu_parse(struct dwbuf *info, struct dwbuf *abbrev, size_t seglen,
+ struct dwcu **dcup)
+{
+ struct dwbuf abseg = *abbrev;
+ struct dwbuf dwbuf;
+ size_t segoff, nextoff, addrsize;
+ struct dwcu *dcu = NULL;
+ uint32_t length = 0, abbroff = 0;
+ uint16_t version;
+ uint8_t psz;
+ int error;
+#ifndef NOPOOL
+ static int dw_pool_inited = 0;
+
+ if (!dw_pool_inited) {
+ pool_init(&dcu_pool, "dcu", 1, sizeof(struct dwcu));
+ pool_init(&dab_pool, "dab", 32, sizeof(struct dwabbrev));
+ pool_init(&dat_pool, "dat", 32, sizeof(struct dwattr));
+ pool_init(&die_pool, "die", 512, sizeof(struct dwdie));
+ pool_init(&dav_pool, "dav", 1024, sizeof(struct dwaval));
+ dw_pool_inited = 1;
+ }
+#endif /* NOPOOL */
+
+ if (info->len == 0 || abbrev->len == 0)
+ return EINVAL;
+
+ /* Offset in the segment of the current Compile Unit. */
+ segoff = seglen - info->len;
+
+ if (dw_read_u32(info, &length))
+ return -1;
+
+ if (length >= 0xfffffff0 || length > info->len)
+ return EOVERFLOW;
+
+ /* Offset of the next Compule Unit. */
+ nextoff = segoff + length + sizeof(uint32_t);
+
+ if (dw_read_buf(info, &dwbuf, length))
+ return -1;
+
+ addrsize = 4; /* XXX */
+
+ if (dw_read_u16(&dwbuf, &version) ||
+ dw_read_bytes(&dwbuf, &abbroff, addrsize) ||
+ dw_read_u8(&dwbuf, &psz))
+ return -1;
+
+ if (dw_skip_bytes(&abseg, abbroff))
+ return -1;
+
+ /* Only DWARF2 until extended. */
+ if (version != 2)
+ return ENOTSUP;
+
+ dcu = pmalloc(&dcu_pool, sizeof(*dcu));
+ if (dcu == NULL)
+ return ENOMEM;
+
+ dcu->dcu_offset = segoff;
+ dcu->dcu_length = length;
+ dcu->dcu_version = version;
+ dcu->dcu_abbroff = abbroff;
+ dcu->dcu_psize = psz;
+ SIMPLEQ_INIT(&dcu->dcu_abbrevs);
+ SIMPLEQ_INIT(&dcu->dcu_dies);
+
+ error = dw_ab_parse(&abseg, &dcu->dcu_abbrevs);
+ if (error != 0) {
+ dw_dcu_free(dcu);
+ return error;
+ }
+
+ error = dw_die_parse(&dwbuf, nextoff, psz, &dcu->dcu_abbrevs,
+ &dcu->dcu_dies);
+ if (error != 0) {
+ dw_dcu_free(dcu);
+ return error;
+ }
+
+ if (dcup != NULL)
+ *dcup = dcu;
+ else
+ dw_dcu_free(dcu);
+
+ return 0;
+}
+
+void
+dw_dcu_free(struct dwcu *dcu)
+{
+ if (dcu == NULL)
+ return;
+
+ dw_die_purge(&dcu->dcu_dies);
+ dw_dabq_purge(&dcu->dcu_abbrevs);
+ pfree(&dcu_pool, dcu);
+}
+
+int
+dw_loc_parse(struct dwbuf *dwbuf, uint8_t *pop, uint64_t *poper1,
+ uint64_t *poper2)
+{
+ uint64_t oper1 = 0, oper2 = 0;
+ uint8_t op;
+
+ if (dw_read_u8(dwbuf, &op))
+ return -1;
+
+ if (pop != NULL)
+ *pop = op;
+
+ switch (op) {
+ case DW_OP_plus_uconst:
+ dw_read_uleb128(dwbuf, &oper1);
+ break;
+ default:
+ return ENOTSUP;
+ }
+
+ if (poper1 != NULL)
+ *poper1 = oper1;
+ if (poper2 != NULL)
+ *poper2 = oper2;
+
+ return 0;
+}
diff --git a/usr.bin/ctfconv/dw.h b/usr.bin/ctfconv/dw.h
new file mode 100644
index 00000000000..eec90f9acf0
--- /dev/null
+++ b/usr.bin/ctfconv/dw.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Martin Pieuchot
+ *
+ * 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.
+ */
+
+#ifndef _DW_H_
+#define _DW_H_
+
+struct dwbuf {
+ const char *buf;
+ size_t len;
+};
+
+struct dwattr {
+ SIMPLEQ_ENTRY(dwattr) dat_next;
+ uint64_t dat_attr;
+ uint64_t dat_form;
+};
+
+struct dwaval {
+ SIMPLEQ_ENTRY(dwaval) dav_next;
+ struct dwattr *dav_dat; /* corresponding attribute */
+ union {
+ struct dwbuf _buf;
+ struct {
+ const char *_str;
+ union {
+ uint64_t _u64;
+ int64_t _s64;
+ uint32_t _u32;
+ uint16_t _u16;
+ uint8_t _u8;
+ } _T;
+ } _V;
+ } AV;
+#define dav_buf AV._buf
+#define dav_str AV._V._str
+#define dav_u64 AV._V._T._u64
+#define dav_s64 AV._V._T._s64
+#define dav_u32 AV._V._T._u32
+#define dav_u16 AV._V._T._u16
+#define dav_u8 AV._V._T._u8
+};
+
+SIMPLEQ_HEAD(dwaval_queue, dwaval);
+
+struct dwdie {
+ SIMPLEQ_ENTRY(dwdie) die_next;
+ struct dwabbrev *die_dab;
+ size_t die_offset;
+ uint8_t die_lvl;
+ struct dwaval_queue die_avals;
+};
+
+SIMPLEQ_HEAD(dwdie_queue, dwdie);
+
+struct dwabbrev {
+ SIMPLEQ_ENTRY(dwabbrev) dab_next;
+ uint64_t dab_code;
+ uint64_t dab_tag;
+ uint8_t dab_children;
+ SIMPLEQ_HEAD(, dwattr) dab_attrs;
+};
+
+SIMPLEQ_HEAD(dwabbrev_queue, dwabbrev);
+
+struct dwcu {
+ uint64_t dcu_length;
+ uint64_t dcu_abbroff;
+ uint16_t dcu_version;
+ uint8_t dcu_psize;
+ size_t dcu_offset; /* offset in the segment */
+ struct dwabbrev_queue dcu_abbrevs;
+ struct dwdie_queue dcu_dies;
+};
+
+const char *dw_tag2name(uint64_t);
+const char *dw_at2name(uint64_t);
+const char *dw_form2name(uint64_t);
+const char *dw_op2name(uint8_t);
+
+int dw_loc_parse(struct dwbuf *, uint8_t *, uint64_t *, uint64_t *);
+
+int dw_ab_parse(struct dwbuf *, struct dwabbrev_queue *);
+int dw_cu_parse(struct dwbuf *, struct dwbuf *, size_t, struct dwcu **);
+
+void dw_dabq_purge(struct dwabbrev_queue *);
+void dw_dcu_free(struct dwcu *);
+
+
+#endif /* _DW_H_ */
diff --git a/usr.bin/ctfconv/dwarf.h b/usr.bin/ctfconv/dwarf.h
new file mode 100644
index 00000000000..3a8373c496b
--- /dev/null
+++ b/usr.bin/ctfconv/dwarf.h
@@ -0,0 +1,972 @@
+/*
+ * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
+ * Copyright (c) 2007 John Birrell (jb@freebsd.org).
+ * 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.
+ */
+
+#ifndef _DWARF_H_
+#define _DWARF_H_
+
+#define DW_TAG_array_type 0x01
+#define DW_TAG_class_type 0x02
+#define DW_TAG_entry_point 0x03
+#define DW_TAG_enumeration_type 0x04
+#define DW_TAG_formal_parameter 0x05
+#define DW_TAG_imported_declaration 0x08
+#define DW_TAG_label 0x0a
+#define DW_TAG_lexical_block 0x0b
+#define DW_TAG_member 0x0d
+#define DW_TAG_pointer_type 0x0f
+#define DW_TAG_reference_type 0x10
+#define DW_TAG_compile_unit 0x11
+#define DW_TAG_string_type 0x12
+#define DW_TAG_structure_type 0x13
+#define DW_TAG_subroutine_type 0x15
+#define DW_TAG_typedef 0x16
+#define DW_TAG_union_type 0x17
+#define DW_TAG_unspecified_parameters 0x18
+#define DW_TAG_variant 0x19
+#define DW_TAG_common_block 0x1a
+#define DW_TAG_common_inclusion 0x1b
+#define DW_TAG_inheritance 0x1c
+#define DW_TAG_inlined_subroutine 0x1d
+#define DW_TAG_module 0x1e
+#define DW_TAG_ptr_to_member_type 0x1f
+#define DW_TAG_set_type 0x20
+#define DW_TAG_subrange_type 0x21
+#define DW_TAG_with_stmt 0x22
+#define DW_TAG_access_declaration 0x23
+#define DW_TAG_base_type 0x24
+#define DW_TAG_catch_block 0x25
+#define DW_TAG_const_type 0x26
+#define DW_TAG_constant 0x27
+#define DW_TAG_enumerator 0x28
+#define DW_TAG_friend 0x2a
+#define DW_TAG_namelist 0x2b
+#define DW_TAG_namelist_item 0x2c
+#define DW_TAG_packed_type 0x2d
+#define DW_TAG_subprogram 0x2e
+#define DW_TAG_template_type_parameter 0x2f
+#define DW_TAG_template_value_parameter 0x30
+#define DW_TAG_thrown_type 0x31
+#define DW_TAG_try_block 0x32
+#define DW_TAG_variant_part 0x33
+#define DW_TAG_variable 0x34
+#define DW_TAG_volatile_type 0x35
+#define DW_TAG_dwarf_procedure 0x36
+#define DW_TAG_restrict_type 0x37
+#define DW_TAG_interface_type 0x38
+#define DW_TAG_namespace 0x39
+#define DW_TAG_imported_module 0x3a
+#define DW_TAG_unspecified_type 0x3b
+#define DW_TAG_partial_unit 0x3c
+#define DW_TAG_imported_unit 0x3d
+#define DW_TAG_condition 0x3f
+#define DW_TAG_shared_type 0x40
+#define DW_TAG_type_unit 0x41
+#define DW_TAG_rvalue_reference_type 0x42
+#define DW_TAG_template_alias 0x43
+#define DW_TAG_lo_user 0x4080
+#define DW_TAG_hi_user 0xffff
+
+/* GNU extensions. */
+#define DW_TAG_format_label 0x4101
+#define DW_TAG_function_template 0x4102
+#define DW_TAG_class_template 0x4103
+#define DW_TAG_GNU_BINCL 0x4104
+#define DW_TAG_GNU_EINCL 0x4105
+#define DW_TAG_GNU_template_template_parameter 0x4106
+#define DW_TAG_GNU_template_template_param 0x4106
+#define DW_TAG_GNU_template_parameter_pack 0x4107
+#define DW_TAG_GNU_formal_parameter_pack 0x4108
+#define DW_TAG_GNU_call_site 0x4109
+#define DW_TAG_GNU_call_site_parameter 0x410a
+
+#define DW_TAG_NAMES \
+ "DW_TAG_array_type", \
+ "DW_TAG_class_type", \
+ "DW_TAG_entry_point", \
+ "DW_TAG_enumeration_type", \
+ "DW_TAG_formal_parameter", \
+ NULL, \
+ NULL, \
+ "DW_TAG_imported_declaration", \
+ NULL, \
+ "DW_TAG_label", \
+ "DW_TAG_lexical_block", \
+ NULL, \
+ "DW_TAG_member", \
+ NULL, \
+ "DW_TAG_pointer_type", \
+ "DW_TAG_reference_type", \
+ "DW_TAG_compile_unit", \
+ "DW_TAG_string_type", \
+ "DW_TAG_structure_type", \
+ NULL, \
+ "DW_TAG_subroutine_type", \
+ "DW_TAG_typedef", \
+ "DW_TAG_union_type", \
+ "DW_TAG_unspecified_parameters", \
+ "DW_TAG_variant", \
+ "DW_TAG_common_block", \
+ "DW_TAG_common_inclusion", \
+ "DW_TAG_inheritance", \
+ "DW_TAG_inlined_subroutine", \
+ "DW_TAG_module", \
+ "DW_TAG_ptr_to_member_type", \
+ "DW_TAG_set_type", \
+ "DW_TAG_subrange_type", \
+ "DW_TAG_with_stmt", \
+ "DW_TAG_access_declaration", \
+ "DW_TAG_base_type", \
+ "DW_TAG_catch_block", \
+ "DW_TAG_const_type", \
+ "DW_TAG_constant", \
+ "DW_TAG_enumerator", \
+ NULL, \
+ "DW_TAG_friend", \
+ "DW_TAG_namelist", \
+ "DW_TAG_namelist_item", \
+ "DW_TAG_packed_type", \
+ "DW_TAG_subprogram", \
+ "DW_TAG_template_type_parameter", \
+ "DW_TAG_template_value_parameter", \
+ "DW_TAG_thrown_type", \
+ "DW_TAG_try_block", \
+ "DW_TAG_variant_part", \
+ "DW_TAG_variable", \
+ "DW_TAG_volatile_type", \
+ "DW_TAG_dwarf_procedure", \
+ "DW_TAG_restrict_type", \
+ "DW_TAG_interface_type", \
+ "DW_TAG_namespace", \
+ "DW_TAG_imported_module", \
+ "DW_TAG_unspecified_type", \
+ "DW_TAG_partial_unit", \
+ "DW_TAG_imported_unit", \
+ NULL, \
+ "DW_TAG_condition", \
+ "DW_TAG_shared_type", \
+ "DW_TAG_type_unit", \
+ "DW_TAG_rvalue_reference_type", \
+ "DW_TAG_template_alias",
+
+#define DW_CHILDREN_no 0x00
+#define DW_CHILDREN_yes 0x01
+
+#define DW_AT_sibling 0x01
+#define DW_AT_location 0x02
+#define DW_AT_name 0x03
+#define DW_AT_ordering 0x09
+#define DW_AT_subscr_data 0x0a
+#define DW_AT_byte_size 0x0b
+#define DW_AT_bit_offset 0x0c
+#define DW_AT_bit_size 0x0d
+#define DW_AT_element_list 0x0f
+#define DW_AT_stmt_list 0x10
+#define DW_AT_low_pc 0x11
+#define DW_AT_high_pc 0x12
+#define DW_AT_language 0x13
+#define DW_AT_member 0x14
+#define DW_AT_discr 0x15
+#define DW_AT_discr_value 0x16
+#define DW_AT_visibility 0x17
+#define DW_AT_import 0x18
+#define DW_AT_string_length 0x19
+#define DW_AT_common_reference 0x1a
+#define DW_AT_comp_dir 0x1b
+#define DW_AT_const_value 0x1c
+#define DW_AT_containing_type 0x1d
+#define DW_AT_default_value 0x1e
+#define DW_AT_inline 0x20
+#define DW_AT_is_optional 0x21
+#define DW_AT_lower_bound 0x22
+#define DW_AT_producer 0x25
+#define DW_AT_prototyped 0x27
+#define DW_AT_return_addr 0x2a
+#define DW_AT_start_scope 0x2c
+#define DW_AT_bit_stride 0x2e
+#define DW_AT_stride_size 0x2e
+#define DW_AT_upper_bound 0x2f
+#define DW_AT_abstract_origin 0x31
+#define DW_AT_accessibility 0x32
+#define DW_AT_address_class 0x33
+#define DW_AT_artificial 0x34
+#define DW_AT_base_types 0x35
+#define DW_AT_calling_convention 0x36
+#define DW_AT_count 0x37
+#define DW_AT_data_member_location 0x38
+#define DW_AT_decl_column 0x39
+#define DW_AT_decl_file 0x3a
+#define DW_AT_decl_line 0x3b
+#define DW_AT_declaration 0x3c
+#define DW_AT_discr_list 0x3d
+#define DW_AT_encoding 0x3e
+#define DW_AT_external 0x3f
+#define DW_AT_frame_base 0x40
+#define DW_AT_friend 0x41
+#define DW_AT_identifier_case 0x42
+#define DW_AT_macro_info 0x43
+#define DW_AT_namelist_item 0x44
+#define DW_AT_priority 0x45
+#define DW_AT_segment 0x46
+#define DW_AT_specification 0x47
+#define DW_AT_static_link 0x48
+#define DW_AT_type 0x49
+#define DW_AT_use_location 0x4a
+#define DW_AT_variable_parameter 0x4b
+#define DW_AT_virtuality 0x4c
+#define DW_AT_vtable_elem_location 0x4d
+#define DW_AT_allocated 0x4e
+#define DW_AT_associated 0x4f
+#define DW_AT_data_location 0x50
+#define DW_AT_byte_stride 0x51
+#define DW_AT_entry_pc 0x52
+#define DW_AT_use_UTF8 0x53
+#define DW_AT_extension 0x54
+#define DW_AT_ranges 0x55
+#define DW_AT_trampoline 0x56
+#define DW_AT_call_column 0x57
+#define DW_AT_call_file 0x58
+#define DW_AT_call_line 0x59
+#define DW_AT_description 0x5a
+#define DW_AT_binary_scale 0x5b
+#define DW_AT_decimal_scale 0x5c
+#define DW_AT_small 0x5d
+#define DW_AT_decimal_sign 0x5e
+#define DW_AT_digit_count 0x5f
+#define DW_AT_picture_string 0x60
+#define DW_AT_mutable 0x61
+#define DW_AT_threads_scaled 0x62
+#define DW_AT_explicit 0x63
+#define DW_AT_object_pointer 0x64
+#define DW_AT_endianity 0x65
+#define DW_AT_elemental 0x66
+#define DW_AT_pure 0x67
+#define DW_AT_recursive 0x68
+#define DW_AT_signature 0x69
+#define DW_AT_main_subprogram 0x6a
+#define DW_AT_data_bit_offset 0x6b
+#define DW_AT_const_expr 0x6c
+#define DW_AT_enum_class 0x6d
+#define DW_AT_linkage_name 0x6e
+#define DW_AT_lo_user 0x2000
+#define DW_AT_hi_user 0x3fff
+
+/* GNU extensions. */
+#define DW_AT_sf_names 0x2101
+#define DW_AT_src_info 0x2102
+#define DW_AT_mac_info 0x2103
+#define DW_AT_src_coords 0x2104
+#define DW_AT_body_begin 0x2105
+#define DW_AT_body_end 0x2106
+#define DW_AT_GNU_vector 0x2107
+#define DW_AT_GNU_guarded_by 0x2108
+#define DW_AT_GNU_pt_guarded_by 0x2109
+#define DW_AT_GNU_guarded 0x210a
+#define DW_AT_GNU_pt_guarded 0x210b
+#define DW_AT_GNU_locks_excluded 0x210c
+#define DW_AT_GNU_exclusive_locks_required 0x210d
+#define DW_AT_GNU_shared_locks_required 0x210e
+#define DW_AT_GNU_odr_signature 0x210f
+#define DW_AT_GNU_template_name 0x2110
+#define DW_AT_GNU_call_site_value 0x2111
+#define DW_AT_GNU_call_site_data_value 0x2112
+#define DW_AT_GNU_call_site_target 0x2113
+#define DW_AT_GNU_call_site_target_clobbered 0x2114
+#define DW_AT_GNU_tail_call 0x2115
+#define DW_AT_GNU_all_tail_call_sites 0x2116
+#define DW_AT_GNU_all_call_sites 0x2117
+#define DW_AT_GNU_all_source_call_sites 0x2118
+
+#define DW_AT_NAMES \
+ "DW_AT_sibling", \
+ "DW_AT_location", \
+ "DW_AT_name", \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ "DW_AT_ordering", \
+ "DW_AT_subscr_data", \
+ "DW_AT_byte_size", \
+ "DW_AT_bit_offset", \
+ "DW_AT_bit_size", \
+ NULL, \
+ "DW_AT_element_list", \
+ "DW_AT_stmt_list", \
+ "DW_AT_low_pc", \
+ "DW_AT_high_pc", \
+ "DW_AT_language", \
+ "DW_AT_member", \
+ "DW_AT_discr", \
+ "DW_AT_discr_value", \
+ "DW_AT_visibility", \
+ "DW_AT_import", \
+ "DW_AT_string_length", \
+ "DW_AT_common_reference", \
+ "DW_AT_comp_dir", \
+ "DW_AT_const_value", \
+ "DW_AT_containing_type", \
+ "DW_AT_default_value", \
+ NULL, \
+ "DW_AT_inline", \
+ "DW_AT_is_optional", \
+ "DW_AT_lower_bound", \
+ NULL, \
+ NULL, \
+ "DW_AT_producer", \
+ NULL, \
+ "DW_AT_prototyped", \
+ NULL, \
+ NULL, \
+ "DW_AT_return_addr", \
+ NULL, \
+ "DW_AT_start_scope", \
+ NULL, \
+ "DW_AT_bit_stride", \
+ "DW_AT_upper_bound", \
+ NULL, \
+ "DW_AT_abstract_origin", \
+ "DW_AT_accessibility", \
+ "DW_AT_address_class", \
+ "DW_AT_artificial", \
+ "DW_AT_base_types", \
+ "DW_AT_calling_convention", \
+ "DW_AT_count", \
+ "DW_AT_data_member_location", \
+ "DW_AT_decl_column", \
+ "DW_AT_decl_file", \
+ "DW_AT_decl_line", \
+ "DW_AT_declaration", \
+ "DW_AT_discr_list", \
+ "DW_AT_encoding", \
+ "DW_AT_external", \
+ "DW_AT_frame_base", \
+ "DW_AT_friend", \
+ "DW_AT_identifier_case", \
+ "DW_AT_macro_info", \
+ "DW_AT_namelist_item", \
+ "DW_AT_priority", \
+ "DW_AT_segment", \
+ "DW_AT_specification", \
+ "DW_AT_static_link", \
+ "DW_AT_type", \
+ "DW_AT_use_location", \
+ "DW_AT_variable_parameter", \
+ "DW_AT_virtuality", \
+ "DW_AT_vtable_elem_location", \
+ "DW_AT_allocated", \
+ "DW_AT_associated", \
+ "DW_AT_data_location", \
+ "DW_AT_byte_stride", \
+ "DW_AT_entry_pc", \
+ "DW_AT_use_UTF8", \
+ "DW_AT_extension", \
+ "DW_AT_ranges", \
+ "DW_AT_trampoline", \
+ "DW_AT_call_column", \
+ "DW_AT_call_file", \
+ "DW_AT_call_line", \
+ "DW_AT_description", \
+ "DW_AT_binary_scale", \
+ "DW_AT_decimal_scale", \
+ "DW_AT_small", \
+ "DW_AT_decimal_sign", \
+ "DW_AT_digit_count", \
+ "DW_AT_picture_string", \
+ "DW_AT_mutable", \
+ "DW_AT_threads_scaled", \
+ "DW_AT_explicit", \
+ "DW_AT_object_pointer", \
+ "DW_AT_endianity", \
+ "DW_AT_elemental", \
+ "DW_AT_pure", \
+ "DW_AT_recursive", \
+ "DW_AT_signature", \
+ "DW_AT_main_subprogram", \
+ "DW_AT_data_bit_offset", \
+ "DW_AT_const_expr", \
+ "DW_AT_enum_class", \
+ "DW_AT_linkage_name", \
+
+#define DW_FORM_addr 0x01
+#define DW_FORM_block2 0x03
+#define DW_FORM_block4 0x04
+#define DW_FORM_data2 0x05
+#define DW_FORM_data4 0x06
+#define DW_FORM_data8 0x07
+#define DW_FORM_string 0x08
+#define DW_FORM_block 0x09
+#define DW_FORM_block1 0x0a
+#define DW_FORM_data1 0x0b
+#define DW_FORM_flag 0x0c
+#define DW_FORM_sdata 0x0d
+#define DW_FORM_strp 0x0e
+#define DW_FORM_udata 0x0f
+#define DW_FORM_ref_addr 0x10
+#define DW_FORM_ref1 0x11
+#define DW_FORM_ref2 0x12
+#define DW_FORM_ref4 0x13
+#define DW_FORM_ref8 0x14
+#define DW_FORM_ref_udata 0x15
+#define DW_FORM_indirect 0x16
+#define DW_FORM_sec_offset 0x17
+#define DW_FORM_exprloc 0x18
+#define DW_FORM_flag_present 0x19
+#define DW_FORM_ref_sig8 0x20
+#define DW_FORM_GNU_ref_alt 0x1f20
+#define DW_FORM_GNU_strp_alt 0x1f21
+
+#define DW_FORM_NAMES \
+ "DW_FORM_addr", \
+ NULL, \
+ "DW_FORM_block2", \
+ "DW_FORM_block4", \
+ "DW_FORM_data2", \
+ "DW_FORM_data4", \
+ "DW_FORM_data8", \
+ "DW_FORM_string", \
+ "DW_FORM_block", \
+ "DW_FORM_block1", \
+ "DW_FORM_data1", \
+ "DW_FORM_flag", \
+ "DW_FORM_sdata", \
+ "DW_FORM_strp", \
+ "DW_FORM_udata", \
+ "DW_FORM_ref_addr", \
+ "DW_FORM_ref1", \
+ "DW_FORM_ref2", \
+ "DW_FORM_ref4", \
+ "DW_FORM_ref8", \
+ "DW_FORM_ref_udata", \
+ "DW_FORM_indirect", \
+ "DW_FORM_sec_offset", \
+ "DW_FORM_exprloc", \
+ "DW_FORM_flag_present", \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ "DW_FORM_ref_sig8", \
+
+#define DW_OP_addr 0x03
+#define DW_OP_deref 0x06
+#define DW_OP_const1u 0x08
+#define DW_OP_const1s 0x09
+#define DW_OP_const2u 0x0a
+#define DW_OP_const2s 0x0b
+#define DW_OP_const4u 0x0c
+#define DW_OP_const4s 0x0d
+#define DW_OP_const8u 0x0e
+#define DW_OP_const8s 0x0f
+#define DW_OP_constu 0x10
+#define DW_OP_consts 0x11
+#define DW_OP_dup 0x12
+#define DW_OP_drop 0x13
+#define DW_OP_over 0x14
+#define DW_OP_pick 0x15
+#define DW_OP_swap 0x16
+#define DW_OP_rot 0x17
+#define DW_OP_xderef 0x18
+#define DW_OP_abs 0x19
+#define DW_OP_and 0x1a
+#define DW_OP_div 0x1b
+#define DW_OP_minus 0x1c
+#define DW_OP_mod 0x1d
+#define DW_OP_mul 0x1e
+#define DW_OP_neg 0x1f
+#define DW_OP_not 0x20
+#define DW_OP_or 0x21
+#define DW_OP_plus 0x22
+#define DW_OP_plus_uconst 0x23
+#define DW_OP_shl 0x24
+#define DW_OP_shr 0x25
+#define DW_OP_shra 0x26
+#define DW_OP_xor 0x27
+#define DW_OP_bra 0x28
+#define DW_OP_eq 0x29
+#define DW_OP_ge 0x2a
+#define DW_OP_gt 0x2b
+#define DW_OP_le 0x2c
+#define DW_OP_lt 0x2d
+#define DW_OP_ne 0x2e
+#define DW_OP_skip 0x2f
+#define DW_OP_lit0 0x30
+#define DW_OP_lit1 0x31
+#define DW_OP_lit2 0x32
+#define DW_OP_lit3 0x33
+#define DW_OP_lit4 0x34
+#define DW_OP_lit5 0x35
+#define DW_OP_lit6 0x36
+#define DW_OP_lit7 0x37
+#define DW_OP_lit8 0x38
+#define DW_OP_lit9 0x39
+#define DW_OP_lit10 0x3a
+#define DW_OP_lit11 0x3b
+#define DW_OP_lit12 0x3c
+#define DW_OP_lit13 0x3d
+#define DW_OP_lit14 0x3e
+#define DW_OP_lit15 0x3f
+#define DW_OP_lit16 0x40
+#define DW_OP_lit17 0x41
+#define DW_OP_lit18 0x42
+#define DW_OP_lit19 0x43
+#define DW_OP_lit20 0x44
+#define DW_OP_lit21 0x45
+#define DW_OP_lit22 0x46
+#define DW_OP_lit23 0x47
+#define DW_OP_lit24 0x48
+#define DW_OP_lit25 0x49
+#define DW_OP_lit26 0x4a
+#define DW_OP_lit27 0x4b
+#define DW_OP_lit28 0x4c
+#define DW_OP_lit29 0x4d
+#define DW_OP_lit30 0x4e
+#define DW_OP_lit31 0x4f
+#define DW_OP_reg0 0x50
+#define DW_OP_reg1 0x51
+#define DW_OP_reg2 0x52
+#define DW_OP_reg3 0x53
+#define DW_OP_reg4 0x54
+#define DW_OP_reg5 0x55
+#define DW_OP_reg6 0x56
+#define DW_OP_reg7 0x57
+#define DW_OP_reg8 0x58
+#define DW_OP_reg9 0x59
+#define DW_OP_reg10 0x5a
+#define DW_OP_reg11 0x5b
+#define DW_OP_reg12 0x5c
+#define DW_OP_reg13 0x5d
+#define DW_OP_reg14 0x5e
+#define DW_OP_reg15 0x5f
+#define DW_OP_reg16 0x60
+#define DW_OP_reg17 0x61
+#define DW_OP_reg18 0x62
+#define DW_OP_reg19 0x63
+#define DW_OP_reg20 0x64
+#define DW_OP_reg21 0x65
+#define DW_OP_reg22 0x66
+#define DW_OP_reg23 0x67
+#define DW_OP_reg24 0x68
+#define DW_OP_reg25 0x69
+#define DW_OP_reg26 0x6a
+#define DW_OP_reg27 0x6b
+#define DW_OP_reg28 0x6c
+#define DW_OP_reg29 0x6d
+#define DW_OP_reg30 0x6e
+#define DW_OP_reg31 0x6f
+#define DW_OP_breg0 0x70
+#define DW_OP_breg1 0x71
+#define DW_OP_breg2 0x72
+#define DW_OP_breg3 0x73
+#define DW_OP_breg4 0x74
+#define DW_OP_breg5 0x75
+#define DW_OP_breg6 0x76
+#define DW_OP_breg7 0x77
+#define DW_OP_breg8 0x78
+#define DW_OP_breg9 0x79
+#define DW_OP_breg10 0x7a
+#define DW_OP_breg11 0x7b
+#define DW_OP_breg12 0x7c
+#define DW_OP_breg13 0x7d
+#define DW_OP_breg14 0x7e
+#define DW_OP_breg15 0x7f
+#define DW_OP_breg16 0x80
+#define DW_OP_breg17 0x81
+#define DW_OP_breg18 0x82
+#define DW_OP_breg19 0x83
+#define DW_OP_breg20 0x84
+#define DW_OP_breg21 0x85
+#define DW_OP_breg22 0x86
+#define DW_OP_breg23 0x87
+#define DW_OP_breg24 0x88
+#define DW_OP_breg25 0x89
+#define DW_OP_breg26 0x8a
+#define DW_OP_breg27 0x8b
+#define DW_OP_breg28 0x8c
+#define DW_OP_breg29 0x8d
+#define DW_OP_breg30 0x8e
+#define DW_OP_breg31 0x8f
+#define DW_OP_regx 0x90
+#define DW_OP_fbreg 0x91
+#define DW_OP_bregx 0x92
+#define DW_OP_piece 0x93
+#define DW_OP_deref_size 0x94
+#define DW_OP_xderef_size 0x95
+#define DW_OP_nop 0x96
+#define DW_OP_push_object_address 0x97
+#define DW_OP_call2 0x98
+#define DW_OP_call4 0x99
+#define DW_OP_call_ref 0x9a
+#define DW_OP_form_tls_address 0x9b
+#define DW_OP_call_frame_cfa 0x9c
+#define DW_OP_bit_piece 0x9d
+#define DW_OP_implicit_value 0x9e
+#define DW_OP_stack_value 0x9f
+#define DW_OP_lo_user 0xe0
+#define DW_OP_hi_user 0xff
+
+/* GNU extensions. */
+#define DW_OP_GNU_push_tls_address 0xe0
+#define DW_OP_GNU_uninit 0xf0
+#define DW_OP_GNU_encoded_addr 0xf1
+#define DW_OP_GNU_implicit_pointer 0xf2
+#define DW_OP_GNU_entry_value 0xf3
+#define DW_OP_GNU_const_type 0xf4
+#define DW_OP_GNU_regval_type 0xf5
+#define DW_OP_GNU_deref_type 0xf6
+#define DW_OP_GNU_convert 0xf7
+#define DW_OP_GNU_reinterpret 0xf9
+#define DW_OP_GNU_parameter_ref 0xfa
+#define DW_OP_GNU_addr_index 0xfb
+#define DW_OP_GNU_const_index 0xfc
+
+#define DW_OP_NAMES \
+ NULL, \
+ NULL, \
+ "DW_OP_addr", \
+ NULL, \
+ NULL, \
+ "DW_OP_deref", \
+ NULL, \
+ "DW_OP_const1u", \
+ "DW_OP_const1s", \
+ "DW_OP_const2u", \
+ "DW_OP_const2s", \
+ "DW_OP_const4u", \
+ "DW_OP_const4s", \
+ "DW_OP_const8u", \
+ "DW_OP_const8s", \
+ "DW_OP_constu", \
+ "DW_OP_consts", \
+ "DW_OP_dup", \
+ "DW_OP_drop", \
+ "DW_OP_over", \
+ "DW_OP_pick", \
+ "DW_OP_swap", \
+ "DW_OP_rot", \
+ "DW_OP_xderef", \
+ "DW_OP_abs", \
+ "DW_OP_and", \
+ "DW_OP_div", \
+ "DW_OP_minus", \
+ "DW_OP_mod", \
+ "DW_OP_mul", \
+ "DW_OP_neg", \
+ "DW_OP_not", \
+ "DW_OP_or", \
+ "DW_OP_plus", \
+ "DW_OP_plus_uconst", \
+ "DW_OP_shl", \
+ "DW_OP_shr", \
+ "DW_OP_shra", \
+ "DW_OP_xor", \
+ "DW_OP_bra", \
+ "DW_OP_eq", \
+ "DW_OP_ge", \
+ "DW_OP_gt", \
+ "DW_OP_le", \
+ "DW_OP_lt", \
+ "DW_OP_ne", \
+ "DW_OP_skip", \
+ "DW_OP_lit0", \
+ "DW_OP_lit1", \
+ "DW_OP_lit2", \
+ "DW_OP_lit3", \
+ "DW_OP_lit4", \
+ "DW_OP_lit5", \
+ "DW_OP_lit6", \
+ "DW_OP_lit7", \
+ "DW_OP_lit8", \
+ "DW_OP_lit9", \
+ "DW_OP_lit10", \
+ "DW_OP_lit11", \
+ "DW_OP_lit12", \
+ "DW_OP_lit13", \
+ "DW_OP_lit14", \
+ "DW_OP_lit15", \
+ "DW_OP_lit16", \
+ "DW_OP_lit17", \
+ "DW_OP_lit18", \
+ "DW_OP_lit19", \
+ "DW_OP_lit20", \
+ "DW_OP_lit21", \
+ "DW_OP_lit22", \
+ "DW_OP_lit23", \
+ "DW_OP_lit24", \
+ "DW_OP_lit25", \
+ "DW_OP_lit26", \
+ "DW_OP_lit27", \
+ "DW_OP_lit28", \
+ "DW_OP_lit29", \
+ "DW_OP_lit30", \
+ "DW_OP_lit31", \
+ "DW_OP_reg0", \
+ "DW_OP_reg1", \
+ "DW_OP_reg2", \
+ "DW_OP_reg3", \
+ "DW_OP_reg4", \
+ "DW_OP_reg5", \
+ "DW_OP_reg6", \
+ "DW_OP_reg7", \
+ "DW_OP_reg8", \
+ "DW_OP_reg9", \
+ "DW_OP_reg10", \
+ "DW_OP_reg11", \
+ "DW_OP_reg12", \
+ "DW_OP_reg13", \
+ "DW_OP_reg14", \
+ "DW_OP_reg15", \
+ "DW_OP_reg16", \
+ "DW_OP_reg17", \
+ "DW_OP_reg18", \
+ "DW_OP_reg19", \
+ "DW_OP_reg20", \
+ "DW_OP_reg21", \
+ "DW_OP_reg22", \
+ "DW_OP_reg23", \
+ "DW_OP_reg24", \
+ "DW_OP_reg25", \
+ "DW_OP_reg26", \
+ "DW_OP_reg27", \
+ "DW_OP_reg28", \
+ "DW_OP_reg29", \
+ "DW_OP_reg30", \
+ "DW_OP_reg31", \
+ "DW_OP_breg0", \
+ "DW_OP_breg1", \
+ "DW_OP_breg2", \
+ "DW_OP_breg3", \
+ "DW_OP_breg4", \
+ "DW_OP_breg5", \
+ "DW_OP_breg6", \
+ "DW_OP_breg7", \
+ "DW_OP_breg8", \
+ "DW_OP_breg9", \
+ "DW_OP_breg10", \
+ "DW_OP_breg11", \
+ "DW_OP_breg12", \
+ "DW_OP_breg13", \
+ "DW_OP_breg14", \
+ "DW_OP_breg15", \
+ "DW_OP_breg16", \
+ "DW_OP_breg17", \
+ "DW_OP_breg18", \
+ "DW_OP_breg19", \
+ "DW_OP_breg20", \
+ "DW_OP_breg21", \
+ "DW_OP_breg22", \
+ "DW_OP_breg23", \
+ "DW_OP_breg24", \
+ "DW_OP_breg25", \
+ "DW_OP_breg26", \
+ "DW_OP_breg27", \
+ "DW_OP_breg28", \
+ "DW_OP_breg29", \
+ "DW_OP_breg30", \
+ "DW_OP_breg31", \
+ "DW_OP_regx", \
+ "DW_OP_fbreg", \
+ "DW_OP_bregx", \
+ "DW_OP_piece", \
+ "DW_OP_deref_size", \
+ "DW_OP_xderef_size", \
+ "DW_OP_nop", \
+ "DW_OP_push_object_address", \
+ "DW_OP_call2", \
+ "DW_OP_call4", \
+ "DW_OP_call_ref", \
+ "DW_OP_form_tls_address", \
+ "DW_OP_call_frame_cfa", \
+ "DW_OP_bit_piece", \
+ "DW_OP_implicit_value", \
+ "DW_OP_stack_value", \
+
+#define DW_ATE_address 0x1
+#define DW_ATE_boolean 0x2
+#define DW_ATE_complex_float 0x3
+#define DW_ATE_float 0x4
+#define DW_ATE_signed 0x5
+#define DW_ATE_signed_char 0x6
+#define DW_ATE_unsigned 0x7
+#define DW_ATE_unsigned_char 0x8
+#define DW_ATE_imaginary_float 0x9
+#define DW_ATE_packed_decimal 0xa
+#define DW_ATE_numeric_string 0xb
+#define DW_ATE_edited 0xc
+#define DW_ATE_signed_fixed 0xd
+#define DW_ATE_unsigned_fixed 0xe
+#define DW_ATE_decimal_float 0xf
+#define DW_ATE_lo_user 0x80
+#define DW_ATE_hi_user 0xff
+
+#define DW_ACCESS_public 0x01
+#define DW_ACCESS_protected 0x02
+#define DW_ACCESS_private 0x03
+
+#define DW_END_default 0x00
+#define DW_END_big 0x01
+#define DW_END_little 0x02
+#define DW_END_lo_user 0x40
+#define DW_END_high_user 0xff
+
+#define DW_VIS_local 0x01
+#define DW_VIS_exported 0x02
+#define DW_VIS_qualified 0x03
+
+#define DW_VIRTUALITY_none 0x00
+#define DW_VIRTUALITY_virtual 0x01
+#define DW_VIRTUALITY_pure_virtual 0x02
+
+#define DW_LANG_C89 0x0001
+#define DW_LANG_C 0x0002
+#define DW_LANG_Ada83 0x0003
+#define DW_LANG_C_plus_plus 0x0004
+#define DW_LANG_Cobol74 0x0005
+#define DW_LANG_Cobol85 0x0006
+#define DW_LANG_Fortran77 0x0007
+#define DW_LANG_Fortran90 0x0008
+#define DW_LANG_Pascal83 0x0009
+#define DW_LANG_Modula2 0x000a
+#define DW_LANG_Java 0x000b
+#define DW_LANG_C99 0x000c
+#define DW_LANG_Ada95 0x000d
+#define DW_LANG_Fortran95 0x000e
+#define DW_LANG_PLI 0x000f
+#define DW_LANG_ObjC 0x0010
+#define DW_LANG_ObjC_plus_plus 0x0011
+#define DW_LANG_UPC 0x0012
+#define DW_LANG_D 0x0013
+#define DW_LANG_lo_user 0x8000
+#define DW_LANG_hi_user 0xffff
+
+#define DW_ID_case_sensitive 0x00
+#define DW_ID_up_case 0x01
+#define DW_ID_down_case 0x02
+#define DW_ID_case_insensitive 0x03
+
+#define DW_CC_normal 0x01
+#define DW_CC_program 0x02
+#define DW_CC_nocall 0x03
+#define DW_CC_lo_user 0x40
+#define DW_CC_hi_user 0xff
+
+#define DW_INL_not_inlined 0x00
+#define DW_INL_inlined 0x01
+#define DW_INL_declared_not_inlined 0x02
+#define DW_INL_declared_inlined 0x03
+
+#define DW_ORD_row_major 0x00
+#define DW_ORD_col_major 0x01
+
+#define DW_DS_unsigned 0x01
+#define DW_DS_leading_overpunch 0x02
+#define DW_DS_trailing_overpunch 0x03
+#define DW_DS_leading_separate 0x04
+#define DW_DS_trailing_separate 0x05
+
+#define DW_DSC_label 0x00
+#define DW_DSC_range 0x01
+
+#define DW_LNS_copy 0x01
+#define DW_LNS_advance_pc 0x02
+#define DW_LNS_advance_line 0x03
+#define DW_LNS_set_file 0x04
+#define DW_LNS_set_column 0x05
+#define DW_LNS_negate_stmt 0x06
+#define DW_LNS_set_basic_block 0x07
+#define DW_LNS_const_add_pc 0x08
+#define DW_LNS_fixed_advance_pc 0x09
+#define DW_LNS_set_prologue_end 0x0a
+#define DW_LNS_set_epilogue_begin 0x0b
+#define DW_LNS_set_isa 0x0c
+
+#define DW_LNE_end_sequence 0x01
+#define DW_LNE_set_address 0x02
+#define DW_LNE_define_file 0x03
+#define DW_LNE_lo_user 0x80
+#define DW_LNE_hi_user 0xff
+
+#define DW_MACINFO_define 0x01
+#define DW_MACINFO_undef 0x02
+#define DW_MACINFO_start_file 0x03
+#define DW_MACINFO_end_file 0x04
+#define DW_MACINFO_vendor_ext 0xff
+
+#define DW_CFA_advance_loc 0x40
+#define DW_CFA_offset 0x80
+#define DW_CFA_restore 0xc0
+#define DW_CFA_extended 0
+
+#define DW_CFA_nop 0x00
+#define DW_CFA_set_loc 0x01
+#define DW_CFA_advance_loc1 0x02
+#define DW_CFA_advance_loc2 0x03
+#define DW_CFA_advance_loc4 0x04
+#define DW_CFA_offset_extended 0x05
+#define DW_CFA_restore_extended 0x06
+#define DW_CFA_undefined 0x07
+#define DW_CFA_same_value 0x08
+#define DW_CFA_register 0x09
+#define DW_CFA_remember_state 0x0a
+#define DW_CFA_restore_state 0x0b
+#define DW_CFA_def_cfa 0x0c
+#define DW_CFA_def_cfa_register 0x0d
+#define DW_CFA_def_cfa_offset 0x0e
+#define DW_CFA_def_cfa_expression 0x0f
+#define DW_CFA_expression 0x10
+#define DW_CFA_offset_extended_sf 0x11
+#define DW_CFA_def_cfa_sf 0x12
+#define DW_CFA_def_cfa_offset_sf 0x13
+#define DW_CFA_val_offset 0x14
+#define DW_CFA_val_offset_sf 0x15
+#define DW_CFA_val_expression 0x16
+#define DW_CFA_lo_user 0x1c
+#define DW_CFA_high_user 0x3f
+
+/*
+ * LSB(Linux Standard Base) extension to DWARF2.
+ */
+
+#define DW_EH_PE_absptr 0x00
+#define DW_EH_PE_uleb128 0x01
+#define DW_EH_PE_udata2 0x02
+#define DW_EH_PE_udata4 0x03
+#define DW_EH_PE_udata8 0x04
+#define DW_EH_PE_sleb128 0x09
+#define DW_EH_PE_sdata2 0x0a
+#define DW_EH_PE_sdata4 0x0b
+#define DW_EH_PE_sdata8 0x0c
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50
+#define DW_EH_PE_omit 0xff
+
+#endif /* !_DWARF_H_ */
diff --git a/usr.bin/ctfconv/elf.c b/usr.bin/ctfconv/elf.c
new file mode 100644
index 00000000000..4323b043ff6
--- /dev/null
+++ b/usr.bin/ctfconv/elf.c
@@ -0,0 +1,284 @@
+/*
+ * 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/exec_elf.h>
+
+#include <machine/reloc.h>
+
+#include <assert.h>
+#include <err.h>
+#include <string.h>
+
+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] != 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;
+
+ 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;
+ }
+ }
+}
diff --git a/usr.bin/ctfconv/generate.c b/usr.bin/ctfconv/generate.c
new file mode 100644
index 00000000000..0862e3a46d3
--- /dev/null
+++ b/usr.bin/ctfconv/generate.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2017 Martin Pieuchot
+ *
+ * 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 <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/ctf.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#ifdef ZLIB
+#include <zlib.h>
+#endif /* ZLIB */
+
+#include "itype.h"
+#include "xmalloc.h"
+#include "hash.h"
+
+#define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y))
+
+/*
+ * Dynamic buffer, used for content & string table.
+ */
+struct dbuf {
+ char *data; /* start data buffer */
+ size_t size; /* size of the buffer */
+
+ char *cptr; /* position in [data, data + size] */
+ size_t coff; /* number of written bytes */
+};
+
+#define DBUF_CHUNKSZ (64 * 1024)
+
+/* In-memory representation of a CTF section. */
+struct imcs {
+ struct dbuf body;
+ struct dbuf stab; /* corresponding string table */
+ struct hash *htab; /* hash table of known strings */
+};
+
+struct strentry {
+ struct hash_entry se_key; /* Must be first */
+#define se_str se_key.hkey
+ size_t se_off;
+};
+
+#ifdef ZLIB
+char *data_compress(const char *, size_t, off_t, size_t *);
+#endif /* ZLIB */
+
+void
+dbuf_realloc(struct dbuf *dbuf, size_t len)
+{
+ assert(dbuf != NULL);
+ assert(len != 0);
+
+ dbuf->data = xrealloc(dbuf->data, dbuf->size + len);
+ dbuf->size += len;
+ dbuf->cptr = dbuf->data + dbuf->coff;
+}
+
+void
+dbuf_copy(struct dbuf *dbuf, void const *data, size_t len)
+{
+ off_t left;
+
+ assert(dbuf->cptr != NULL);
+ assert(dbuf->data != NULL);
+ assert(dbuf->size != 0);
+
+ if (len == 0)
+ return;
+
+ left = dbuf->size - dbuf->coff;
+ if (left < (off_t)len)
+ dbuf_realloc(dbuf, ROUNDUP((len - left), DBUF_CHUNKSZ));
+
+ memcpy(dbuf->cptr, data, len);
+ dbuf->cptr += len;
+ dbuf->coff += len;
+}
+
+size_t
+dbuf_pad(struct dbuf *dbuf, int align)
+{
+ int i = (align - (dbuf->coff % align)) % align;
+
+ while (i-- > 0)
+ dbuf_copy(dbuf, "", 1);
+
+ return dbuf->coff;
+}
+
+size_t
+imcs_add_string(struct imcs *imcs, const char *str)
+{
+ struct strentry *se;
+ unsigned int slot;
+
+ if (str == NULL || *str == '\0')
+ return 0;
+
+ se = (struct strentry *)hash_find(imcs->htab, str, &slot);
+ if (se == NULL) {
+ se = xmalloc(sizeof(*se));
+ hash_insert(imcs->htab, slot, &se->se_key, str);
+ se->se_off = imcs->stab.coff;
+
+ dbuf_copy(&imcs->stab, str, strlen(str) + 1);
+ }
+
+ return se->se_off;
+}
+
+void
+imcs_add_func(struct imcs *imcs, struct itype *it)
+{
+ uint16_t func, arg;
+ struct imember *im;
+ int kind, root, vlen;
+
+ vlen = it->it_nelems;
+ kind = it->it_type;
+ root = 0;
+
+ func = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
+ dbuf_copy(&imcs->body, &func, sizeof(func));
+
+ if (kind == CTF_K_UNKNOWN)
+ return;
+
+ func = it->it_refp->it_idx;
+ dbuf_copy(&imcs->body, &func, sizeof(func));
+
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ arg = im->im_refp->it_idx;
+ dbuf_copy(&imcs->body, &arg, sizeof(arg));
+ }
+}
+
+void
+imcs_add_obj(struct imcs *imcs, struct itype *it)
+{
+ uint16_t type;
+
+ type = it->it_refp->it_idx;
+ dbuf_copy(&imcs->body, &type, sizeof(type));
+}
+
+void
+imcs_add_type(struct imcs *imcs, struct itype *it)
+{
+ struct imember *im;
+ struct ctf_type ctt;
+ struct ctf_array cta;
+ unsigned int eob;
+ uint32_t size;
+ uint16_t arg;
+ size_t ctsz;
+ int kind, root, vlen;
+
+ assert(it->it_type != CTF_K_UNKNOWN && it->it_type != CTF_K_FORWARD);
+
+ vlen = it->it_nelems;
+ size = it->it_size;
+ kind = it->it_type;
+ root = 0;
+
+ ctt.ctt_name = imcs_add_string(imcs, it_name(it));
+ ctt.ctt_info = (kind << 11) | (root << 10) | (vlen & CTF_MAX_VLEN);
+
+ /* Base types don't have reference, typedef & pointer don't have size */
+ if (it->it_refp != NULL && kind != CTF_K_ARRAY) {
+ ctt.ctt_type = it->it_refp->it_idx;
+ ctsz = sizeof(struct ctf_stype);
+ } else if (size <= CTF_MAX_SIZE) {
+ ctt.ctt_size = size;
+ ctsz = sizeof(struct ctf_stype);
+ } else {
+ ctt.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
+ ctt.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
+ ctt.ctt_size = CTF_LSIZE_SENT;
+ ctsz = sizeof(struct ctf_type);
+ }
+
+ dbuf_copy(&imcs->body, &ctt, ctsz);
+
+ switch (kind) {
+ assert(1 == 0);
+ break;
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ eob = CTF_INT_DATA(it->it_enc, 0, size);
+ dbuf_copy(&imcs->body, &eob, sizeof(eob));
+ break;
+ case CTF_K_ARRAY:
+ memset(&cta, 0, sizeof(cta));
+ cta.cta_contents = it->it_refp->it_idx;
+ cta.cta_index = long_tidx;
+ cta.cta_nelems = it->it_nelems;
+ dbuf_copy(&imcs->body, &cta, sizeof(cta));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (size < CTF_LSTRUCT_THRESH) {
+ struct ctf_member ctm;
+
+ memset(&ctm, 0, sizeof(ctm));
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ ctm.ctm_name =
+ imcs_add_string(imcs, im->im_name);
+ ctm.ctm_type = im->im_refp->it_idx;
+ ctm.ctm_offset = im->im_off;
+
+ dbuf_copy(&imcs->body, &ctm, sizeof(ctm));
+ }
+ } else {
+ struct ctf_lmember ctlm;
+
+ memset(&ctlm, 0, sizeof(ctlm));
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ ctlm.ctlm_name =
+ imcs_add_string(imcs, im->im_name);
+ ctlm.ctlm_type = im->im_refp->it_idx;
+ ctlm.ctlm_offsethi =
+ CTF_OFFSET_TO_LMEMHI(im->im_off);
+ ctlm.ctlm_offsetlo =
+ CTF_OFFSET_TO_LMEMLO(im->im_off);
+
+
+ dbuf_copy(&imcs->body, &ctlm, sizeof(ctlm));
+ }
+ }
+ break;
+ case CTF_K_FUNCTION:
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ arg = im->im_refp->it_idx;
+ dbuf_copy(&imcs->body, &arg, sizeof(arg));
+ }
+ if (vlen & 1) {
+ arg = 0;
+ dbuf_copy(&imcs->body, &arg, sizeof(arg));
+ }
+ break;
+ case CTF_K_ENUM:
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ struct ctf_enum cte;
+
+ cte.cte_name = imcs_add_string(imcs, im->im_name);
+ cte.cte_value = im->im_ref;
+
+ dbuf_copy(&imcs->body, &cte, sizeof(cte));
+ }
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ default:
+ break;
+ }
+}
+
+void
+imcs_generate(struct imcs *imcs, struct ctf_header *cth, const char *label)
+{
+ struct itype *it;
+ struct ctf_lblent lbl;
+
+ memset(imcs, 0, sizeof(*imcs));
+
+ dbuf_realloc(&imcs->body, DBUF_CHUNKSZ);
+ dbuf_realloc(&imcs->stab, DBUF_CHUNKSZ);
+
+ imcs->htab = hash_init(10);
+ if (imcs->htab == NULL)
+ err(1, "hash_init");
+
+ /* Add empty string */
+ dbuf_copy(&imcs->stab, "", 1);
+
+ /* We don't use parent label */
+ cth->cth_parlabel = 0;
+ cth->cth_parname = 0;
+
+ /* Insert a single label for all types. */
+ cth->cth_lbloff = 0;
+ lbl.ctl_label = imcs_add_string(imcs, label);
+ lbl.ctl_typeidx = tidx;
+ dbuf_copy(&imcs->body, &lbl, sizeof(lbl));
+
+ /* Insert objects */
+ cth->cth_objtoff = dbuf_pad(&imcs->body, 2);
+ TAILQ_FOREACH(it, &iobjq, it_symb)
+ imcs_add_obj(imcs, it);
+
+ /* Insert functions */
+ cth->cth_funcoff = dbuf_pad(&imcs->body, 2);
+ TAILQ_FOREACH(it, &ifuncq, it_symb)
+ imcs_add_func(imcs, it);
+
+ /* Insert types */
+ cth->cth_typeoff = dbuf_pad(&imcs->body, 4);
+ TAILQ_FOREACH(it, &itypeq, it_next) {
+ if (it->it_flags & (ITF_FUNC|ITF_OBJ))
+ continue;
+
+ imcs_add_type(imcs, it);
+ }
+
+ /* String table is written from its own buffer. */
+ cth->cth_stroff = imcs->body.coff;
+ cth->cth_strlen = imcs->stab.coff;
+}
+
+/*
+ * Generate a CTF buffer from the internal type representation.
+ */
+int
+generate(const char *path, const char *label, int compress)
+{
+ char *p, *ctfdata = NULL;
+ ssize_t ctflen;
+ struct ctf_header cth;
+ struct imcs imcs;
+ int error, fd;
+
+ memset(&cth, 0, sizeof(cth));
+
+ cth.cth_magic = CTF_MAGIC;
+ cth.cth_version = CTF_VERSION;
+
+#ifdef ZLIB
+ if (compress)
+ cth.cth_flags = CTF_F_COMPRESS;
+#endif /* ZLIB */
+
+ imcs_generate(&imcs, &cth, label);
+
+ ctflen = sizeof(cth) + imcs.body.coff + imcs.stab.coff;
+ p = ctfdata = xmalloc(ctflen);
+
+ memcpy(p, &cth, sizeof(cth));
+ p += sizeof(cth);
+
+ memcpy(p, imcs.body.data, imcs.body.coff);
+ p += imcs.body.coff;
+
+ memcpy(p, imcs.stab.data, imcs.stab.coff);
+ p += imcs.stab.coff;
+
+ assert((p - ctfdata) == ctflen);
+
+#ifdef ZLIB
+ if (compress) {
+ char *cdata;
+ size_t clen;
+
+ cdata = data_compress(ctfdata + sizeof(cth),
+ ctflen - sizeof(cth), ctflen - sizeof(cth), &clen);
+ if (cdata == NULL) {
+ warnx("compressing CTF data");
+ free(ctfdata);
+ return -1;
+ }
+
+ memcpy(ctfdata + sizeof(cth), cdata, clen);
+ ctflen = clen + sizeof(cth);
+
+ free(cdata);
+ }
+#endif /* ZLIB */
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1) {
+ warn("open %s", path);
+ free(ctfdata);
+ return -1;
+ }
+
+ if (write(fd, ctfdata, ctflen) != ctflen) {
+ warn("unable to write %zd bytes for %s", ctflen, path);
+ error = -1;
+ }
+
+ close(fd);
+ free(ctfdata);
+ return 0;
+}
+
+#ifdef ZLIB
+char *
+data_compress(const char *buf, size_t size, off_t len, size_t *pclen)
+{
+ z_stream stream;
+ char *data;
+ int error;
+
+ data = malloc(len);
+ if (data == NULL) {
+ warn(NULL);
+ return NULL;
+ }
+
+ memset(&stream, 0, sizeof(stream));
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if ((error = deflateInit(&stream, Z_BEST_COMPRESSION)) != Z_OK) {
+ warnx("zlib deflateInit failed: %s", zError(error));
+ goto exit;
+ }
+
+ stream.next_in = (void *)buf;
+ stream.avail_in = size;
+ stream.next_out = (unsigned char *)data;
+ stream.avail_out = len;
+
+ if ((error = deflate(&stream, Z_FINISH)) != Z_STREAM_END) {
+ warnx("zlib deflate failed: %s", zError(error));
+ deflateEnd(&stream);
+ goto exit;
+ }
+
+ if ((error = deflateEnd(&stream)) != Z_OK) {
+ warnx("zlib deflateEnd failed: %s", zError(error));
+ goto exit;
+ }
+
+ if (pclen != NULL)
+ *pclen = stream.total_out;
+
+ return data;
+
+exit:
+ free(data);
+ return NULL;
+}
+#endif /* ZLIB */
diff --git a/usr.bin/ctfconv/hash.c b/usr.bin/ctfconv/hash.c
new file mode 100644
index 00000000000..3111fe3873f
--- /dev/null
+++ b/usr.bin/ctfconv/hash.c
@@ -0,0 +1,242 @@
+/* Copyright (c) 1999, 2004 Marc Espie <espie@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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "hash.h"
+
+struct _hash_record {
+ uint32_t hv;
+ struct hash_entry *p;
+};
+
+struct hash {
+ struct _hash_record *t;
+ unsigned int size;
+ unsigned int total;
+ unsigned int deleted;
+};
+
+#define DELETED ((struct hash_entry *)h)
+#define NONE (h->size)
+
+/* Don't bother changing the hash table if the change is small enough. */
+#define MINSIZE (1UL << 4)
+#define MINDELETED 4
+
+static void hash_resize(struct hash *);
+static uint32_t hash_interval(const char *, const char **);
+static unsigned int hash_qlookup(struct hash *, const char *);
+
+
+/* hash_delete only frees the hash structure. Use hash_first/hash_next
+ * to free entries as well. */
+void
+hash_delete(struct hash *h)
+{
+ free(h->t);
+ h->t = NULL;
+}
+
+static void
+hash_resize(struct hash *h)
+{
+ struct _hash_record *n;
+ size_t ns;
+ unsigned int j;
+ unsigned int i, incr;
+
+ if (4 * h->deleted < h->total) {
+ if (h->size >= (UINT_MAX >> 1U))
+ ns = UINT_MAX;
+ else
+ ns = h->size << 1U;
+ } else if (3 * h->deleted > 2 * h->total)
+ ns = h->size >> 1U;
+ else
+ ns = h->size;
+ if (ns < MINSIZE)
+ ns = MINSIZE;
+
+ n = calloc(ns, sizeof(struct _hash_record));
+ if (!n)
+ return;
+
+ for (j = 0; j < h->size; j++) {
+ if (h->t[j].p != NULL && h->t[j].p != DELETED) {
+ i = h->t[j].hv % ns;
+ incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
+ while (n[i].p != NULL) {
+ i += incr;
+ if (i >= ns)
+ i -= ns;
+ }
+ n[i].hv = h->t[j].hv;
+ n[i].p = h->t[j].p;
+ }
+ }
+ free(h->t);
+ h->t = n;
+ h->size = ns;
+ h->total -= h->deleted;
+ h->deleted = 0;
+}
+
+void *
+hash_remove(struct hash *h, unsigned int i)
+{
+ void *result = (void *)h->t[i].p;
+
+ if (result == NULL || result == DELETED)
+ return NULL;
+
+ h->t[i].p = DELETED;
+ h->deleted++;
+ if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
+ hash_resize(h);
+ return result;
+}
+
+void
+hash_insert(struct hash *h, unsigned int i, struct hash_entry *p,
+ const char *key)
+{
+ p->hkey = key;
+
+ if (h->t[i].p == DELETED) {
+ h->deleted--;
+ h->t[i].p = p;
+ } else {
+ h->t[i].p = p;
+ /* Arbitrary resize boundary. Tweak if not efficient enough. */
+ if (++h->total * 4 > h->size * 3)
+ hash_resize(h);
+ }
+}
+
+void *
+hash_first(struct hash *h, unsigned int *pos)
+{
+ *pos = 0;
+ return hash_next(h, pos);
+}
+
+void *
+hash_next(struct hash *h, unsigned int *pos)
+{
+ for (; *pos < h->size; (*pos)++)
+ if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
+ return (void *)h->t[(*pos)++].p;
+ return NULL;
+}
+
+struct hash *
+hash_init(unsigned int size)
+{
+ struct hash *h;
+
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ return NULL;
+
+ h->size = 1UL << size;
+ if (h->size < MINSIZE)
+ h->size = MINSIZE;
+ /* Copy info so that caller may free it. */
+ h->total = h->deleted = 0;
+ h->t = calloc(h->size, sizeof(struct _hash_record));
+ if (h->t == NULL) {
+ free(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+static uint32_t
+hash_interval(const char *s, const char **e)
+{
+ uint32_t k;
+
+ if (!*e)
+ *e = s + strlen(s);
+ if (s == *e)
+ k = 0;
+ else
+ k = *s++;
+ while (s != *e)
+ k = ((k << 2) | (k >> 30)) ^ *s++;
+ return k;
+}
+
+static unsigned int
+hash_qlookup(struct hash *h, const char *start)
+{
+ const char *end = NULL;
+ unsigned int i, incr;
+ unsigned int empty;
+ uint32_t hv;
+
+ hv = hash_interval(start, &end);
+
+ empty = NONE;
+ i = hv % h->size;
+ incr = ((hv % (h->size-2)) & ~1) + 1;
+ while (h->t[i].p != NULL) {
+ if (h->t[i].p == DELETED) {
+ if (empty == NONE)
+ empty = i;
+ } else if (h->t[i].hv == hv &&
+ strncmp(h->t[i].p->hkey, start, end - start) == 0 &&
+ (h->t[i].p->hkey)[end-start] == '\0') {
+ if (empty != NONE) {
+ h->t[empty].hv = hv;
+ h->t[empty].p = h->t[i].p;
+ h->t[i].p = DELETED;
+ return empty;
+ } else {
+ return i;
+ }
+ }
+ i += incr;
+ if (i >= h->size)
+ i -= h->size;
+ }
+
+ /* Found an empty position. */
+ if (empty != NONE)
+ i = empty;
+ h->t[i].hv = hv;
+ return i;
+}
+
+struct hash_entry *
+hash_find(struct hash *h, const char *start, unsigned int *slot)
+{
+ unsigned int i;
+
+ i = hash_qlookup(h, start);
+ if (slot != NULL)
+ *slot = i;
+
+ if (h->t[i].p == DELETED)
+ return NULL;
+
+ return h->t[i].p;
+}
diff --git a/usr.bin/ctfconv/hash.h b/usr.bin/ctfconv/hash.h
new file mode 100644
index 00000000000..280021255cc
--- /dev/null
+++ b/usr.bin/ctfconv/hash.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 Martin Pieuchot
+ *
+ * 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.
+ */
+
+struct hash;
+
+struct hash_entry {
+ const char *hkey;
+};
+
+struct hash *hash_init(unsigned);
+void hash_delete(struct hash *);
+
+void *hash_first(struct hash *, unsigned int *);
+void *hash_next(struct hash *, unsigned int *);
+struct hash_entry *hash_find(struct hash *, const char *, unsigned int *);
+void *hash_remove(struct hash *, unsigned int);
+void hash_insert(struct hash *, unsigned int, struct hash_entry *, const char *);
diff --git a/usr.bin/ctfconv/itype.h b/usr.bin/ctfconv/itype.h
new file mode 100644
index 00000000000..f19d2adad42
--- /dev/null
+++ b/usr.bin/ctfconv/itype.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016-2017 Martin Pieuchot
+ * Copyright (c) 2016 Jasper Lievisse Adriaanse <jasper@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.
+ */
+
+#ifndef _ITTYPE_H_
+#define _ITTYPE_H_
+
+#define ITNAME_MAX 128
+
+struct imember;
+struct itref;
+
+/*
+ * Internal type representation.
+ *
+ * Some bits of DWARF that we want to keep around to resolve types and
+ * variables to their intrinsics.
+ */
+struct itype {
+ TAILQ_ENTRY(itype) it_next; /* itype: global queue of types */
+ TAILQ_ENTRY(itype) it_symb; /* itype: global queue of symbol */
+ RB_ENTRY(itype) it_node; /* itype: per-type tree of types */
+
+ SIMPLEQ_HEAD(, itref) it_refs; /* itpye: backpointing refs */
+
+ TAILQ_HEAD(, imember) it_members;/* itype: members of struct/union */
+
+ size_t it_off; /* DWARF: matching .abbrev offset */
+ uint64_t it_ref; /* DWARF: CU offset of ref. type */
+
+ struct itype *it_refp; /* itype: resolved type */
+
+ char it_name[ITNAME_MAX];/* CTF: type name */
+ uint32_t it_size; /* CTF: size in byte or bits */
+ uint32_t it_nelems; /* CTF: # of members or arguments */
+ uint16_t it_enc; /* CTF: base type encoding */
+ uint16_t it_idx; /* CTF: generated type ID */
+ uint16_t it_type; /* CTF: type */
+ uint8_t __pad[2];
+
+ unsigned int it_flags; /* itype: parser flags */
+#define ITF_UNRES 0x01 /* needs to be resolved */
+#define ITF_UNRES_MEMB 0x02 /* members need to be resolved */
+#define ITF_FUNC 0x04 /* is a function */
+#define ITF_OBJ 0x08 /* is an object */
+#define ITF_VARARGS 0x10 /* takes varargs */
+#define ITF_INSERTED 0x20 /* already found/inserted */
+#define ITF_USED 0x40 /* referenced in the current CU */
+#define ITF_ANON 0x80 /* type without name */
+#define ITF_MASK (ITF_INSERTED|ITF_USED)
+};
+
+/*
+ * Member for types with a variable length (struct, array, etc).
+ */
+struct imember {
+ TAILQ_ENTRY(imember) im_next;
+ char im_name[ITNAME_MAX]; /* struct field name */
+ size_t im_ref; /* CU offset of the field type */
+ size_t im_off; /* field offset in struct/union */
+ struct itype *im_refp; /* resolved CTF type */
+ unsigned int im_flags; /* parser flags */
+#define ITM_ANON 0x01 /* member without name */
+};
+
+/*
+ * Used to build a list of backpointing references to speed up
+ * merging duplicated types.
+ */
+struct itref {
+ SIMPLEQ_ENTRY(itref) ir_next;
+ struct itype *ir_itp;
+};
+
+TAILQ_HEAD(itype_queue, itype);
+RB_HEAD(isymb_tree, itype);
+
+/* lists of types, functions & data objects */
+extern struct itype_queue itypeq, ifuncq, iobjq;
+extern struct isymb_tree isymbt; /* tree of symbols */
+extern uint16_t tidx; /* type index */
+extern uint16_t long_tidx; /* type ID for "long" */
+
+RB_PROTOTYPE(isymb_tree, itype, it_node, it_name_cmp);
+
+struct itype *it_dup(struct itype *);
+const char *it_name(struct itype *);
+
+#endif /*_ITTYPE_H_ */
diff --git a/usr.bin/ctfconv/parse.c b/usr.bin/ctfconv/parse.c
new file mode 100644
index 00000000000..3d5a1d5727b
--- /dev/null
+++ b/usr.bin/ctfconv/parse.c
@@ -0,0 +1,1308 @@
+/*
+ * Copyright (c) 2016-2017 Martin Pieuchot
+ * Copyright (c) 2016 Jasper Lievisse Adriaanse <jasper@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.
+ */
+
+/*
+ * DWARF to IT (internal type) representation parser.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/ctf.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "itype.h"
+#include "xmalloc.h"
+#include "dwarf.h"
+#include "dw.h"
+#include "pool.h"
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#ifndef NOPOOL
+struct pool it_pool, im_pool, ir_pool;
+#endif /* NOPOOL */
+
+#define DPRINTF(x...) do { /*printf(x)*/ } while (0)
+
+#define VOID_OFFSET 1 /* Fake offset for generating "void" type. */
+
+/*
+ * Tree used to resolve per-CU types based on their offset in
+ * the abbrev section.
+ */
+RB_HEAD(ioff_tree, itype);
+
+/*
+ * Per-type trees used to merge exsiting types with the ones of
+ * a newly parsed CU.
+ */
+RB_HEAD(itype_tree, itype) itypet[CTF_K_MAX];
+
+/*
+ * Tree of symbols used to build a list matching the order of
+ * the ELF symbol table.
+ */
+struct isymb_tree isymbt;
+
+struct itype *void_it;
+uint16_t tidx, fidx, oidx; /* type, func & object IDs */
+uint16_t long_tidx; /* index of "long", for array */
+
+
+void cu_stat(void);
+void cu_parse(struct dwcu *, struct itype_queue *,
+ struct ioff_tree *);
+void cu_resolve(struct dwcu *, struct itype_queue *,
+ struct ioff_tree *);
+void cu_reference(struct dwcu *, struct itype_queue *);
+void cu_merge(struct dwcu *, struct itype_queue *);
+
+struct itype *parse_base(struct dwdie *, size_t);
+struct itype *parse_refers(struct dwdie *, size_t, int);
+struct itype *parse_array(struct dwdie *, size_t);
+struct itype *parse_enum(struct dwdie *, size_t);
+struct itype *parse_struct(struct dwdie *, size_t, int, size_t);
+struct itype *parse_function(struct dwdie *, size_t);
+struct itype *parse_funcptr(struct dwdie *, size_t);
+struct itype *parse_variable(struct dwdie *, size_t);
+
+void subparse_subrange(struct dwdie *, size_t, struct itype *);
+void subparse_enumerator(struct dwdie *, size_t, struct itype *);
+void subparse_member(struct dwdie *, size_t, struct itype *, size_t);
+void subparse_arguments(struct dwdie *, size_t, struct itype *);
+
+size_t dav2val(struct dwaval *, size_t);
+const char *dav2str(struct dwaval *);
+const char *enc2name(unsigned short);
+
+struct itype *it_new(uint64_t, size_t, const char *, uint32_t, uint16_t,
+ uint64_t, uint16_t, unsigned int);
+void it_reference(struct itype *);
+void it_free(struct itype *);
+int it_cmp(struct itype *, struct itype *);
+int it_name_cmp(struct itype *, struct itype *);
+int it_off_cmp(struct itype *, struct itype *);
+void ir_add(struct itype *, struct itype *);
+void ir_purge(struct itype *);
+struct imember *im_new(const char *, size_t, size_t);
+
+RB_GENERATE(itype_tree, itype, it_node, it_cmp);
+RB_GENERATE(isymb_tree, itype, it_node, it_name_cmp);
+RB_GENERATE(ioff_tree, itype, it_node, it_off_cmp);
+
+/*
+ * Construct a list of internal type and functions based on DWARF
+ * INFO and ABBREV sections.
+ *
+ * Multiple CUs are supported.
+ */
+void
+dwarf_parse(const char *infobuf, size_t infolen, const char *abbuf,
+ size_t ablen)
+{
+ struct dwbuf info = { .buf = infobuf, .len = infolen };
+ struct dwbuf abbrev = { .buf = abbuf, .len = ablen };
+ struct dwcu *dcu = NULL;
+ struct ioff_tree cu_iofft;
+ struct itype_queue cu_itypeq;
+ struct itype *it;
+ int i;
+
+ for (i = 0; i < CTF_K_MAX; i++)
+ RB_INIT(&itypet[i]);
+ RB_INIT(&isymbt);
+
+ void_it = it_new(++tidx, VOID_OFFSET, "void", 0,
+ CTF_INT_SIGNED, 0, CTF_K_INTEGER, 0);
+ TAILQ_INSERT_TAIL(&itypeq, void_it, it_next);
+
+ while (dw_cu_parse(&info, &abbrev, infolen, &dcu) == 0) {
+ TAILQ_INIT(&cu_itypeq);
+ RB_INIT(&cu_iofft);
+
+ /* Parse this CU */
+ cu_parse(dcu, &cu_itypeq, &cu_iofft);
+
+ /* Resolve its types. */
+ cu_resolve(dcu, &cu_itypeq, &cu_iofft);
+ assert(RB_EMPTY(&cu_iofft));
+
+ /* Mark used type as such. */
+ cu_reference(dcu, &cu_itypeq);
+
+#ifdef DEBUG
+ /* Dump statistics for current CU. */
+ cu_stat();
+#endif
+
+ /* Merge them with the common type list. */
+ cu_merge(dcu, &cu_itypeq);
+
+ dw_dcu_free(dcu);
+ }
+
+ /* We force array's index type to be 'long', for that we need its ID. */
+ RB_FOREACH(it, itype_tree, &itypet[CTF_K_INTEGER]) {
+ if (it_name(it) == NULL || it->it_size != (8 * sizeof(long)))
+ continue;
+
+ if (strcmp(it_name(it), "unsigned") == 0) {
+ long_tidx = it->it_idx;
+ break;
+ }
+ }
+}
+
+struct itype *
+it_new(uint64_t index, size_t off, const char *name, uint32_t size,
+ uint16_t enc, uint64_t ref, uint16_t type, unsigned int flags)
+{
+ struct itype *it;
+#ifndef NOPOOL
+ static int it_pool_inited = 0;
+
+ if (!it_pool_inited) {
+ pool_init(&it_pool, "it", 512, sizeof(struct itype));
+ pool_init(&im_pool, "im", 1024, sizeof(struct imember));
+ pool_init(&ir_pool, "ir", 1024, sizeof(struct itref));
+ it_pool_inited = 1;
+ }
+#endif
+
+ assert((name != NULL) || !(flags & (ITF_FUNC|ITF_OBJ)));
+
+ it = pmalloc(&it_pool, sizeof(*it));
+ SIMPLEQ_INIT(&it->it_refs);
+ TAILQ_INIT(&it->it_members);
+ it->it_off = off;
+ it->it_ref = ref;
+ it->it_refp = NULL;
+ it->it_size = size;
+ it->it_nelems = 0;
+ it->it_enc = enc;
+ it->it_idx = index;
+ it->it_type = type;
+ it->it_flags = flags;
+
+ if (name == NULL) {
+ it->it_flags |= ITF_ANON;
+ } else {
+ size_t n;
+
+ if ((n = strlcpy(it->it_name, name, ITNAME_MAX)) > ITNAME_MAX)
+ warnx("name %s too long %zd > %d", name, n, ITNAME_MAX);
+ }
+
+ return it;
+}
+
+struct itype *
+it_dup(struct itype *it)
+{
+ struct imember *copim, *im;
+ struct itype *copit;
+
+ copit = it_new(it->it_idx, it->it_off, it_name(it), it->it_size,
+ it->it_enc, it->it_ref, it->it_type, it->it_flags);
+
+ copit->it_refp = it->it_refp;
+ copit->it_nelems = it->it_nelems;
+
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ copim = im_new(im->im_name, im->im_ref, im->im_off);
+ copim->im_refp = im->im_refp;
+ TAILQ_INSERT_TAIL(&copit->it_members, copim, im_next);
+ }
+
+ return copit;
+}
+
+const char *
+it_name(struct itype *it)
+{
+ if (!(it->it_flags & ITF_ANON))
+ return it->it_name;
+
+ return NULL;
+}
+
+void
+it_reference(struct itype *it)
+{
+ struct imember *im;
+
+ if (it == NULL || it->it_flags & ITF_USED)
+ return;
+
+ it->it_flags |= ITF_USED;
+
+ it_reference(it->it_refp);
+ TAILQ_FOREACH(im, &it->it_members, im_next)
+ it_reference(im->im_refp);
+}
+
+void
+it_free(struct itype *it)
+{
+ struct imember *im;
+
+ if (it == NULL)
+ return;
+
+ while ((im = TAILQ_FIRST(&it->it_members)) != NULL) {
+ TAILQ_REMOVE(&it->it_members, im, im_next);
+ pfree(&im_pool, im);
+ }
+
+ ir_purge(it);
+ pfree(&it_pool, it);
+}
+
+/*
+ * Return 0 if ``a'' matches ``b''.
+ */
+int
+it_cmp(struct itype *a, struct itype *b)
+{
+ int diff;
+
+ if ((diff = (a->it_type - b->it_type)) != 0)
+ return diff;
+
+ if ((diff = (a->it_size - b->it_size)) != 0)
+ return diff;
+
+ if ((diff = (a->it_nelems - b->it_nelems)) != 0)
+ return diff;
+
+ /* Match by name */
+ if (!(a->it_flags & ITF_ANON) && !(b->it_flags & ITF_ANON))
+ return strcmp(it_name(a), it_name(b));
+
+ /* Only one of them is anonym */
+ if ((a->it_flags & ITF_ANON) != (b->it_flags & ITF_ANON))
+ return (a->it_flags & ITF_ANON) ? -1 : 1;
+
+ /* Match by reference */
+ if ((a->it_refp != NULL) && (b->it_refp != NULL))
+ return it_cmp(a->it_refp, b->it_refp);
+
+ return 1;
+}
+
+int
+it_name_cmp(struct itype *a, struct itype *b)
+{
+ int diff;
+
+ if ((diff = strcmp(it_name(a), it_name(b))) != 0)
+ return diff;
+
+ return ((a->it_flags|ITF_MASK) - (b->it_flags|ITF_MASK));
+}
+
+int
+it_off_cmp(struct itype *a, struct itype *b)
+{
+ return a->it_off - b->it_off;
+}
+
+void
+ir_add(struct itype *it, struct itype *tmp)
+{
+ struct itref *ir;
+
+ SIMPLEQ_FOREACH(ir, &tmp->it_refs, ir_next) {
+ if (ir->ir_itp == it)
+ return;
+ }
+
+ ir = pmalloc(&ir_pool, sizeof(*ir));
+ ir->ir_itp = it;
+ SIMPLEQ_INSERT_TAIL(&tmp->it_refs, ir, ir_next);
+}
+
+void
+ir_purge(struct itype *it)
+{
+ struct itref *ir;
+
+ while ((ir = SIMPLEQ_FIRST(&it->it_refs)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&it->it_refs, ir_next);
+ pfree(&ir_pool, ir);
+ }
+}
+
+struct imember *
+im_new(const char *name, size_t ref, size_t off)
+{
+ struct imember *im;
+
+ im = pmalloc(&im_pool, sizeof(*im));
+ im->im_ref = ref;
+ im->im_off = off;
+ im->im_refp = NULL;
+ if (name == NULL) {
+ im->im_flags = ITM_ANON;
+ } else {
+ size_t n;
+
+ n = strlcpy(im->im_name, name, ITNAME_MAX);
+ if (n > ITNAME_MAX)
+ warnx("name %s too long %zd > %d", name, n,
+ ITNAME_MAX);
+ im->im_flags = 0;
+ }
+
+ return im;
+}
+
+void
+cu_stat(void)
+{
+#ifndef NOPOOL
+ pool_dump();
+#endif
+}
+/*
+ * Worst case it's a O(n*n) resolution lookup, with ``n'' being the number
+ * of elements in ``cutq''.
+ */
+void
+cu_resolve(struct dwcu *dcu, struct itype_queue *cutq, struct ioff_tree *cuot)
+{
+ struct itype *it, *ref, tmp;
+ struct imember *im;
+ unsigned int toresolve;
+ size_t off = dcu->dcu_offset;
+
+ TAILQ_FOREACH(it, cutq, it_next) {
+ if (!(it->it_flags & (ITF_UNRES|ITF_UNRES_MEMB)))
+ continue;
+
+ if (it->it_flags & ITF_UNRES) {
+ tmp.it_off = it->it_ref + off;
+ ref = RB_FIND(ioff_tree, cuot, &tmp);
+ if (ref != NULL) {
+ it->it_refp = ref;
+ ir_add(it, ref);
+ it->it_flags &= ~ITF_UNRES;
+ }
+ }
+
+ /* All members need to be resolved. */
+ toresolve = it->it_nelems;
+ if ((it->it_flags & ITF_UNRES_MEMB) && toresolve > 0) {
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ tmp.it_off = im->im_ref + off;
+ ref = RB_FIND(ioff_tree, cuot, &tmp);
+ if (ref != NULL) {
+ im->im_refp = ref;
+ ir_add(it, ref);
+ toresolve--;
+ }
+ }
+ if (toresolve == 0)
+ it->it_flags &= ~ITF_UNRES_MEMB;
+ }
+#if defined(DEBUG)
+ if (it->it_flags & (ITF_UNRES|ITF_UNRES_MEMB)) {
+ printf("0x%zx: %s type=%d unresolved 0x%llx",
+ it->it_off, it_name(it), it->it_type, it->it_ref);
+ if (toresolve)
+ printf(": %d members", toresolve);
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ if (im->im_refp == NULL) {
+ printf("\n%zu: %s", im->im_ref,
+ im->im_name);
+ }
+ }
+ printf("\n");
+ }
+#endif /* defined(DEBUG) */
+ }
+
+ RB_FOREACH_SAFE(it, ioff_tree, cuot, ref)
+ RB_REMOVE(ioff_tree, cuot, it);
+}
+
+void
+cu_reference(struct dwcu *dcu, struct itype_queue *cutq)
+{
+ struct itype *it;
+
+ TAILQ_FOREACH(it, cutq, it_next) {
+ if (it->it_flags & (ITF_OBJ|ITF_FUNC))
+ it_reference(it);
+ }
+}
+
+/*
+ * Merge type representation from a CU with already known types.
+ */
+void
+cu_merge(struct dwcu *dcu, struct itype_queue *cutq)
+{
+ struct itype *it, *nit, *prev, *first;
+ int diff;
+
+ /* First ``it'' that needs a duplicate check. */
+ first = TAILQ_FIRST(cutq);
+ if (first == NULL)
+ return;
+
+ TAILQ_CONCAT(&itypeq, cutq, it_next);
+
+ /*
+ * First pass: merge types
+ */
+ for (it = first; it != NULL; it = nit) {
+ nit = TAILQ_NEXT(it, it_next);
+
+ /* Move functions & variable to their own list. */
+ if (it->it_flags & (ITF_FUNC|ITF_OBJ)) {
+ /*
+ * FIXME: allow static variables with the same name
+ * to be of different type.
+ */
+ if (RB_FIND(isymb_tree, &isymbt, it) == NULL)
+ RB_INSERT(isymb_tree, &isymbt, it);
+ continue;
+ }
+
+ /* Look if we already have this type. */
+ if (it->it_flags & ITF_USED)
+ prev = RB_FIND(itype_tree, &itypet[it->it_type], it);
+ else
+ prev = NULL;
+
+ if (prev != NULL) {
+ struct itype *old = it;
+ struct itref *ir;
+ struct imember *im;
+
+ /* Substitute references */
+ while ((ir = SIMPLEQ_FIRST(&old->it_refs)) != NULL) {
+ it = ir->ir_itp;
+
+ SIMPLEQ_REMOVE_HEAD(&old->it_refs, ir_next);
+ pfree(&ir_pool, ir);
+
+ if (it->it_refp == old)
+ it->it_refp = prev;
+
+ TAILQ_FOREACH(im, &it->it_members, im_next) {
+ if (im->im_refp == old)
+ im->im_refp = prev;
+ }
+ }
+
+ old->it_flags &= ~ITF_USED;
+ } else if (it->it_flags & ITF_USED) {
+ RB_INSERT(itype_tree, &itypet[it->it_type], it);
+ }
+ }
+
+ /*
+ * Second pass: update indexes
+ */
+ diff = 0;
+ for (it = first; it != NULL; it = nit) {
+ nit = TAILQ_NEXT(it, it_next);
+
+ if (it->it_flags & (ITF_FUNC|ITF_OBJ))
+ continue;
+
+ /* Adjust indexes */
+ if (it->it_flags & ITF_USED) {
+ it->it_idx -= diff;
+ continue;
+ }
+
+ /* Remove unused */
+ TAILQ_REMOVE(&itypeq, it, it_next);
+ it_free(it);
+ diff++;
+ }
+
+ /* Update global index to match removed entries. */
+ it = TAILQ_LAST(&itypeq, itype_queue);
+ while (it->it_flags & (ITF_FUNC|ITF_OBJ))
+ it = TAILQ_PREV(it, itype_queue, it_next);
+
+ tidx = it->it_idx;
+}
+
+/*
+ * Parse a CU.
+ */
+void
+cu_parse(struct dwcu *dcu, struct itype_queue *cutq, struct ioff_tree *cuot)
+{
+ struct itype *it = NULL;
+ struct dwdie *die;
+ size_t psz = dcu->dcu_psize;
+ size_t off = dcu->dcu_offset;
+
+ assert(RB_EMPTY(cuot));
+
+ SIMPLEQ_FOREACH(die, &dcu->dcu_dies, die_next) {
+ uint64_t tag = die->die_dab->dab_tag;
+
+ switch (tag) {
+ case DW_TAG_array_type:
+ it = parse_array(die, dcu->dcu_psize);
+ break;
+ case DW_TAG_enumeration_type:
+ it = parse_enum(die, dcu->dcu_psize);
+ break;
+ case DW_TAG_pointer_type:
+ it = parse_refers(die, psz, CTF_K_POINTER);
+ break;
+ case DW_TAG_structure_type:
+ it = parse_struct(die, psz, CTF_K_STRUCT, off);
+ if (it == NULL)
+ continue;
+ break;
+ case DW_TAG_typedef:
+ it = parse_refers(die, psz, CTF_K_TYPEDEF);
+ break;
+ case DW_TAG_union_type:
+ it = parse_struct(die, psz, CTF_K_UNION, off);
+ if (it == NULL)
+ continue;
+ break;
+ case DW_TAG_base_type:
+ it = parse_base(die, psz);
+ break;
+ case DW_TAG_const_type:
+ it = parse_refers(die, psz, CTF_K_CONST);
+ break;
+ case DW_TAG_volatile_type:
+ it = parse_refers(die, psz, CTF_K_VOLATILE);
+ break;
+ case DW_TAG_restrict_type:
+ it = parse_refers(die, psz, CTF_K_RESTRICT);
+ break;
+ case DW_TAG_subprogram:
+ it = parse_function(die, psz);
+ if (it == NULL)
+ continue;
+ break;
+ case DW_TAG_subroutine_type:
+ it = parse_funcptr(die, psz);
+ break;
+ /*
+ * Children are assumed to be right after their parent in
+ * the list. The parent parsing function takes care of
+ * parsing them.
+ */
+ case DW_TAG_member:
+ assert(it->it_type == CTF_K_STRUCT ||
+ it->it_type == CTF_K_UNION ||
+ it->it_type == CTF_K_ENUM);
+ continue;
+ case DW_TAG_subrange_type:
+ assert(it->it_type == CTF_K_ARRAY);
+ continue;
+ case DW_TAG_formal_parameter:
+ /*
+ * If we skipped the second inline definition,
+ * skip its arguments.
+ */
+ if (it == NULL)
+ continue;
+
+ /* See comment in subparse_arguments(). */
+ if (it->it_type == CTF_K_STRUCT ||
+ it->it_type == CTF_K_UNION ||
+ it->it_type == CTF_K_ENUM ||
+ it->it_type == CTF_K_TYPEDEF)
+ continue;
+
+ if (it->it_flags & ITF_OBJ)
+ continue;
+
+ assert(it->it_type == CTF_K_FUNCTION);
+ continue;
+ case DW_TAG_variable:
+ it = parse_variable(die, psz);
+ /* Unnamed variables are discarded. */
+ if (it == NULL)
+ continue;
+ break;
+#if 1
+ case DW_TAG_lexical_block:
+ case DW_TAG_inlined_subroutine:
+ continue;
+#endif
+ case DW_TAG_compile_unit:
+ default:
+ DPRINTF("%s\n", dw_tag2name(tag));
+ continue;
+ }
+
+ TAILQ_INSERT_TAIL(cutq, it, it_next);
+ RB_INSERT(ioff_tree, cuot, it);
+ }
+}
+
+struct itype *
+parse_base(struct dwdie *die, size_t psz)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ uint16_t encoding, enc = 0, bits = 0;
+ int type;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_encoding:
+ enc = dav2val(dav, psz);
+ break;
+ case DW_AT_byte_size:
+ bits = 8 * dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ switch (enc) {
+ case DW_ATE_unsigned:
+ case DW_ATE_address:
+ encoding = 0;
+ type = CTF_K_INTEGER;
+ break;
+ case DW_ATE_unsigned_char:
+ encoding = CTF_INT_CHAR;
+ type = CTF_K_INTEGER;
+ break;
+ case DW_ATE_signed:
+ encoding = CTF_INT_SIGNED;
+ type = CTF_K_INTEGER;
+ break;
+ case DW_ATE_signed_char:
+ encoding = CTF_INT_SIGNED | CTF_INT_CHAR;
+ type = CTF_K_INTEGER;
+ break;
+ case DW_ATE_boolean:
+ encoding = CTF_INT_SIGNED | CTF_INT_BOOL;
+ type = CTF_K_INTEGER;
+ break;
+ case DW_ATE_float:
+ if (bits < psz)
+ encoding = CTF_FP_SINGLE;
+ else if (bits == psz)
+ encoding = CTF_FP_DOUBLE;
+ else
+ encoding = CTF_FP_LDOUBLE;
+ type = CTF_K_FLOAT;
+ break;
+ case DW_ATE_complex_float:
+ if (bits < psz)
+ encoding = CTF_FP_CPLX;
+ else if (bits == psz)
+ encoding = CTF_FP_DCPLX;
+ else
+ encoding = CTF_FP_LDCPLX;
+ type = CTF_K_FLOAT;
+ break;
+ case DW_ATE_imaginary_float:
+ if (bits < psz)
+ encoding = CTF_FP_IMAGRY;
+ else if (bits == psz)
+ encoding = CTF_FP_DIMAGRY;
+ else
+ encoding = CTF_FP_LDIMAGRY;
+ type = CTF_K_FLOAT;
+ break;
+ default:
+ DPRINTF("unknown encoding: %d\n", enc);
+ return (NULL);
+ }
+
+ it = it_new(++tidx, die->die_offset, enc2name(enc), bits,
+ encoding, 0, type, 0);
+
+ return it;
+}
+
+struct itype *
+parse_refers(struct dwdie *die, size_t psz, int type)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t ref = 0, size = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ case DW_AT_byte_size:
+ size = dav2val(dav, psz);
+ assert(size < UINT_MAX);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ it = it_new(++tidx, die->die_offset, name, size, 0, ref, type,
+ ITF_UNRES);
+
+ if (it->it_ref == 0 && (it->it_size == sizeof(void *) ||
+ type == CTF_K_CONST || type == CTF_K_VOLATILE ||
+ type == CTF_K_POINTER)) {
+ /* Work around GCC/clang not emiting a type for void */
+ it->it_flags &= ~ITF_UNRES;
+ it->it_ref = VOID_OFFSET;
+ it->it_refp = void_it;
+ }
+
+ return it;
+}
+
+struct itype *
+parse_array(struct dwdie *die, size_t psz)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t ref = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ it = it_new(++tidx, die->die_offset, name, 0, 0, ref, CTF_K_ARRAY,
+ ITF_UNRES);
+
+ subparse_subrange(die, psz, it);
+
+ return it;
+}
+
+struct itype *
+parse_enum(struct dwdie *die, size_t psz)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t size = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_byte_size:
+ size = dav2val(dav, psz);
+ assert(size < UINT_MAX);
+ break;
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ it = it_new(++tidx, die->die_offset, name, size, 0, 0, CTF_K_ENUM, 0);
+
+ subparse_enumerator(die, psz, it);
+
+ return it;
+}
+
+void
+subparse_subrange(struct dwdie *die, size_t psz, struct itype *it)
+{
+ struct dwaval *dav;
+
+ assert(it->it_type == CTF_K_ARRAY);
+
+ if (die->die_dab->dab_children == DW_CHILDREN_no)
+ return;
+
+ /*
+ * This loop assumes that the children of a DIE are just
+ * after it on the list.
+ */
+ while ((die = SIMPLEQ_NEXT(die, die_next)) != NULL) {
+ uint64_t tag = die->die_dab->dab_tag;
+ size_t nelems = 0;
+
+ if (tag != DW_TAG_subrange_type)
+ break;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_count:
+ nelems = dav2val(dav, psz);
+ break;
+ case DW_AT_upper_bound:
+ nelems = dav2val(dav, psz) + 1;
+ break;
+ default:
+ DPRINTF("%s\n",
+ dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ assert(nelems < UINT_MAX);
+ it->it_nelems = nelems;
+ }
+}
+
+void
+subparse_enumerator(struct dwdie *die, size_t psz, struct itype *it)
+{
+ struct imember *im;
+ struct dwaval *dav;
+
+ assert(it->it_type == CTF_K_ENUM);
+
+ if (die->die_dab->dab_children == DW_CHILDREN_no)
+ return;
+
+ /*
+ * This loop assumes that the children of a DIE are just
+ * after it on the list.
+ */
+ while ((die = SIMPLEQ_NEXT(die, die_next)) != NULL) {
+ uint64_t tag = die->die_dab->dab_tag;
+ size_t val = 0;
+ const char *name = NULL;
+
+ if (tag != DW_TAG_enumerator)
+ break;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_const_value:
+ val = dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n",
+ dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ if (name == NULL) {
+ warnx("%s with anon member", it_name(it));
+ continue;
+ }
+
+ im = im_new(name, val, 0);
+ assert(it->it_nelems < UINT_MAX);
+ it->it_nelems++;
+ TAILQ_INSERT_TAIL(&it->it_members, im, im_next);
+ }
+}
+
+struct itype *
+parse_struct(struct dwdie *die, size_t psz, int type, size_t off)
+{
+ struct itype *it = NULL;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t size = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_byte_size:
+ size = dav2val(dav, psz);
+ assert(size < UINT_MAX);
+ break;
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ it = it_new(++tidx, die->die_offset, name, size, 0, 0, type, 0);
+
+ subparse_member(die, psz, it, off);
+
+ return it;
+}
+
+void
+subparse_member(struct dwdie *die, size_t psz, struct itype *it, size_t offset)
+{
+ struct imember *im;
+ struct dwaval *dav;
+ const char *name;
+ size_t off = 0, ref = 0, bits = 0;
+ uint8_t lvl = die->die_lvl;
+
+ assert(it->it_type == CTF_K_STRUCT || it->it_type == CTF_K_UNION);
+
+ if (die->die_dab->dab_children == DW_CHILDREN_no)
+ return;
+
+ /*
+ * This loop assumes that the children of a DIE are just
+ * after it on the list.
+ */
+ while ((die = SIMPLEQ_NEXT(die, die_next)) != NULL) {
+ int64_t tag = die->die_dab->dab_tag;
+
+ name = NULL;
+ if (die->die_lvl <= lvl)
+ break;
+
+ /* Skip members of members */
+ if (die->die_lvl > lvl + 1)
+ continue;
+
+ it->it_flags |= ITF_UNRES_MEMB;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ case DW_AT_data_member_location:
+ off = 8 * dav2val(dav, psz);
+ break;
+ case DW_AT_bit_size:
+ bits = dav2val(dav, psz);
+ assert(bits < USHRT_MAX);
+ break;
+ default:
+ DPRINTF("%s\n",
+ dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ /*
+ * When a structure is declared inside an union, we
+ * have to generate a reference to make the resolver
+ * happy.
+ */
+ if ((ref == 0) && (tag == DW_TAG_structure_type))
+ ref = die->die_offset - offset;
+
+ im = im_new(name, ref, off);
+ assert(it->it_nelems < UINT_MAX);
+ it->it_nelems++;
+ TAILQ_INSERT_TAIL(&it->it_members, im, im_next);
+ }
+}
+
+
+void
+subparse_arguments(struct dwdie *die, size_t psz, struct itype *it)
+{
+ struct imember *im;
+ struct dwaval *dav;
+ size_t ref = 0;
+
+ assert(it->it_type == CTF_K_FUNCTION);
+
+ if (die->die_dab->dab_children == DW_CHILDREN_no)
+ return;
+
+ /*
+ * This loop assumes that the children of a DIE are after it
+ * on the list.
+ */
+ while ((die = SIMPLEQ_NEXT(die, die_next)) != NULL) {
+ uint64_t tag = die->die_dab->dab_tag;
+
+ if (tag == DW_TAG_unspecified_parameters) {
+ it->it_flags |= ITF_VARARGS;
+ continue;
+ }
+
+ /*
+ * Nested declaration.
+ *
+ * This matches the case where a ``struct'', ``union'',
+ * ``enum'' or ``typedef'' is first declared "inside" a
+ * function declaration.
+ */
+ if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type ||
+ tag == DW_TAG_enumeration_type || tag == DW_TAG_typedef)
+ continue;
+
+ if (tag != DW_TAG_formal_parameter)
+ break;
+
+ it->it_flags |= ITF_UNRES_MEMB;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n",
+ dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ im = im_new(NULL, ref, 0);
+ assert(it->it_nelems < UINT_MAX);
+ it->it_nelems++;
+ TAILQ_INSERT_TAIL(&it->it_members, im, im_next);
+ }
+}
+
+struct itype *
+parse_function(struct dwdie *die, size_t psz)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t ref = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ case DW_AT_abstract_origin:
+ /*
+ * Skip second empty definition for inline
+ * functions.
+ */
+ return NULL;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ /*
+ * Work around for clang 4.0 generating DW_TAG_subprogram without
+ * any attribute.
+ */
+ if (name == NULL)
+ return NULL;
+
+ it = it_new(++fidx, die->die_offset, name, 0, 0, ref, CTF_K_FUNCTION,
+ ITF_UNRES|ITF_FUNC);
+
+ subparse_arguments(die, psz, it);
+
+ if (it->it_ref == 0) {
+ /* Work around GCC not emiting a type for void */
+ it->it_flags &= ~ITF_UNRES;
+ it->it_ref = VOID_OFFSET;
+ it->it_refp = void_it;
+ }
+
+ return it;
+}
+
+struct itype *
+parse_funcptr(struct dwdie *die, size_t psz)
+{
+ struct itype *it;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t ref = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+ it = it_new(++tidx, die->die_offset, name, 0, 0, ref, CTF_K_FUNCTION,
+ ITF_UNRES);
+
+ subparse_arguments(die, psz, it);
+
+ if (it->it_ref == 0) {
+ /* Work around GCC not emiting a type for void */
+ it->it_flags &= ~ITF_UNRES;
+ it->it_ref = VOID_OFFSET;
+ it->it_refp = void_it;
+ }
+
+ return it;
+}
+
+struct itype *
+parse_variable(struct dwdie *die, size_t psz)
+{
+ struct itype *it = NULL;
+ struct dwaval *dav;
+ const char *name = NULL;
+ size_t ref = 0;
+ int declaration = 0;
+
+ SIMPLEQ_FOREACH(dav, &die->die_avals, dav_next) {
+ switch (dav->dav_dat->dat_attr) {
+ case DW_AT_declaration:
+ declaration = dav2val(dav, psz);
+ break;
+ case DW_AT_name:
+ name = dav2str(dav);
+ break;
+ case DW_AT_type:
+ ref = dav2val(dav, psz);
+ break;
+ default:
+ DPRINTF("%s\n", dw_at2name(dav->dav_dat->dat_attr));
+ break;
+ }
+ }
+
+
+ if (!declaration && name != NULL) {
+ it = it_new(++oidx, die->die_offset, name, 0, 0, ref, 0,
+ ITF_UNRES|ITF_OBJ);
+ }
+
+ return it;
+}
+
+size_t
+dav2val(struct dwaval *dav, size_t psz)
+{
+ uint64_t val = (uint64_t)-1;
+
+ switch (dav->dav_dat->dat_form) {
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ if (psz == sizeof(uint32_t))
+ val = dav->dav_u32;
+ else
+ val = dav->dav_u64;
+ break;
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ dw_loc_parse(&dav->dav_buf, NULL, &val, NULL);
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ val = dav->dav_u8;
+ break;
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ val = dav->dav_u16;
+ break;
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ val = dav->dav_u32;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ val = dav->dav_u64;
+ break;
+ case DW_FORM_strp:
+ val = dav->dav_u32;
+ break;
+ case DW_FORM_flag_present:
+ val = 1;
+ break;
+ default:
+ break;
+ }
+
+ return val;
+}
+
+const char *
+dav2str(struct dwaval *dav)
+{
+ const char *str = NULL;
+ extern const char *dstrbuf;
+
+ switch (dav->dav_dat->dat_form) {
+ case DW_FORM_string:
+ str = dav->dav_str;
+ break;
+ case DW_FORM_strp:
+ str = dstrbuf + dav->dav_u32;
+ break;
+ default:
+ break;
+ }
+
+ return str;
+}
+
+const char *
+enc2name(unsigned short enc)
+{
+ static const char *enc_name[] = { "address", "boolean", "complex float",
+ "float", "signed", "char", "unsigned", "unsigned char",
+ "imaginary float", "packed decimal", "numeric string", "edited",
+ "signed fixed", "unsigned fixed", "decimal float" };
+
+ if (enc > 0 && enc <= nitems(enc_name))
+ return enc_name[enc - 1];
+
+ return "invalid";
+}
diff --git a/usr.bin/ctfconv/pool.c b/usr.bin/ctfconv/pool.c
new file mode 100644
index 00000000000..c864fae025b
--- /dev/null
+++ b/usr.bin/ctfconv/pool.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017 Martin Pieuchot
+ *
+ * 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.
+ */
+
+#ifndef NOPOOL
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmalloc.h"
+#include "pool.h"
+
+#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
+
+struct pool_item {
+ SLIST_ENTRY(pool_item) pi_list;
+};
+
+SIMPLEQ_HEAD(, pool) pool_head = SIMPLEQ_HEAD_INITIALIZER(pool_head);
+
+void
+pool_init(struct pool *pp, const char *name, size_t nmemb, size_t size)
+{
+ size = MAXIMUM(size, sizeof(struct pool_item));
+
+ SLIST_INIT(&pp->pr_free);
+ pp->pr_name = name;
+ pp->pr_nmemb = nmemb;
+ pp->pr_size = size;
+ pp->pr_nitems = 0;
+ pp->pr_nfree = 0;
+
+ SIMPLEQ_INSERT_TAIL(&pool_head, pp, pr_list);
+}
+
+void *
+pool_get(struct pool *pp)
+{
+ struct pool_item *pi;
+
+ if (SLIST_EMPTY(&pp->pr_free)) {
+ char *p;
+ size_t i;
+
+ p = xreallocarray(NULL, pp->pr_nmemb, pp->pr_size);
+ for (i = 0; i < pp->pr_nmemb; i++) {
+ pi = (struct pool_item *)p;
+ SLIST_INSERT_HEAD(&pp->pr_free, pi, pi_list);
+ p += pp->pr_size;
+ }
+ pp->pr_nitems += pp->pr_nmemb;
+ pp->pr_nfree += pp->pr_nmemb;
+ }
+
+ pi = SLIST_FIRST(&pp->pr_free);
+ SLIST_REMOVE_HEAD(&pp->pr_free, pi_list);
+ pp->pr_nfree--;
+
+ return pi;
+}
+
+void
+pool_put(struct pool *pp, void *p)
+{
+ struct pool_item *pi = (struct pool_item *)p;
+
+ if (pi == NULL)
+ return;
+
+ assert(pp->pr_nfree < pp->pr_nitems);
+
+ SLIST_INSERT_HEAD(&pp->pr_free, pi, pi_list);
+ pp->pr_nfree++;
+}
+
+void
+pool_dump(void)
+{
+ struct pool *pp;
+
+ SIMPLEQ_FOREACH(pp, &pool_head, pr_list)
+ printf("%s: %zd items, %zd free\n", pp->pr_name, pp->pr_nitems,
+ pp->pr_nfree);
+}
+#endif /* NOPOOL */
diff --git a/usr.bin/ctfconv/pool.h b/usr.bin/ctfconv/pool.h
new file mode 100644
index 00000000000..234cd915d44
--- /dev/null
+++ b/usr.bin/ctfconv/pool.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Martin Pieuchot
+ *
+ * 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.
+ */
+
+#ifndef _POOL_H_
+#define _POOL_H_
+
+#ifndef NOPOOL
+
+struct pool {
+ SIMPLEQ_ENTRY(pool) pr_list; /* list of all pools */
+ const char *pr_name; /* identifier */
+ SLIST_HEAD(, pool_item) pr_free; /* free list */
+ size_t pr_nmemb; /* # of items per allocation */
+ size_t pr_size; /* size of an item */
+ size_t pr_nitems; /* # of available items */
+ size_t pr_nfree; /* # items on the free list */
+};
+
+void pool_init(struct pool *, const char *, size_t, size_t);
+void *pool_get(struct pool *);
+void pool_put(struct pool *, void *);
+void pool_dump(void);
+
+static inline void *
+pmalloc(struct pool *pp, size_t sz)
+{
+ return pool_get(pp);
+}
+
+static inline void *
+pzalloc(struct pool *pp, size_t sz)
+{
+ char *p;
+
+ p = pool_get(pp);
+ memset(p, 0, sz);
+
+ return p;
+}
+
+static inline void
+pfree(struct pool *pp, void *p)
+{
+ pool_put(pp, p);
+}
+
+#else /* !NOPOOL */
+#define pmalloc(a, b) malloc(b)
+#define pzalloc(a, b) calloc(1, b)
+#define pfree(a, b) free(b)
+#endif /* NOPOOL */
+
+#endif /* _POOL_H_ */
diff --git a/usr.bin/ctfconv/xmalloc.c b/usr.bin/ctfconv/xmalloc.c
new file mode 100644
index 00000000000..f006e2e8c26
--- /dev/null
+++ b/usr.bin/ctfconv/xmalloc.c
@@ -0,0 +1,82 @@
+/* $OpenBSD: xmalloc.c,v 1.1 2017/08/11 14:21:24 mpi Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatalx if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#include <err.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmalloc.h"
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ errx(1, "xmalloc: zero size");
+ ptr = malloc(size);
+ if (ptr == NULL)
+ err(1, "xmalloc: allocating %zu bytes", size);
+ return ptr;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if (size == 0 || nmemb == 0)
+ errx(1, "xcalloc: zero size");
+ ptr = calloc(nmemb, size);
+ if (ptr == NULL)
+ err(1, "xcalloc: allocating %zu * %zu bytes",
+ nmemb, size);
+ return ptr;
+}
+
+void *
+xrealloc(void *ptr, size_t size)
+{
+ return xreallocarray(ptr, 1, size);
+}
+
+void *
+xreallocarray(void *ptr, size_t nmemb, size_t size)
+{
+ void *new_ptr;
+
+ if (nmemb == 0 || size == 0)
+ errx(1, "xreallocarray: zero size");
+ new_ptr = reallocarray(ptr, nmemb, size);
+ if (new_ptr == NULL)
+ err(1, "xreallocarray: allocating %zu * %zu bytes",
+ nmemb, size);
+ return new_ptr;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *cp;
+
+ if ((cp = strdup(str)) == NULL)
+ err(1, "xstrdup");
+ return cp;
+}
diff --git a/usr.bin/ctfconv/xmalloc.h b/usr.bin/ctfconv/xmalloc.h
new file mode 100644
index 00000000000..c856fa18a41
--- /dev/null
+++ b/usr.bin/ctfconv/xmalloc.h
@@ -0,0 +1,28 @@
+/* $OpenBSD: xmalloc.h,v 1.1 2017/08/11 14:21:24 mpi Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * Created: Mon Mar 20 22:09:17 1995 ylo
+ *
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatal if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef XMALLOC_H
+#define XMALLOC_H
+
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+void *xrealloc(void *, size_t);
+void *xreallocarray(void *, size_t, size_t);
+char *xstrdup(const char *);
+
+#endif /* XMALLOC_H */