diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2017-08-11 14:21:25 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2017-08-11 14:21:25 +0000 |
commit | 0f635dd347f73e5f59e4a251b28bc5cc9b42cb00 (patch) | |
tree | ace885b812c0db57a2e2aeb02915ca915cfb4db4 | |
parent | a36ce483bfa196e35c97bf7a746d9bae9f4e7b11 (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/Makefile | 19 | ||||
-rw-r--r-- | usr.bin/ctfconv/ctfconv.1 | 57 | ||||
-rw-r--r-- | usr.bin/ctfconv/ctfconv.c | 452 | ||||
-rwxr-xr-x | usr.bin/ctfconv/ctfstrip | 34 | ||||
-rw-r--r-- | usr.bin/ctfconv/ctfstrip.1 | 51 | ||||
-rw-r--r-- | usr.bin/ctfconv/dw.c | 662 | ||||
-rw-r--r-- | usr.bin/ctfconv/dw.h | 102 | ||||
-rw-r--r-- | usr.bin/ctfconv/dwarf.h | 972 | ||||
-rw-r--r-- | usr.bin/ctfconv/elf.c | 284 | ||||
-rw-r--r-- | usr.bin/ctfconv/generate.c | 461 | ||||
-rw-r--r-- | usr.bin/ctfconv/hash.c | 242 | ||||
-rw-r--r-- | usr.bin/ctfconv/hash.h | 30 | ||||
-rw-r--r-- | usr.bin/ctfconv/itype.h | 102 | ||||
-rw-r--r-- | usr.bin/ctfconv/parse.c | 1308 | ||||
-rw-r--r-- | usr.bin/ctfconv/pool.c | 103 | ||||
-rw-r--r-- | usr.bin/ctfconv/pool.h | 66 | ||||
-rw-r--r-- | usr.bin/ctfconv/xmalloc.c | 82 | ||||
-rw-r--r-- | usr.bin/ctfconv/xmalloc.h | 28 |
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 */ |