]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
ctf: generate CTF information for the kernel
authorNick Alcock <nick.alcock@oracle.com>
Fri, 11 May 2012 15:59:13 +0000 (16:59 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 29 Jun 2015 21:40:25 +0000 (22:40 +0100)
This introdues a new tool, dwarf2ctf, which runs at modpost time whenever any
module or any part of the core kernel is changed, extracting the debugging
information from the kernel build tree, deduplicating it, and emitting it in
Sun's Compact Type Format into gzipped files in a new .ctf directory.  These
files are then linked into the kernel modules as new sections named .SUNW_ctf.

One file is emitted per kernel module, whether builtin or no, as well as one
file for types that are used by no modules, and one file for types shared
between more than one of the other files.  As the built-in modules and shared
types have no module of their own to go into, they are placed in a new
dtrace_ctf.ko module (which serves no other purpose: loading it is useless).
DTrace userspace will no longer start if this module is not present.

Due to the extensive sharing of types, dwarf2ctf must run whenever any object
files at all are changed, and may trigger relinks of modules that you would not
otherwise think had changed.

Standalone modules also have CTF generated for them, but never share types with
any other modules.

Because dwarf2ctf is slow enough to be annoying when running frequent kernel
builds to debug some unrelated problem, a new CONFIG_DT_DISABLE_CTF debugging
configuraton option is added, which suppresses CTF generation entirely.

This commit introduces new kernel build-time dependencies on elfutils and the
new libdtrace-ctf package (shared with dtrace userspace).  No new runtime
dependencies are introduced.

Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Documentation/Changes
Makefile
kernel/dtrace/Kconfig
kernel/dtrace/Makefile
kernel/dtrace/dtrace_ctf.c
scripts/Makefile
scripts/Makefile.modpost
scripts/dwarf2ctf/Makefile [new file with mode: 0644]
scripts/dwarf2ctf/dwarf2ctf.c [new file with mode: 0644]
scripts/move-if-change [new file with mode: 0755]

index 4db80e85cd57251b8179f19bcf210d6cd09aedcc..a567f68b82ad72852c63a5065094ea383af0d91f 100644 (file)
@@ -21,8 +21,8 @@ running a Linux kernel.  Also, not all tools are necessary on all
 systems; obviously, if you don't have any ISDN hardware, for example,
 you probably needn't concern yourself with isdn4k-utils.
 
-o  GNU C                  3.2                     # gcc --version
-o  GNU make               3.80                    # make --version
+o  Gnu C                  3.2                     # gcc --version
+o  Gnu make               3.81                    # make --version
 o  binutils               2.12                    # ld -v
 o  util-linux             2.10o                   # fdformat --version
 o  module-init-tools      0.9.10                  # depmod -V
@@ -44,11 +44,12 @@ o  grub                   0.93                    # grub --version || grub-insta
 o  mcelog                 0.6                     # mcelog --version
 o  iptables               1.4.2                   # iptables -V
 
-Build-time requirements only:
+Build-time requirements only, for DTrace:
 
 o  elfutils               0.152                   # eu-readelf --version
 o  pkg-config             0.16                    # pkg-config --version
 o  glib                   2.x                     # pkg-config --exists glib-2.0 && echo present
+o  libdtrace-ctf          0.3
 
 Kernel compilation
 ==================
@@ -409,3 +410,7 @@ o  <http://www.freedesktop.org/wiki/Software/pkg-config>
 glib (build-time only: 2.x required, not 3.x)
 ----
 o  <http://www.gtk.org/>
+
+libdtrace-ctf (build-time only)
+-------------
+o  <https://oss.oracle.com/git/?p=libdtrace-ctf.git>
index 0c0c33eb0d963f52faf08feb41eda148fac5b5dd..f6325c35d187c7c4a989150165eaf69577f6ba76 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1096,12 +1096,29 @@ all: modules
 # using awk while concatenating to the final file.
 
 PHONY += modules
-modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
+modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin objects.builtin
        $(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
        @$(kecho) '  Building modules, stage 2.';
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
 
+ifdef CONFIG_DTRACE
+# This contains all the object files that are unconditionally built into the
+# kernel, for consumption by dwarf2ctf in Makefile.modpost.
+# This is made doubly annoying by the presence of '.o' files which are actually
+# empty ar archives.
+objects.builtin: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) FORCE
+       @echo $(vmlinux-all) | tr " " "\n" | grep "\.o$$" | xargs file | \
+               grep ELF | cut -d: -f1 > objects.builtin
+       @for archive in $$(echo $(vmlinux-all) | tr " " "\n" | grep "\.a$$"); do \
+           ar t "$$archive" | grep '\.o$$' | \
+                sed "s,^,$${archive%/*}/," >> objects.builtin; \
+       done
+else
+PHONY += objects.builtin
+objects.builtin:
+endif
+
 # Target to prepare building external modules
 PHONY += modules_prepare
 modules_prepare: prepare scripts
@@ -1169,7 +1186,7 @@ modules.builtin: $(vmlinux-dirs:%=%/modules.builtin)
 # make distclean Remove editor backup files, patch leftover files and the like
 
 # Directories & files removed with 'make clean'
-CLEAN_DIRS  += $(MODVERDIR)
+CLEAN_DIRS  += $(MODVERDIR) .ctf
 
 # Directories & files removed with 'make mrproper'
 MRPROPER_DIRS  += include/config usr/include include/generated          \
@@ -1433,7 +1450,8 @@ clean: $(clean-dirs)
                -o -name '*.dwo'  \
                -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
                -o -name '*.symtypes' -o -name 'modules.order' \
-               -o -name modules.builtin -o -name '.tmp_*.o.*' \
+               -o -name 'modules.builtin' -o -name 'objects.builtin' \
+               -o -name '.tmp_*.o.*' \
                -o -name '*.gcno' \) -type f -print | xargs rm -f
 
 # Generate tags for editors
index 44790e5bf8fe07559b8b9d3424f048ce9de36da1..12a35d636f6314c60a3f909438e03361ea999c46 100644 (file)
@@ -84,6 +84,15 @@ config DT_DEBUG_MUTEX
          messages whenever a mutex is locked or unlocked within the DTrace
          code (core and providers).
 
+config DT_DISABLE_CTF
+       bool "Disable CTF generation"
+       default n
+       help
+         Disables the time-consuming generation of CTF information.  This is
+         quite a time-consuming process and may not always be desirable.
+         (The modules will still contain CTF sections, but they will be
+         empty.)
+
 endif  # DT_DEBUG
 
 endif  # DT_CORE
index 57a588f829ab5739bb15b06fef357a0725506b9d..d284c7eab6a9b86d7306101963621b973f8ccb5f 100644 (file)
@@ -8,5 +8,4 @@ ifdef CONFIG_DT_CORE
 obj-y                          += dtrace_os.o dtrace_cpu.o \
                                   dtrace_stubs_x86_64.o sdt_register.o
 obj-m                          += dtrace_ctf.o
-
 endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a462fb062af07e22f4c898526a5fd21d451f0180 100644 (file)
@@ -0,0 +1,13 @@
+/*
+ * FILE:       dtrace_ctf.c
+ * DESCRIPTION:        Dynamic Tracing: CTF container module
+ *
+ * Copyright (C) 2012 Oracle Corporation
+ */
+
+#include <linux/module.h>
+
+MODULE_AUTHOR("Nick Alcock <nick.alcock@oracle.com>");
+MODULE_DESCRIPTION("CTF container module, not for modprobing");
+MODULE_VERSION("v0.1");
+MODULE_LICENSE("GPL");
index 0c2cf750db9d645a8c084629a663a945a2c35d52..df832ed9c0a384725f534398ebb1e59928cb9cd8 100644 (file)
@@ -39,6 +39,7 @@ build_docproc: $(obj)/docproc
 
 subdir-$(CONFIG_MODVERSIONS) += genksyms
 subdir-y                     += mod
+subdir-$(CONFIG_DTRACE)      += dwarf2ctf
 subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_DTC)         += dtc
 subdir-$(CONFIG_GDB_SCRIPTS) += gdb
index 69f0a1417e9a47669f5568af2097f031c247e607..acdf1715acd581867ed1d6a4891a45b20ae81f5c 100644 (file)
@@ -14,7 +14,9 @@
 # 3)  create one <module>.mod.c file pr. module
 # 4)  create one Module.symvers file with CRC for all exported symbols
 # 5) compile all <module>.mod.c files
-# 6) final link of the module to a <module.ko> file
+# 6) generate CTF for the entire kernel, or for the module alone if this is
+#    a build of an external module
+# 7) final link of the module
 
 # Step 3 is used to place certain information in the module's ELF
 # section, including information such as:
 # Step 4 is solely used to allow module versioning in external modules,
 # where the CRC of each module is retrieved from the Module.symvers file.
 
+# We need secondary expansion for 'module-ctfs-modular-prereq', below.
+
+.SECONDEXPANSION:
+
 # KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined
 # symbols in the final module linking stage
 # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
@@ -99,7 +105,6 @@ vmlinux.o: FORCE
 $(symverfile):         __modpost ;
 $(modules:.ko=.mod.c): __modpost ;
 
-
 # Step 5), compile all *.mod.c files
 
 # modname is set to make c_flags define KBUILD_MODNAME
@@ -114,18 +119,107 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
 
 targets += $(modules:.ko=.mod.o)
 
-# Step 6), final link of the modules
+# Step 6), generate CTF for the entire kernel, or for the module alone if this
+# is a build of an external module.
+
+ifndef CONFIG_DT_DISABLE_CTF
+
+# This is quite tricky.  If called for non-external-modules, dwarf2ctf needs to
+# be told about all the built-in objects as well as all the external modules --
+# but Makefile.modpost only knows about the latter.  So the toplevel makefile
+# emits the names of the built-in objects into a temporary file, which is
+# then catted and its contents used as prerequisites by this rule.
+#
+# Because we only run dwarf2ctf once (in non-standalone-module mode), and it
+# depends on a stamp file (because its real targets cannot change unless the
+# object files change, in which case a relink of the appropriate modules is
+# triggered anyway), we don't emit a filename in this output.
+#
+# Out-of-tree module CTF gets its own per-module set of stamp files, since its
+# CTF is rebuilt independently.
+
+ifeq ($(KBUILD_EXTMOD),)
+quiet_cmd_ctf = DWARF2CTF
+      cmd_ctf = scripts/dwarf2ctf/dwarf2ctf objects.builtin modules.builtin $^
+builtins := $(shell cat objects.builtin 2>/dev/null)
+ctf-stamp := .ctf/dtrace-ctf.stamp
+
+# The dtrace CTF module depends on the CTF stamp file, in lieu of the builtin
+# CTF files whose names we cannot determine until it is too late.
+
+kernel/dtrace/dtrace_ctf.ko: .ctf/dtrace-ctf.stamp
+
+else
+quiet_cmd_ctf = DWARF2CTF
+      cmd_ctf = scripts/dwarf2ctf/dwarf2ctf -e $^
+builtins :=
+ctf-stamp := .ctf/$(notdir $(M)-extmod).stamp
+
+endif
+
+# All the modules' CTF likewise depends on the stamp file.
+
+all-module-ctfs = $(addprefix .ctf/,$(notdir $(modules:.ko=.mod.ctf)))
+$(all-module-ctfs): $(ctf-stamp)
+
+# We depend upon a stamp file in lieu of the builtin modules' CTF files, because
+# the names of the generated CTF files for the builtins are so variable.
+# (Standalone modules get their own per-module stamp files.)
+$(ctf-stamp): $(builtins) $(modules:.ko=.o)
+       $(call if_changed,ctf)
+       @shopt -s nullglob; \
+       for name in .ctf/*.ctf.new; do \
+               scripts/move-if-change $$name $${name%.new}; \
+       done
+       @touch $(ctf-stamp)
+
+# Expands to the names of the CTF files to be incorporated into this module.
+# The former is used in prerequisite lists, thanks to secondary expansion.
+
+module-ctfs-modular-prereq = $$(addprefix .ctf/,$$(notdir $$*.mod.ctf))
+module-ctfs-modular = $(addprefix .ctf/,$(notdir $*.mod.ctf))
+
+# Expands to a series of objcopy --add-section arguments to add all
+# necessary CTF files to a module, with appropriate section names.
+# We also take advantage of the opportunity to strip the guaranteed-
+# useless debugging information out of dtrace_ctf.ko at the same time.
+
+module-ctf-flags = $(if $(filter dtrace_ctf.ko,$(notdir $@)), \
+                  --strip-debug \
+                   $(foreach builtin,$(wildcard .ctf/*.builtin.ctf), \
+                             --add-section $(patsubst %.builtin.ctf,.SUNW_ctf.%,$(notdir $(builtin)))=$(builtin)), \
+                   --add-section .SUNW_ctf=$(addprefix .ctf/,$(notdir $(basename $@)).mod.ctf))
+
+# We have to put content in our dummy no-CTF files because --add-section
+# in binutils 2.20 silently fails if asked to add an empty file as a section.
+
+cmd_touch_ctf = @for name in $(filter .ctf/%,$(module-ctfs-modular)); do \
+                   test -f $$name || dd if=/dev/zero of=$$name bs=1 count=1 2>/dev/null; \
+               done
+
+else
+
+module-ctfs-modular-prereq =
+module-ctfs-builtin =
+module-ctf-flags =
+cmd-touch-ctf = @:
+
+endif
+
+# Step 7), final link of the modules
 quiet_cmd_ld_ko_o = LD [M]  $@
       cmd_ld_ko_o = $(LD) -r $(LDFLAGS)                                 \
                              $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-                             -o $@ $(filter-out FORCE,$^)
+                             $(LDFLAGS_$(modname)) -o $@.tmp            \
+                             $(patsubst .ctf/%,,$(filter-out FORCE,$^)) && \
+                    $(OBJCOPY) $(module-ctf-flags) $@.tmp $@ && rm -f $@.tmp
 
-$(modules): %.ko :%.o %.mod.o FORCE
+$(modules): %.ko : %.o %.mod.o $(module-ctfs-modular-prereq) FORCE
+       $(call cmd_touch_ctf)
        $(call if_changed,ld_ko_o)
 
 targets += $(modules)
 
-
 # Add FORCE to the prequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
 
diff --git a/scripts/dwarf2ctf/Makefile b/scripts/dwarf2ctf/Makefile
new file mode 100644 (file)
index 0000000..751c6d0
--- /dev/null
@@ -0,0 +1,5 @@
+hostprogs-y    := dwarf2ctf
+always         := $(hostprogs-y)
+
+HOSTCFLAGS_dwarf2ctf.o := $(shell pkg-config --cflags glib-2.0)
+HOSTLOADLIBES_dwarf2ctf := -ldtrace-ctf -lelf -ldw $(shell pkg-config --libs glib-2.0) -lz
diff --git a/scripts/dwarf2ctf/dwarf2ctf.c b/scripts/dwarf2ctf/dwarf2ctf.c
new file mode 100644 (file)
index 0000000..348466d
--- /dev/null
@@ -0,0 +1,2896 @@
+/*
+ * dwarf2ctf.c: Read in DWARF[23] debugging information from some set of ELF
+ * files, and generate CTF in correspondingly-named files.
+ *
+ * (C) 2011, 2012 Oracle, Inc.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include <libelf.h>
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/libdw.h>
+#include <sys/ctf_api.h>
+#include <glib.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#ifndef __GNUC__
+#define __attribute__((foo))
+#endif
+
+#define __unused__ __attribute__((__unused__))
+
+/*
+ * If non-NULL, tracing is on.
+ */
+static const char *trace;
+
+/*
+ * Trace something.
+ */
+#ifdef DEBUG
+#define dw_ctf_trace(format, ...) if (trace) fprintf(stderr, (format), ## __VA_ARGS__)
+#else
+#define dw_ctf_trace(format, ...)
+#endif
+
+/*
+ * Run dwarf2ctf over a single object file or set thereof.
+ *
+ * starting_argv is the point in the argv array at which the arguments start.
+ */
+static void run(int starting_argv, char *argv[]);
+
+/*
+ * A fully descriptive CTF type ID: both file and type ID in one place.
+ */
+typedef struct ctf_full_id {
+       ctf_file_t *ctf_file;
+       ctf_id_t ctf_id;
+#ifdef DEBUG
+       char module_name[PATH_MAX];
+       char file_name[PATH_MAX];
+#endif
+} ctf_full_id_t;
+
+/*
+ * A mapping from the type ID of a DIE (see type_id()) to ctf_full_id_t's
+ * describing the type with that ID.
+ *
+ * This is used to look up types regardless of which CTF file they may reside
+ * in.  Not the same as a DWARF4 type signature because we must encode scope
+ * information which DWARF4 can encode in its DIE refs.
+ *
+ * (TODO: store a hash of the ID rather than the ID itself, to save memory.
+ * Makes debugging slightly harder though.)
+ */
+static GHashTable *id_to_type;
+
+/*
+ * A mapping from the type ID of a DIE to the name of the module (and thus CTF
+ * table) incorporating that type.  (Modules in this context, and throughout
+ * dwarf2ctf, are DTrace modules: a name without suffix or path.)
+ *
+ * This is used to merge types identical across modules (e.g. those in global
+ * header files).
+ */
+static GHashTable *id_to_module;
+
+/*
+ * A mapping from module name to ctf_file_t *.  The CTF named 'vmlinux' is the
+ * CTF corresponding to the types in always-built-in translation units; the CTF
+ * named 'dtrace_ctf' (not appearing in this mapping) is the CTF corresponding
+ * to types shared between more than one module (even between two currently-
+ * built-in modules: we do not distinguish at this level between built-in
+ * modules and non-built-in modules.)
+ */
+static GHashTable *module_to_ctf_file;
+
+/*
+ * The names of module object files presently built in to the kernel, in the
+ * same format as the module names in tu_to_module.
+ *
+ * If this is NULL, an external module is being processed, and type
+ * deduplication is disabled.
+ */
+static char **builtin_modules;
+static size_t builtin_modules_cnt;
+
+/*
+ * The names of object files that are *always* built in to the kernel.
+ * (If something is in neither this list nor builtin_modules, it is
+ * an external module.)
+ */
+static char **builtin_objects;
+static size_t builtin_objects_cnt;
+
+/*
+ * Populate the builtin_modules and builtin_objects lists from the
+ * objects.builtin and modules.builtin files.
+ */
+static void init_builtin(const char *builtin_objects_file,
+                        const char *builtin_module_file);
+
+/*
+ * A mapping from translation unit name to the name of the module that
+ * translation unit is part of.  Module names have no trailing suffix.
+ *
+ * This table is not complete until the first detect_duplicates pass is over,
+ * but names corresponding to builtin modules and objects are populated early.
+ */
+static GHashTable *tu_to_module;
+
+/*
+ * Populate the tu_to_module hash with names corresponding to builtin modules.
+ */
+static void init_tu_to_modules(void);
+
+/*
+ * Initialize a CTF type table, and possibly fill it with those special types
+ * that appear in CTF but not in DWARF (such as 'void').  (This filling happens
+ * only for the type table named "dtrace_ctf", unless deduplication is turned
+ * off, signified by the builtin_modules list being NULL.)
+ *
+ * If this is a local type table, and deduplication is active, make the global
+ * type table its parent.
+ */
+static ctf_file_t *init_ctf_table(const char *module_name);
+
+/*
+ * A few useful singleton CTF type IDs in the global type table: a void pointer
+ * and a function pointer.  Constructed by init_ctf_table().
+ */
+static ctf_id_t ctf_void_type;
+static ctf_id_t ctf_funcptr_type;
+
+/*
+ * Compute the type ID of a DWARF DIE and return it in a new dynamically-
+ * allocated string.
+ *
+ * Optionally, call a callback with the computed ID once we know it (this is a
+ * recursive process, so the callback can be called multiple times as the ID
+ * is built up).
+ *
+ * An ID of NULL indicates that this DIE has no ID and need not be considered.
+ */
+static char *type_id(Dwarf_Die *die, void (*fun)(Dwarf_Die *die,
+                                                const char *id,
+                                                void *data),
+                    void *data) __attribute__((__warn_unused_result__));
+
+/*
+ * Process a file, calling the dwarf_process function for every type found
+ * therein (even types in functions).  Optionally call tu_init() at the start of
+ * each translation unit, and tu_done() at the end.
+ */
+static void process_file(const char *file_name,
+                        void (*dwarf_process)(const char *module_name,
+                                              const char *file_name,
+                                              Dwarf_Die *die,
+                                              Dwarf_Die *parent_die,
+                                              void *data),
+                        void (*tu_init)(const char *module_name,
+                                        const char *file_name,
+                                        Dwarf_Die *tu_die,
+                                        void *data),
+                        void (*tu_done)(const char *module_name,
+                                        const char *file_name,
+                                        Dwarf_Die *tu_die,
+                                        void *data),
+                        void *data);
+
+/*
+ * process_file() helper, walking over subroutines recursively and picking up
+ * types therein.
+ */
+static void process_tu_func(const char *module_name,
+                           const char *file_name,
+                           Dwarf_Die *tu_die,
+                           Dwarf_Die *die,
+                           void (*dwarf_process)(const char *module_name,
+                                                 const char *file_name,
+                                                 Dwarf_Die *die,
+                                                 Dwarf_Die *parent_die,
+                                                 void *data),
+                           void (*tu_init)(const char *module_name,
+                                           const char *file_name,
+                                           Dwarf_Die *tu_die,
+                                           void *data),
+                           void (*tu_done)(const char *module_name,
+                                           const char *file_name,
+                                           Dwarf_Die *tu_die,
+                                           void *data),
+                           void *data);
+
+/*
+ * Detect duplicate types and determine which CTF file they should be located
+ * in; determine the mapping from translation unit name to module name.
+ */
+static void detect_duplicates(const char *module_name, const char *file_name,
+                             Dwarf_Die *die, Dwarf_Die *parent_die,
+                             void *data);
+
+/*
+ * Second duplication detection pass, checking for opaque/nonopaque structure
+ * alias sharing.
+ */
+static void detect_duplicates_alias_fixup(const char *module_name,
+                                         const char *file_name,
+                                         Dwarf_Die *die,
+                                         Dwarf_Die *parent_die,
+                                         void *data);
+
+/*
+ * Set up state for detect_duplicates().  A tu_init() callback.
+ */
+static void detect_duplicates_init(const char *module_name,
+                                  const char *file_name,
+                                  Dwarf_Die *tu_die,
+                                  void *data);
+
+/*
+ * Free state for detect_duplicates().  A tu_done() callback.
+ */
+static void detect_duplicates_done(const char *module_name,
+                                  const char *file_name,
+                                  Dwarf_Die *tu_die,
+                                  void *data);
+
+/*
+ * Mark a type (optionally, with an already-known ID) as duplicated and located
+ * in the shared CTF table.
+ */
+static void mark_duplicate(Dwarf_Die *die, const char *id,
+                          void *data);
+
+/*
+ * The structure used as the data argument for detect_duplicates().
+ */
+struct detect_duplicates_state {
+       GHashTable *structs_seen;
+};
+
+/*
+ * Construct CTF out of each type.
+ */
+static void construct_ctf(const char *module_name, const char *file_name,
+                         Dwarf_Die *die,
+                         Dwarf_Die *parent_die,
+                         void *unused __unused__);
+
+/*
+ * Write out the CTF files from the module_to_ctf_file hashtable.
+ */
+static void write_types(void);
+
+/*
+ * Construct CTF out of each type and return that type's ID and file
+ */
+static ctf_full_id_t *construct_ctf_id(const char *module_name,
+                                      const char *file_name,
+                                      Dwarf_Die *die,
+                                      Dwarf_Die *parent_die);
+
+/*
+ * Things to do after a CTF recursion step.
+ */
+enum skip_type { SKIP_CONTINUE = 0, SKIP_SKIP, SKIP_ABORT };
+
+/*
+ * Recursively construct CTF for a type and its children.
+ */
+static ctf_id_t recurse_ctf(const char *module_name, const char *file_name,
+                           Dwarf_Die *die, Dwarf_Die *parent_die,
+                           ctf_file_t *ctf, ctf_id_t parent_ctf_id,
+                           int top_level_type, enum skip_type *skip,
+                           const char *id);
+
+/*
+ * Look up a type through its reference: return its ctf_id_t, or
+ * recursively construct it if need be.
+ *
+ * Must be called on a DIE with a type attribute.
+ */
+static ctf_id_t lookup_ctf_type(const char *module_name, const char *file_name,
+                               Dwarf_Die *die, ctf_file_t *ctf,
+                               const char *locerrstr);
+
+/*
+ * Assemble a given DIE and its children into CTF in some fashion, returning the
+ * ID of the top-level piece of generated CTF (only relevant for aggregates).
+ *
+ * The parent_ctf_id is the ID of the CTF entity that was or is being generated
+ * from the enclosing DWARF DIE, or -1 if none.  The parent_die is the parent of
+ * the current DWARF DIE, and is always populated (even if just with the CU's
+ * DIE).  The parent_ctf_id is always in the same CTF file as the ctf_id, just
+ * as the parent DWARF DIE is always in the same DWARF CU: this is lexical
+ * scope, not dynamic, so referenced types themselves located at the top level
+ * have the CU as their parent.
+ *
+ * Returning an error value (see below) indicates that no CTF was generated from
+ * this DWARF DIE.
+ *
+ * Setting skip to SKIP_ABORT indicates that the translation of this entity
+ * failed, and the entire top-level type of which it is a part should be
+ * skipped.  Setting it to SKIP_SKIP indicates that this entity does not need to
+ * be translated (perhaps because it already exists), so recursion into
+ * sub-entities can be skipped, but translation of the containing type should
+ * continue.  Setting it to SKIP_CONTINUE indicates no error.
+ *
+ * Recurse_ctf calls these functions repeatedly for every child of the requested
+ * DIE: the CTF ID eventually returned is whatever ID is returned by the last
+ * such function, and parent_ctf_id is repeatedly replaced with the ID returned
+ * by the last assembly function.  Thus, assembly functions that augment an
+ * already-present ctf_id should return parent_ctf_id: assembly functions that
+ * wrap it in a new ctf_id referring to the parent_ctf_id should return the new
+ * ID.  (Assembly functions should never entirely disregard the parent_ctf_id.)
+ */
+typedef ctf_id_t (*ctf_assembly_fun)(const char *module_name,
+                                    const char *file_name,
+                                    Dwarf_Die *die,
+                                    Dwarf_Die *parent_die,
+                                    ctf_file_t *ctf,
+                                    ctf_id_t parent_ctf_id,
+                                    const char *locerrstr,
+                                    int top_level_type,
+                                    enum skip_type *skip);
+
+#define ASSEMBLY_FUN(name)                                           \
+       static ctf_id_t assemble_ctf_##name(const char *module_name, \
+                                           const char *file_name,    \
+                                           Dwarf_Die *die,           \
+                                           Dwarf_Die *parent_die,    \
+                                           ctf_file_t *ctf,          \
+                                           ctf_id_t parent_ctf_id,   \
+                                           const char *locerrstr,    \
+                                           int top_level_type,       \
+                                           enum skip_type *skip)
+
+/*
+ * Defined assembly functions.
+ */
+ASSEMBLY_FUN(base);
+ASSEMBLY_FUN(array);
+ASSEMBLY_FUN(array_dimension);
+ASSEMBLY_FUN(cvr_qual);
+ASSEMBLY_FUN(enumeration);
+ASSEMBLY_FUN(enumerator);
+ASSEMBLY_FUN(pointer);
+ASSEMBLY_FUN(struct_union);
+ASSEMBLY_FUN(su_member);
+ASSEMBLY_FUN(typedef);
+
+/*
+ * Error return values from CTF assembly functions.  These differ only in that
+ * recurse_ctf() reports the ctf_errmsg() if CTF_NO_ERROR_REPORTED is returned,
+ * but says nothing in the CTF_ERROR_REPORTED case.
+ */
+
+#define CTF_NO_ERROR_REPORTED CTF_ERR
+#define CTF_ERROR_REPORTED (-2L)
+
+/*
+ * The total number of type errors encountered.
+ */
+static long num_errors;
+
+/*
+ * A mapping from DW_TAG_* to functions which assemble this DW_TAG_* and
+ * possibly its children into the passed CTF.  This table is not used
+ * directly, but rather assembled into a lookup table.
+ */
+static struct assembly_tab_t
+{
+       int tag;
+       ctf_assembly_fun fun;
+} assembly_tab_init[] = {{ DW_TAG_base_type, assemble_ctf_base },
+                        { DW_TAG_array_type, assemble_ctf_array },
+                        { DW_TAG_subrange_type, assemble_ctf_array_dimension },
+                        { DW_TAG_const_type, assemble_ctf_cvr_qual },
+                        { DW_TAG_restrict_type, assemble_ctf_cvr_qual },
+                        { DW_TAG_enumeration_type, assemble_ctf_enumeration },
+                        { DW_TAG_enumerator, assemble_ctf_enumerator },
+                        { DW_TAG_pointer_type, assemble_ctf_pointer },
+                        { DW_TAG_structure_type, assemble_ctf_struct_union },
+                        { DW_TAG_union_type, assemble_ctf_struct_union },
+                        { DW_TAG_member, assemble_ctf_su_member },
+                        { DW_TAG_typedef, assemble_ctf_typedef },
+                        { DW_TAG_volatile_type, assemble_ctf_cvr_qual },
+                        { 0, NULL }};
+
+/*
+ * The CTF assembly lookup table, in constructed form.
+ */
+static ctf_assembly_fun *assembly_tab;
+static size_t assembly_len;
+
+/*
+ * Populate the assembly_tab from the assembly_tab_init.
+ */
+static void init_assembly_tab(void);
+
+/*
+ * A mapping from sizeof() to CTF type encoding.
+ */
+struct type_encoding_t {
+       size_t size;
+       int ctf_encoding;
+};
+
+/*
+ * Given a type encoding table, and a size, return the CTF encoding for that
+ * type, or 0 if none.
+ */
+static int find_ctf_encoding(struct type_encoding_t *type_tab, size_t size);
+
+/*
+ * Count the number of members of a DWARF aggregate.
+ */
+static long count_dwarf_members(Dwarf_Die *die);
+
+/*
+ * Count the number of members of a CTF aggregate.
+ */
+static long count_ctf_members(ctf_file_t *fp, ctf_id_t souid);
+
+/*
+ * Increment said count.
+ */
+static int count_ctf_members_internal(const char *name, ctf_id_t member,
+                                     ulong_t offset, void *count);
+
+/*
+ * Wrap up dwfl_new() complexities.
+ */
+static Dwfl *private_dwfl_new(const char *file_name);
+
+/*
+ * The converse of private_dwfl_new().
+ */
+static void private_dwfl_free(Dwfl *dwfl);
+
+/*
+ * Given a DIE that may contain a type attribute, look up the target of that
+ * attribute and return it, or NULL if none.
+ */
+
+static Dwarf_Die *private_dwarf_type(Dwarf_Die *die, Dwarf_Die *target_die);
+
+/*
+ * A string appender working on dynamic strings.
+ */
+static char *str_append(char *s, const char *append)
+       __attribute__((__warn_unused_result__));
+
+/*
+ * A vararg string appender.
+ */
+static char *str_appendn(char *s, ...) __attribute__((__warn_unused_result__));
+
+/*
+ * An error-checking strdup().
+ */
+static char *xstrdup(const char *s) __attribute__((__nonnull__,
+                                                  __warn_unused_result__,
+                                                  __malloc__));
+
+/*
+ * Figure out the (pathless, suffixless) module name for a given module file (.o
+ * or .ko), and return it in a new dynamically allocated string.
+ */
+static char *fn_to_module(const char *file_name);
+
+/*
+ * Stub libdwfl callback, use only the ELF handle passed in.
+ */
+
+static int no_debuginfo(Dwfl_Module *mod __unused__,
+                       void **userdata __unused__,
+                       const char *modname __unused__,
+                       Dwarf_Addr base __unused__,
+                       const char *file_name __unused__,
+                       const char *debuglink_file __unused__,
+                       GElf_Word debuglink_crc __unused__,
+                       char **debuginfo_file_name __unused__);
+
+/*
+ * Trivial wrapper, avoid an incompatible pointer type warning.
+ */
+static void private_ctf_free(void *ctf_file);
+
+/* Initialization.  */
+
+int main(int argc, char *argv[])
+{
+       char *builtin_objects_file;
+       char *builtin_module_file;
+       int starting_argv = 2;
+
+       trace = getenv("DWARF2CTF_TRACE");
+
+       if ((argc == 2 && (strcmp(argv[1], "-e") != 0)) || (argc < 3)) {
+               fprintf(stderr, "Syntax: dwarf2ctf objects.builtin "
+                       "modules.builtin vmlinux.o module.o...,\n");
+               fprintf(stderr, "        or dwarf2ctf -e module.o ... "
+                       "for (inefficient)\n");
+               fprintf(stderr, "external module use\n");
+               exit(1);
+       }
+
+       elf_version(EV_CURRENT);
+
+       if (elf_errno()) {
+               fprintf(stderr, "Version synchronization fault: %s\n",
+                       elf_errmsg(elf_errno()));
+               exit(1);
+       }
+
+       init_assembly_tab();
+
+       /*
+        * When not building an external module, we run over all the arguments
+        * at once, deduplicating them.  In external-module mode, we act as if
+        * independently invoked with every argument.
+        */
+       if (strcmp(argv[1], "-e") != 0) {
+               builtin_objects_file = argv[1];
+               builtin_module_file = argv[2];
+               starting_argv = 3;
+               init_builtin(builtin_objects_file, builtin_module_file);
+
+               run(starting_argv, argv);
+       } else {
+               char **name;
+
+               for (name = &argv[starting_argv]; *name; name++) {
+                       char *one_argv[2];
+                       one_argv[0] = *name;
+                       one_argv[1] = NULL;
+
+                       run(0, one_argv);
+               }
+       }
+
+       if (num_errors > 0)
+               fprintf(stderr, "%li CTF construction errors.\n", num_errors);
+
+       return 0;
+}
+
+/*
+ * Run dwarf2ctf over a single object file or set thereof.
+ *
+ * starting_argv is the point in the argv array at which the arguments start.
+ */
+static void run(int starting_argv, char *argv[])
+{
+       char **name;
+
+       /*
+        * Create all the hashes, assemble the translation unit->module list for
+        * builtin modules, and create the shared CTF file if deduplicating.
+        */
+
+       id_to_type = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                          free, free);
+       id_to_module = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            free, free);
+       tu_to_module = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            free, free);
+       module_to_ctf_file = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                  free, private_ctf_free);
+
+       dw_ctf_trace("Initializing...\n");
+       init_tu_to_modules();
+
+       if (builtin_modules != NULL)
+               init_ctf_table("dtrace_ctf");
+
+       /*
+        * First, determine which types are referenced by more than one
+        * translation unit, and construct the mapping from translation unit to
+        * non-builtin module name.
+        *
+        * The first pass detects duplicated types, without considering
+        * opaque/transparent structure/union aliasing.
+        *
+        * We must do this even when deduplication is disabled, because we need
+        * the TU->module-name mapping, even if in this case it is trivial.
+        */
+       dw_ctf_trace("Duplicate elimination.\n");
+       for (name = &argv[starting_argv]; *name; name++) {
+               struct detect_duplicates_state state;
+
+               process_file(*name, detect_duplicates, detect_duplicates_init,
+                            detect_duplicates_done, &state);
+       }
+
+       if (builtin_modules != NULL) {
+               /*
+                * The second pass recognizes that opaque structures must be
+                * shared if the transparent equivalents are, and vice versa.
+                */
+               dw_ctf_trace("Duplicate elimination, pass 2.\n");
+               for (name = &argv[starting_argv]; *name; name++) {
+                       struct detect_duplicates_state state;
+
+                       process_file(*name, detect_duplicates_alias_fixup,
+                                    detect_duplicates_init,
+                                    detect_duplicates_done, &state);
+               }
+
+               /*
+                * The third pass handles shared structures which reference
+                * types whose base type is a structure marked shared in pass 2:
+                * those intermediate types are not marked shared yet.
+                */
+               dw_ctf_trace("Duplicate elimination, pass 3.\n");
+               for (name = &argv[starting_argv]; *name; name++) {
+                       struct detect_duplicates_state state;
+
+                       process_file(*name, detect_duplicates,
+                                    detect_duplicates_init,
+                                    detect_duplicates_done, &state);
+               }
+       }
+
+       /*
+        * Now construct CTF out of the types, writing out the .ctf files as we
+        * go.
+        */
+       dw_ctf_trace("CTF construction.\n");
+       for (name = &argv[starting_argv]; *name; name++)
+               process_file(*name, construct_ctf, NULL, NULL, NULL);
+
+       /*
+        * Finally, emit the types into their .ctf files, and generate the
+        * necessary linker scripts.
+        */
+       dw_ctf_trace("Writeout.\n");
+       write_types();
+
+       g_hash_table_destroy(id_to_type);
+       g_hash_table_destroy(id_to_module);
+       g_hash_table_destroy(tu_to_module);
+       g_hash_table_destroy(module_to_ctf_file);
+}
+
+/*
+ * Populate the builtin_modules and builtin_objects lists from the
+ * objects.builtin and modules.builtin file.
+ */
+static void init_builtin(const char *builtin_objects_file,
+                        const char *builtin_module_file)
+{
+       FILE *f;
+       char *line = NULL;
+       size_t line_size = 0;
+
+       if ((f = fopen(builtin_objects_file, "r")) == NULL) {
+               fprintf(stderr, "Cannot open builtin objects file %s: "
+                       "%s\n", builtin_objects_file, strerror(errno));
+               exit(1);
+       }
+
+       /*
+        * This needs no massaging other than linefeed removal, just reading and
+        * stashing.
+        */
+
+       while (getline(&line, &line_size, f) >= 0) {
+               size_t len = strlen(line);
+
+               if (len == 0)
+                       continue;
+
+               if (line[len-1] == '\n')
+                       line[len-1] = '\0';
+
+               builtin_objects = realloc(builtin_objects,
+                                         ++builtin_objects_cnt *
+                                         sizeof (char *));
+
+               if (builtin_objects == NULL) {
+                       fprintf(stderr, "Out of memory reading %s",
+                               builtin_objects_file);
+                       exit(1);
+               }
+
+               builtin_objects[builtin_objects_cnt-1] = xstrdup(line);
+       }
+
+       if (ferror(f)) {
+               fprintf(stderr, "Error reading from %s: %s\n",
+                       builtin_objects_file, strerror(errno));
+               exit(1);
+       }
+
+       fclose(f);
+
+       if ((f = fopen(builtin_module_file, "r")) == NULL) {
+               fprintf(stderr, "Cannot open builtin module file %s: "
+                       "%s\n", builtin_module_file, strerror(errno));
+               exit(1);
+       }
+
+       /*
+        * Read in, realloc()ing and assigning as we go, stripping off the
+        * leading path element, if any, and transforming the suffix into .o
+        * from .ko.  Any elements that don't have files corresponding to them
+        * elicit a warning.
+        */
+
+       while (getline(&line, &line_size, f) >= 0) {
+               char *first_slash;
+               char *last_dot;
+
+               if (line[0] == '\0')
+                       continue;
+
+               if ((first_slash = strchr(line, '/')) != NULL)
+                       first_slash++;
+               else
+                       first_slash = line;
+
+               last_dot = strrchr(line, '.');
+               if ((last_dot != NULL) &&
+                   ((strcmp(last_dot, ".ko") == 0) ||
+                    (strcmp(last_dot, ".ko\n") == 0))) {
+                       strcpy(last_dot, ".o");
+               }
+
+               if (access(first_slash, R_OK) == 0) {
+                       builtin_modules = realloc(builtin_modules,
+                                                 ++builtin_modules_cnt *
+                                                 sizeof (char *));
+
+                       if (builtin_modules == NULL) {
+                               fprintf(stderr, "Out of memory reading %s",
+                                       builtin_module_file);
+                               exit(1);
+                       }
+
+                       builtin_modules[builtin_modules_cnt-1] = xstrdup(first_slash);
+               } else {
+                       fprintf(stderr, "%s population: module %s is not "
+                               "readable.\n", builtin_module_file,
+                               first_slash);
+               }
+       }
+       free(line);
+
+       if (ferror(f)) {
+               fprintf(stderr, "Error reading from %s: %s\n",
+                       builtin_module_file, strerror(errno));
+               exit(1);
+       }
+
+       fclose(f);
+}
+
+/*
+ * Translate the assembly lookup table into an array.
+ */
+static void init_assembly_tab(void)
+{
+       struct assembly_tab_t *walk;
+
+       for (walk = assembly_tab_init; walk->fun != NULL; walk++) {
+               if (assembly_len < walk->tag)
+                       assembly_len = walk->tag;
+       }
+
+       if ((assembly_tab = calloc(sizeof (ctf_assembly_fun *),
+                                  assembly_len + 1)) == NULL) {
+               fprintf(stderr, "Out of memory allocating assembly table\n");
+               exit(1);
+       }
+
+       for (walk = assembly_tab_init; walk->fun != NULL; walk++)
+               assembly_tab[walk->tag] = walk->fun;
+}
+
+/*
+ * Initialize a CTF type table, and possibly fill it with those special types
+ * that appear in CTF but not in DWARF (such as 'void').  (This filling happens
+ * only for the type table named "dtrace_ctf", unless deduplication is turned
+ * off, signified by the builtin_modules list being NULL.)
+ *
+ * If this is a local type table, and deduplication is active, make the global
+ * type table its parent.
+ */
+static ctf_file_t *init_ctf_table(const char *module_name)
+{
+       ctf_file_t *ctf_file;
+       int ctf_err;
+
+       if ((ctf_file = ctf_create(&ctf_err)) == NULL) {
+               fprintf(stderr, "Cannot create CTF file: %s\n",
+                       strerror(ctf_err));
+               exit(1);
+       }
+       g_hash_table_replace(module_to_ctf_file, xstrdup(module_name),
+                            ctf_file);
+
+       dw_ctf_trace("Initializing module: %s\n", module_name);
+       if ((strcmp(module_name, "dtrace_ctf") == 0) ||
+           (builtin_modules == NULL)) {
+               ctf_encoding_t void_encoding = { CTF_INT_SIGNED, 0, 0 };
+               ctf_encoding_t int_encoding = { CTF_INT_SIGNED, 0,
+                                               sizeof (int) };
+               ctf_id_t int_type;
+               ctf_id_t func_type;
+               ctf_funcinfo_t func_info;
+
+               /*
+                * Global types module, or deduplication is disabled.  Add a
+                * type for 'void *' to point to, and a type for the return
+                * value of pointers to functions: then add the (single,
+                * universal) pointer-to-function value.
+                */
+               ctf_void_type = ctf_add_integer(ctf_file, CTF_ADD_ROOT,
+                                               "void", &void_encoding);
+               int_type = ctf_add_integer(ctf_file, CTF_ADD_ROOT, "int",
+                                          &int_encoding);
+
+               func_info.ctc_return = int_type;
+               func_info.ctc_argc = 0;
+               func_info.ctc_flags = 0;
+               func_type = ctf_add_function(ctf_file, CTF_ADD_ROOT,
+                                            &func_info, NULL);
+               ctf_funcptr_type = ctf_add_pointer(ctf_file, CTF_ADD_ROOT,
+                                                  func_type);
+       } else {
+               /*
+                * Local types module with deduplication enabled: point the
+                * parent at the global CTF file, which must exist by this
+                * point.
+                */
+               if (ctf_import(ctf_file,
+                              g_hash_table_lookup(module_to_ctf_file,
+                                                  "dtrace_ctf")) < 0) {
+                       fprintf(stderr, "Cannot set parent of CTF file for "
+                               "module %s: %s\n", module_name,
+                               ctf_errmsg(ctf_errno(ctf_file)));
+                       exit(1);
+               }
+       }
+
+       dw_ctf_trace("Created CTF file for module %s: %p\n",
+                    module_name, ctf_file);
+
+       return ctf_file;
+}
+
+/* DWARF walkers.  */
+
+/*
+ * Compute the mapping from translation unit name to module name for built-in
+ * modules and always-built-in object files.
+ */
+static void init_tu_to_modules(void)
+{
+       size_t i;
+
+       /*
+        * Always-built-in object files map from their TU name to 'vmlinux'.
+        */
+       for (i = 0; i < builtin_objects_cnt; i++) {
+               /*
+                * Walk over the translation units in this module and construct
+                * mappings from each TU to "vmlinux".
+                */
+
+               Dwfl *dwfl = private_dwfl_new(builtin_objects[i]);
+               Dwarf_Die *tu = NULL;
+               Dwarf_Addr junk;
+
+               while ((tu = dwfl_nextcu(dwfl, tu, &junk)) != NULL) {
+                       const char *tu_name = dwarf_diename(tu);
+
+                       if ((tu_name != NULL) &&
+                           (dwarf_tag(tu) == DW_TAG_compile_unit))
+                               g_hash_table_replace(tu_to_module,
+                                                    xstrdup(tu_name),
+                                                    xstrdup("vmlinux"));
+               }
+               private_dwfl_free(dwfl);
+       }
+
+       /*
+        * Built-in modules map from their TU name to their module name.
+        */
+
+       for (i = 0; i < builtin_modules_cnt; i++) {
+               char *module_name = fn_to_module(builtin_modules[i]);
+
+               /*
+                * Walk over the translation units in this module and construct
+                * mappings from each TU to the module name.
+                */
+
+               Dwfl *dwfl = private_dwfl_new(builtin_modules[i]);
+               Dwarf_Die *tu = NULL;
+               Dwarf_Addr junk;
+
+               while ((tu = dwfl_nextcu(dwfl, tu, &junk)) != NULL) {
+                       const char *tu_name = dwarf_diename(tu);
+
+                       if ((tu_name != NULL) &&
+                           (dwarf_tag(tu) == DW_TAG_compile_unit))
+                               g_hash_table_replace(tu_to_module,
+                                                    xstrdup(tu_name),
+                                                    xstrdup(module_name));
+               }
+               private_dwfl_free(dwfl);
+               free(module_name);
+       }
+}
+
+/*
+ * Type ID computation.
+ *
+ * A type ID is a constant, recursively-constructed string describing a given
+ * DWARF DIE in such a way that any DWARF file containing the same type will
+ * have the same type ID.
+ *
+ * Optionally, call a callback with the computed ID once we know it (this is a
+ * recursive process, so the callback can be called multiple times as the ID is
+ * built up).
+ *
+ * An ID of NULL indicates that this DIE has no ID and need not be considered.
+ */
+static char *type_id(Dwarf_Die *die, void (*fun)(Dwarf_Die *die,
+                                                const char *id,
+                                                void *data),
+                    void *data)
+{
+       char *id = NULL;
+       int no_type_id = 0;
+       Dwarf_Die type_die;
+
+       /*
+        * The ID of a null pointer is NULL.
+        */
+       if (die == NULL)
+               return NULL;
+
+       /*
+        * The ID of a function pointer is '//fp//', as a special case,
+        * with no location, ever.
+        */
+       if (dwarf_tag(die) == DW_TAG_subroutine_type) {
+               id = xstrdup("//fp//");
+               if (fun)
+                       fun(die, id, data);
+               return id;
+       }
+
+       /*
+        * If we have a type DIE, generate it first.
+        *
+        * If we don't have a type DIE, note the location of this DIE, providing
+        * scoping information for all types based upon this one.  Location
+        * elements are separated by //, an element impossible in a Linux path.
+        */
+       if (dwarf_tag(die) != DW_TAG_base_type)
+               id = type_id(private_dwarf_type(die, &type_die), fun, data);
+
+       if (id == NULL) {
+               const char *decl_file_name = dwarf_decl_file(die);
+               int decl_line_num;
+
+               no_type_id = 1;
+               if (decl_file_name != NULL) {
+                       char abspath[PATH_MAX];
+                       if (realpath(decl_file_name, abspath) != NULL)
+                               id = xstrdup(abspath);
+                       else
+                               id = xstrdup(decl_file_name);
+               }
+               id = str_append(id, "//");
+
+               if (dwarf_decl_line(die, &decl_line_num) >= 0) {
+                       char line_num[21];  /* bigger than 2^64's digit count */
+                       snprintf(line_num, sizeof (line_num), "%i",
+                                decl_line_num);
+                       id = str_append(id, line_num);
+               }
+               id = str_append(id, "//");
+       }
+
+       /*
+        * We implement this via a switch statement, rather than a jump table
+        * like the assembly_tab, simply because most cases are so small that
+        * splitting them into separate functions would do more harm than good
+        * to readability.
+        */
+       switch (dwarf_tag(die)) {
+       case DW_TAG_base_type:
+               id = str_append(id, dwarf_diename(die));
+               break;
+       case DW_TAG_enumeration_type:
+               id = str_append(id, "enum ");
+               id = str_append(id, dwarf_diename(die));
+               break;
+       case DW_TAG_structure_type:
+               id = str_append(id, "struct ");
+               id = str_append(id, dwarf_diename(die));
+               break;
+       case DW_TAG_union_type:
+               id = str_append(id, "union ");
+               id = str_append(id, dwarf_diename(die));
+               break;
+       case DW_TAG_typedef:
+               id = str_append(id, " typedef ");
+               id = str_append(id, dwarf_diename(die));
+               break;
+       case DW_TAG_const_type:
+               id = str_append(id, " ");
+               id = str_append(id, "const");
+               break;
+       case DW_TAG_restrict_type:
+               id = str_append(id, " ");
+               id = str_append(id, "restrict");
+               break;
+       case DW_TAG_volatile_type:
+               id = str_append(id, " ");
+               id = str_append(id, "volatile");
+               break;
+       case DW_TAG_pointer_type:
+               if (no_type_id)
+                       id = str_append(id, "void");
+               id = str_append(id, " *");
+               break;
+
+       case DW_TAG_array_type: {
+               /*
+                * No explicit notation: all done per-dimension: so recurse to
+                * those.
+                */
+
+               int sib_ret;
+               int dimens = 0;
+               Dwarf_Die dim_die;
+
+               switch (dwarf_child(die, &dim_die)) {
+               case -1:
+                       fprintf(stderr, "Corrupt DWARF: Cannot get array "
+                               "dimensions: %s\n", dwarf_errmsg(dwarf_errno()));
+                       exit(1);
+               case 1: /* No dimensions.  */
+                       id = str_append(id, "[]");
+                       break;
+               default:
+                       dimens = 1;
+               }
+
+               if (!dimens)
+                       break;
+
+               while ((sib_ret = dwarf_siblingof(&dim_die, &dim_die)) == 0) {
+                       char *sub_id = type_id(&dim_die, fun, data);
+                       id = str_append(id, " ");
+                       id = str_append(id, sub_id);
+                       free(sub_id);
+               }
+
+               if (sib_ret == -1) {
+                       fprintf(stderr, "Corrupt DWARF: Cannot get array "
+                               "dimensions: %s\n", dwarf_errmsg(dwarf_errno()));
+                       exit(1);
+               }
+               break;
+       }
+       case DW_TAG_subrange_type: {
+               Dwarf_Attribute nelem_attr;
+               Dwarf_Word nelems;
+
+               char elems[21];             /* bigger than 2^64's digit count */
+
+               id = str_append(id, "[");
+               if ((dwarf_hasattr(die, DW_AT_type)) &&
+                   (((dwarf_attr(die, DW_AT_upper_bound, &nelem_attr)) != NULL) ||
+                    ((dwarf_attr(die, DW_AT_count, &nelem_attr))) != NULL)) {
+                       char *sub_id = type_id(private_dwarf_type(die, &type_die),
+                                              fun, data);
+
+                       id = str_append(id, sub_id);
+                       id = str_append(id, " ");
+                       free(sub_id);
+
+                       dwarf_formudata(&nelem_attr, &nelems);
+                       snprintf(elems, sizeof (elems), "%li", nelems);
+                       id = str_append(id, elems);
+               }
+               id = str_append(id, "]");
+               break;
+       }
+       }
+
+       if (fun)
+               fun(die, id, data);
+
+       return id;
+}
+
+/*
+ * Process a file, calling the dwarf_process function for every top-level type
+ * found therein.  Optionally call tu_init() at the start of each translation
+ * unit, and tu_done() at the end.
+ */
+static void process_file(const char *file_name,
+                        void (*dwarf_process)(const char *module_name,
+                                              const char *file_name,
+                                              Dwarf_Die *die,
+                                              Dwarf_Die *parent_die,
+                                              void *data),
+                        void (*tu_init)(const char *module_name,
+                                        const char *file_name,
+                                        Dwarf_Die *tu_die,
+                                        void *data),
+                        void (*tu_done)(const char *module_name,
+                                        const char *file_name,
+                                        Dwarf_Die *tu_die,
+                                        void *data),
+                        void *data)
+{
+       const char *err;
+       char *fn_module_name = fn_to_module(file_name);
+       const char *module_name = fn_module_name;
+
+       Dwfl *dwfl = private_dwfl_new(file_name);
+       GHashTable *seen_before = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       free, free);
+       Dwarf_Die *tu_die = NULL;
+       Dwarf_Addr junk;
+
+       if (seen_before == NULL) {
+               fprintf(stderr, "Out of memory creating seen_before hash\n");
+               exit(1);
+       }
+
+       while ((tu_die = dwfl_nextcu(dwfl, tu_die, &junk)) != NULL) {
+               const char *tu_name;
+
+               if (dwarf_tag(tu_die) != DW_TAG_compile_unit) {
+                       err = "Malformed DWARF: non-compile_unit at top level";
+                       goto fail;
+               }
+
+               tu_name = dwarf_diename(tu_die);
+
+               dw_ctf_trace("Processing %s\n", tu_name);
+
+               /*
+                * If we have seen this TU before, skip it.  We assume that
+                * types in multiple identical TUs are always entirely
+                * identical.  This lets us skip cases where the same object
+                * file is linked in multiple places without scanning every type
+                * in it.  (Note: this may be inaccurate if a TU is built
+                * repeatedly with different #defines in force.  I hope this
+                * cannot happen, but if it does, a workaround a-la libtool is
+                * simple: rename or symlink the TU for such repeated builds.)
+                *
+                * Otherwise, note the name of the module to which this TU maps,
+                * if it is not already known: otherwise, extract that name.
+                */
+               if (g_hash_table_lookup_extended(seen_before, tu_name,
+                                                NULL, NULL))
+                       continue;
+
+               g_hash_table_replace(seen_before, xstrdup(tu_name), NULL);
+
+               if (!g_hash_table_lookup(tu_to_module, tu_name))
+                       g_hash_table_replace(tu_to_module,
+                                            xstrdup(tu_name),
+                                            xstrdup(fn_module_name));
+               else
+                       module_name = g_hash_table_lookup(tu_to_module, tu_name);
+
+               /*
+                * We are only interested in top-level definitions within each
+                * TU.
+                */
+               Dwarf_Die die;
+
+               switch (dwarf_child(tu_die, &die)) {
+               case -1:
+                       err = "fetch first child of TU";
+                       goto fail;
+               case 1: /* No DIEs at all in this TU */
+                       continue;
+               default: /* Child DIEs exist.  */
+                       break;
+               }
+
+
+               if (tu_init != NULL)
+                       tu_init(module_name, file_name, tu_die, data);
+
+               process_tu_func(module_name, file_name, tu_die, &die,
+                               dwarf_process, tu_init, tu_done, data);
+
+               if (tu_done != NULL)
+                       tu_done(module_name, file_name, tu_die, data);
+       }
+
+       free(fn_module_name);
+       private_dwfl_free(dwfl);
+       g_hash_table_destroy(seen_before);
+
+       return;
+
+ fail:
+       fprintf(stderr, "Cannot %s for %s: %s\n", err, module_name,
+               dwarf_errmsg(dwarf_errno()));
+       exit(1);
+}
+
+/*
+ * process_file() helper, walking over subroutines and their contained blocks
+ * recursively and picking up types therein.
+ */
+static void process_tu_func(const char *module_name,
+                           const char *file_name,
+                           Dwarf_Die *tu_die,
+                           Dwarf_Die *die,
+                           void (*dwarf_process)(const char *module_name,
+                                                 const char *file_name,
+                                                 Dwarf_Die *die,
+                                                 Dwarf_Die *parent_die,
+                                                 void *data),
+                           void (*tu_init)(const char *module_name,
+                                           const char *file_name,
+                                           Dwarf_Die *tu_die,
+                                           void *data),
+                           void (*tu_done)(const char *module_name,
+                                           const char *file_name,
+                                           Dwarf_Die *tu_die,
+                                           void *data),
+                           void *data)
+{
+       const char *err;
+       int sib_ret;
+
+       /*
+        * We are only interested in definitions for which we can
+        * (eventually) emit CTF: call the processing function for all
+        * such.  Recurse into subprograms to catch type declarations there as
+        * well, since there may be definitions of aggregates referred to
+        * outside this function only opaquely.
+        */
+       do {
+               if ((dwarf_tag(die) <= assembly_len) &&
+                   (assembly_tab[dwarf_tag(die)] != NULL)) {
+                       dwarf_process(module_name, file_name, die,
+                                     tu_die, data);
+               }
+               if ((dwarf_tag(die) == DW_TAG_subprogram) ||
+                   (dwarf_tag(die) == DW_TAG_lexical_block)) {
+                       Dwarf_Die subroutine_die;
+
+                       switch (dwarf_child(die, &subroutine_die)) {
+                       case -1:
+                               err = "fetch first child of subroutine";
+                               goto fail;
+                       case 1: /* No DIEs at all in this subroutine */
+                               continue;
+                       default: /* Child DIEs exist.  */
+                               break;
+                       }
+                       process_tu_func(module_name, file_name, tu_die,
+                                       &subroutine_die, dwarf_process, tu_init,
+                                       tu_done, data);
+               }
+       } while ((sib_ret = dwarf_siblingof(die, die)) == 0);
+
+       if (sib_ret == -1) {
+               err = "fetch sibling";
+               goto fail;
+       }
+
+       return;
+ fail:
+       fprintf(stderr, "Cannot %s for %s: %s\n", err, module_name,
+               dwarf_errmsg(dwarf_errno()));
+       exit(1);
+}
+
+/*
+ * Set up state for detect_duplicates().  A tu_init() callback.
+ */
+static void detect_duplicates_init(const char *module_name,
+                                  const char *file_name,
+                                  Dwarf_Die *tu_die,
+                                  void *data)
+{
+       struct detect_duplicates_state *state = data;
+
+       state->structs_seen = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                   free, free);
+}
+
+/*
+ * Free state for detect_duplicates().  A tu_done() callback.
+ */
+static void detect_duplicates_done(const char *module_name,
+                                  const char *file_name,
+                                  Dwarf_Die *tu_die,
+                                  void *data)
+{
+       struct detect_duplicates_state *state = data;
+
+       g_hash_table_destroy(state->structs_seen);
+       state->structs_seen = NULL;
+}
+
+/*
+ * Duplicate detection.
+ *
+ * Scan for duplicate types.  A duplicate type is defined as any type which
+ * appears in more than one module, or, more precisely, any type for which a
+ * type with the same ID already exists in another module.
+ *
+ * This pass also constructs the id_to_module table, so is essential even when
+ * deduplication is disabled (though then, it need be run only once.)
+ */
+
+static void detect_duplicates(const char *module_name,
+                             const char *file_name,
+                             Dwarf_Die *die,
+                             Dwarf_Die *parent_die,
+                             void *data)
+{
+       char *id = type_id(die, NULL, NULL);
+
+       /*
+        * If a DWARF-4 type signature is found, abort.  While we can support
+        * DWARF-4 eventually, support in elfutils is insufficiently robust for
+        * now (elfutils 0.152).
+        */
+       if (dwarf_hasattr(die, DW_AT_type)) {
+               Dwarf_Attribute type_attr;
+
+               if ((dwarf_attr(die, DW_AT_type, &type_attr) != NULL) &&
+                   (dwarf_whatform(&type_attr) == DW_FORM_ref_sig8)) {
+                       fprintf(stderr, "sorry, not yet implemented: %s "
+                               "contains DWARF-4 debugging information.\n",
+                               file_name);
+                       exit(1);
+               }
+       }
+
+
+       /*
+        * If we know of a single module incorporating this type, and it is not
+        * the same as the module we are currently in, then this type is
+        * duplicated across modules and belongs in the global type table.
+        * (This means that duplicated types are repeatedly so marked: this
+        * is unavoidable, because pass 3 requires re-marking structures that
+        * have already been marked, to pick up unmarked intermediate types.)
+        */
+
+       const char *existing_type_module;
+
+       existing_type_module = g_hash_table_lookup(id_to_module, id);
+
+       if (existing_type_module != NULL) {
+               if ((strcmp(existing_type_module, module_name) != 0) &&
+                   (builtin_modules != NULL))
+                       mark_duplicate(die, NULL, data);
+
+               /*
+                * A duplicated type, but in the same module, or deduplication
+                * is disabled, so id_to_module is already correct.  (When
+                * deduplication is disabled, we will be running with only one
+                * module at a time, and id_to_module will be a trivial
+                * mapping.)
+                */
+               free(id);
+               return;
+       }
+
+       /*
+        * Record that we have seen this type in this module.
+        */
+
+       g_hash_table_replace(id_to_module, id, xstrdup(module_name));
+}
+
+/*
+ * Mark a type as duplicated and located in the shared CTF table.  Recursive,
+ * via the type_id() callback mechanism.
+ */
+static void mark_duplicate(Dwarf_Die *die, const char *id,
+                          void *data)
+{
+       struct detect_duplicates_state *state = data;
+       const char *existing_module;
+
+       /*
+        * Base case.  Trigger type_id for its recursive callback, throwing the
+        * result away.
+        */
+       if (id == NULL) {
+               free(type_id(die, mark_duplicate, state));
+               return;
+       }
+
+       existing_module = g_hash_table_lookup(id_to_module, id);
+
+       if ((existing_module == NULL) ||
+           (strcmp(existing_module, "dtrace_ctf") != 0))
+               g_hash_table_replace(id_to_module, xstrdup(id),
+                                    xstrdup("dtrace_ctf"));
+
+       /*
+        * If this is a structure or union, mark its members as duplicates too.
+        * Do this even if we've seen this structure before, as this instance of
+        * the structure may have more members than the last we saw.  However,
+        * if we have seen this structure before *in this translation unit*,
+        * skip it, to avoid infinite recursion in mutually referential
+        * structures.
+        */
+       if ((dwarf_tag(die) == DW_TAG_structure_type) ||
+           (dwarf_tag(die) == DW_TAG_union_type)) {
+               Dwarf_Die child;
+
+               if (g_hash_table_lookup_extended(state->structs_seen, id,
+                                                NULL, NULL))
+                       return;
+               g_hash_table_replace(state->structs_seen, xstrdup(id), NULL);
+
+               switch (dwarf_child(die, &child)) {
+               case -1:
+                       goto fail;
+               case 1: /* No DIEs at all in this aggregate */
+                       return;
+               }
+
+               /*
+                * We are only interested in children of type DW_TAG_member.
+                */
+               int sib_ret;
+
+               do {
+                       free(type_id(&child, mark_duplicate, state));
+               } while ((sib_ret = dwarf_siblingof(&child, &child)) == 0);
+
+               if (sib_ret == -1)
+                       goto fail;
+       }
+
+       return;
+
+ fail:
+       fprintf(stderr, "Cannot mark aggregate %s members as duplicated: %s\n",
+               dwarf_diename(die), dwarf_errmsg(dwarf_errno()));
+       exit(1);
+}
+
+/*
+ * Detect duplicates alias fixup pass.  Once the first pass is complete, we
+ * may have marked an opaque 'struct foo' for sharing but not caught the
+ * non-opaque instance, because no users of the non-opaque instance appeared
+ * in the DWARF after the opaque copy was detected as a duplicate.
+ *
+ * This detects such cases, and marks their members as duplicates too.
+ */
+static void detect_duplicates_alias_fixup(const char *module_name,
+                                         const char *file_name,
+                                         Dwarf_Die *die,
+                                         Dwarf_Die *parent_die,
+                                         void *data)
+{
+       int transparent_shared = 0;
+       int opaque_shared = 0;
+
+       /*
+        * We only do anything for structures and unions that are not opaque.
+        * For these, we compute the corresponding opaque variant and check to
+        * see if either is marked shared, then mark both as shared if either
+        * is.
+        */
+
+       if ((dwarf_tag(die) != DW_TAG_structure_type) &&
+           (dwarf_tag(die) != DW_TAG_union_type))
+               return;
+
+       char *id = type_id(die, NULL, NULL);
+
+       if (strncmp(id, "////", strlen("////")) == 0) {
+               free(id);
+               return;
+       }
+
+       char *opaque_id = xstrdup("////");
+
+       if (dwarf_tag(die) == DW_TAG_structure_type)
+               opaque_id = str_append(opaque_id, "struct ");
+       else
+               opaque_id = str_append(opaque_id, "union ");
+
+       opaque_id = str_append(opaque_id, dwarf_diename(die));
+
+       const char *transparent_module = g_hash_table_lookup(id_to_module,
+                                                            id);
+       const char *opaque_module = g_hash_table_lookup(id_to_module,
+                                                       opaque_id);
+
+       transparent_shared = ((transparent_module != NULL) &&
+                             (strcmp(transparent_module, "dtrace_ctf") == 0));
+
+       opaque_shared = ((opaque_module != NULL) &&
+                        (strcmp(opaque_module, "dtrace_ctf") == 0));
+
+       if (opaque_shared && !transparent_shared)
+               mark_duplicate(die, NULL, data);
+
+       /*
+        * We don't have the opaque type's DIE, so we can't use
+        * mark_duplicate().  Instead, do it by hand: this is simple as member
+        * recursion is guaranteed not to be required for an opaque type.
+        */
+       if (transparent_shared && !opaque_shared)
+               g_hash_table_replace(id_to_module, xstrdup(opaque_id),
+                                    xstrdup("dtrace_ctf"));
+
+       free(id);
+       free(opaque_id);
+}
+
+/*
+ * Type assembly.
+ *
+ * Given a DWARF DIE corresponding to a top-level type, call the appropriate
+ * construction function, passing it the appropriate ctf_file_t, constructing it
+ * if necessary, and stashing them in the module_to_ctf_file hash.  Return the
+ * ctf_id_t of this type.
+ */
+static ctf_full_id_t *construct_ctf_id(const char *module_name,
+                                      const char *file_name,
+                                      Dwarf_Die *die,
+                                      Dwarf_Die *parent_die)
+{
+       char *id = type_id(die, NULL, NULL);
+       char *ctf_module;
+       ctf_file_t *ctf;
+
+       dw_ctf_trace("    %p: %s: looking up %s: %s\n", &id, module_name,
+                    dwarf_diename(die), id);
+       /*
+        * Make sure this type does not already exist.  (Recursive chasing for
+        * referenced types can lead to construct_ctf() being called on them
+        * more than once.)
+        */
+       ctf_full_id_t *ctf_id;
+       if ((ctf_id = g_hash_table_lookup(id_to_type, id)) != NULL) {
+               dw_ctf_trace("    %p: found in module %s, file %s\n", &id,
+                            ctf_id->module_name, ctf_id->file_name);
+               free(id);
+               return ctf_id;
+       }
+
+       /*
+        * Create the CTF file for this type, if it does not exist.  Verify that
+        * the duplicate-detection pass scanned this type, and that this is
+        * either the current module or the shared CTF module.
+        */
+
+       ctf_module = g_hash_table_lookup(id_to_module, id);
+
+       if (ctf_module == NULL) {
+               fprintf(stderr, "Internal error: within file %s, module %s, "
+                       "type at DIE offset %lx with ID %s was not already "
+                       "noted by detect_duplicates().\n", file_name,
+                       module_name, (unsigned long) dwarf_dieoffset(die), id);
+               exit(1);
+       }
+
+       if ((strcmp(ctf_module, module_name) != 0) &&
+           (strcmp(ctf_module, "dtrace_ctf") != 0)) {
+               fprintf(stderr, "Internal error: within file %s, module %s, "
+                       "type at DIE offset %lx with ID %s is in a different "
+                       "non-shared module, %s.\n", file_name, module_name,
+                       (unsigned long) dwarf_dieoffset(die), id, ctf_module);
+               exit(1);
+       }
+
+       ctf = g_hash_table_lookup(module_to_ctf_file, ctf_module);
+
+       if (ctf == NULL) {
+               ctf = init_ctf_table(ctf_module);
+               dw_ctf_trace("%p: %s: initialized CTF file %p\n", &id,
+                            module_name, ctf);
+       }
+
+       /*
+        * Construct the CTF, then insert the top-level CTF entity into the
+        * id->type hash so that references from other types can find it, and
+        * update the CTF container.  If conversion failed, roll back all
+        * changes made since the last successful call to this function.
+        *
+        * NOTE: references within DWARF to non-top-level types will currently
+        * fail, but I'm not sure if these can exist.  (The type ID
+        * representation implicitly assumes that they cannot.)
+        */
+
+       enum skip_type skip = SKIP_CONTINUE;
+       dw_ctf_trace("%p: into recurse_ctf() for %s\n", &id, id);
+       ctf_id_t this_ctf_id = recurse_ctf(module_name, file_name, die,
+                                          parent_die, ctf, -1, 1, &skip,
+                                          id);
+       dw_ctf_trace("%p: out of recurse_ctf()\n", &id);
+
+       ctf_id = malloc(sizeof (struct ctf_full_id));
+       if (ctf_id == NULL) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       if (skip != SKIP_ABORT) {
+               if (ctf_update(ctf) < 0) {
+                       fprintf(stderr, "Cannot update CTF file: %s\n",
+                               ctf_errmsg(ctf_errno(ctf)));
+                       exit(1);
+               }
+
+               ctf_id->ctf_file = ctf;
+               ctf_id->ctf_id = this_ctf_id;
+#ifdef DEBUG
+               strcpy(ctf_id->module_name, module_name);
+               strcpy(ctf_id->file_name, file_name);
+#endif
+
+               g_hash_table_replace(id_to_type, id, ctf_id);
+
+               dw_ctf_trace("    %lx: %s: new type added, CTF ID %p:%i\n",
+                            (unsigned long) dwarf_dieoffset(die), id,
+                            ctf_id->ctf_file, (int) ctf_id->ctf_id);
+       } else {
+               /*
+                * Failure.  Remove the type from the id_to_type mapping, if it
+                * is there, and discard any added types from the CTF.
+                */
+
+               if (ctf_discard(ctf) < 0) {
+                       fprintf(stderr, "Cannot discard from CTF file on "
+                               "conversion failure or skip: %s\n",
+                               ctf_errmsg(ctf_errno(ctf)));
+                       exit(1);
+               }
+
+               free(ctf_id);
+               ctf_id = NULL;
+
+               g_hash_table_remove(id_to_type, id);
+               free(id);
+
+               dw_ctf_trace("    %p: (failure)\n", &id);
+       }
+
+       return ctf_id;
+}
+
+/*
+ * Given a DWARF DIE corresponding to a top-level type, or to an aggregate
+ * member, and the ctf_file_t where it is to be placed, call the appropriate
+ * construction function to place it and (for aggregates) its siblings there,
+ * recursing to handle contained aggregates.
+ *
+ * Note: id is only defined when top_level_type is 1.  (We never use it
+ * in other situations, and computing it is quite expensive.)
+ */
+static ctf_id_t recurse_ctf(const char *module_name, const char *file_name,
+                           Dwarf_Die *die, Dwarf_Die *parent_die,
+                           ctf_file_t *ctf, ctf_id_t parent_ctf_id,
+                           int top_level_type, enum skip_type *skip,
+                           const char *id)
+{
+       int sib_ret = 0;
+       ctf_id_t this_ctf_id;
+
+       do {
+               const char *id_name;
+               const char *decl_file_name = dwarf_decl_file(die);
+               int decl_line_num;
+               char locerrstr[1024];
+
+               /*
+                * Compute a name for our current location, for error messages.
+                * (The type representation could be used, but is likely to be
+                * hard for users to comprehend, and should we move to a hashed
+                * representation would be entirely useless for this purpose.)
+                */
+
+               if ((decl_file_name == NULL) ||
+                   (dwarf_decl_line(die, &decl_line_num) < 0)) {
+                       decl_file_name = "global";
+                       decl_line_num = 0;
+               }
+
+               id_name = dwarf_diename(die);
+               if (id_name == NULL)
+                       id_name = "(unnamed type)";
+
+               snprintf(locerrstr, sizeof (locerrstr), "%s:%i:%s",
+                        decl_file_name, decl_line_num, id_name);
+
+               dw_ctf_trace("Working over %s:%s:%s:%lx with CTF file %p\n",
+                            module_name, file_name,
+                            dwarf_diename(die)==NULL?"NULL":dwarf_diename(die),
+                            (unsigned long) dwarf_dieoffset(die), ctf);
+
+               /*
+                * Only process a given node, or its children, if we know how to
+                * do so.
+                */
+               if ((dwarf_tag(die) >= assembly_len) ||
+                   (assembly_tab[dwarf_tag(die)] == NULL)) {
+                       fprintf(stderr, "%s:%i: warning: skipping identifier "
+                               "%s with unknown DWARF tag %lx.\n",
+                               decl_file_name, decl_line_num, id_name,
+                               (unsigned long) dwarf_tag(die));
+                       return -1;
+               }
+
+               *skip = SKIP_CONTINUE;
+
+               this_ctf_id = assembly_tab[dwarf_tag(die)](module_name,
+                                                          file_name,
+                                                          die, parent_die,
+                                                          ctf,
+                                                          parent_ctf_id,
+                                                          locerrstr,
+                                                          top_level_type,
+                                                          skip);
+
+               if (this_ctf_id < 0) {
+                       if ((this_ctf_id == CTF_NO_ERROR_REPORTED) &&
+                           (ctf_errno(ctf) != 0))
+                               fprintf(stderr, "%s: CTF error in assembly of "
+                                       "item with tag %i: %s\n", locerrstr,
+                                       dwarf_tag(die),
+                                       ctf_errmsg(ctf_errno(ctf)));
+
+                       num_errors++;
+#ifdef DEBUG
+                       exit(1);
+#endif
+                       *skip = SKIP_ABORT;
+               }
+
+               /*
+                * Add newly-added non-skipped top-level structure or union CTF
+                * IDs to the type table at once.  This allows circular type
+                * references via pointers referenced in structure/union member
+                * DIEs to be looked up correctly.
+                */
+               if (top_level_type && (*skip == SKIP_CONTINUE) &&
+                   ((dwarf_tag(die) == DW_TAG_structure_type) ||
+                    (dwarf_tag(die) == DW_TAG_union_type))) {
+                       ctf_full_id_t full_ctf_id = { ctf, this_ctf_id };
+                       ctf_full_id_t *ctf_id;
+
+#ifdef DEBUG
+                       strcpy(full_ctf_id.module_name, module_name);
+                       strcpy(full_ctf_id.file_name, file_name);
+#endif
+
+                       if ((ctf_id = malloc(sizeof (ctf_full_id_t))) == NULL) {
+                               fprintf(stderr, "Out of memory allocating "
+                                       "type ID\n");
+                               exit(1);
+                       }
+
+                       dw_ctf_trace("    recurse_ctf(): immediate addition of "
+                                    "%s in module %s, file %s\n", id,
+                                    module_name, file_name);
+                       *ctf_id = full_ctf_id;
+
+                       g_hash_table_replace(id_to_type, xstrdup(id), ctf_id);
+
+                       /*
+                        * This prevents a clean rollback on error from deeply
+                        * nested types: some unreachable types may persist.
+                        * Probably unfixable wihtout a radical rewrite of
+                        * libctf (a good idea anyway, ctf_update() is terribly
+                        * slow).
+                        */
+                       if (ctf_update(ctf) < 0) {
+                               fprintf(stderr, "Cannot update CTF file: %s\n",
+                                       ctf_errmsg(ctf_errno(ctf)));
+                               exit(1);
+                       }
+               }
+
+               /*
+                * Recurse to handle contained DIEs.
+                */
+
+               if ((dwarf_haschildren(die)) && (*skip == SKIP_CONTINUE)) {
+                       Dwarf_Die child_die;
+
+                       if (dwarf_child(die, &child_die) < 0) {
+                               fprintf(stderr, "%s: Cannot recurse to "
+                                       "DWARF DIE children: %s\n", locerrstr,
+                                       dwarf_errmsg(dwarf_errno()));
+                               exit(1);
+                       }
+
+                       recurse_ctf(module_name, file_name, &child_die, die,
+                                   ctf, this_ctf_id, 0, skip, NULL);
+               }
+
+               /*
+                * Walk siblings of non-top-level types only: the sibling walk
+                * of top-level types is done by process_file(), so that
+                * construct_ctf() gets a chance to put each such type in the
+                * right CTF file.
+                */
+       } while (*skip != SKIP_ABORT && !top_level_type &&
+                (sib_ret = dwarf_siblingof(die, die)) == 0);
+
+       if (sib_ret == -1) {
+               fprintf(stderr, "In module %s, failure walking the sibling "
+                       "list: %s\n", module_name, dwarf_errmsg(dwarf_errno()));
+               exit(1);
+       }
+
+       return this_ctf_id;
+}
+
+/*
+ * Calls construct_ctf_id() and throws the ID away.  Used as a process_file()
+ * callback.
+ */
+static void construct_ctf(const char *module_name, const char *file_name,
+                         Dwarf_Die *die, Dwarf_Die *parent_die,
+                         void *unused __unused__)
+{
+       construct_ctf_id(module_name, file_name, die, parent_die);
+}
+
+/*
+ * Look up a type through its reference: return its ctf_id, or recursively
+ * construct it if need be.
+ */
+static ctf_id_t lookup_ctf_type(const char *module_name, const char *file_name,
+                               Dwarf_Die *die, ctf_file_t *ctf,
+                               const char *locerrstr)
+{
+       Dwarf_Die tmp;
+       Dwarf_Die *type_die = private_dwarf_type(die, &tmp);
+       Dwarf_Die cu_die;
+       ctf_full_id_t *type_ref;
+
+       if (type_die == NULL)
+               return ctf_void_type;
+
+       /*
+        * Pointers to functions are special cases: there is only one of
+        * these in CTF, and it applies to all functions, so we can use a
+        * global singleton.
+        */
+
+       if (dwarf_tag(type_die) == DW_TAG_subroutine_type)
+               return ctf_funcptr_type;
+
+       /*
+        * Look up or construct CTF for this type.
+        */
+
+       dwarf_diecu(type_die, &cu_die, NULL, NULL);
+
+       dw_ctf_trace("    %s: Looking up dependent type for type %s "
+                    "at module %s, file %s named %s\n", locerrstr,
+                    dwarf_diename(die), module_name, file_name,
+                    dwarf_diename(type_die));
+
+       type_ref = construct_ctf_id(module_name, file_name,
+                                   type_die, &cu_die);
+
+       /*
+        * Pass any error back up.
+        */
+
+       if (type_ref == NULL) {
+               fprintf(stderr, "%s: type lookup failed.\n", locerrstr);
+               return -1;
+       }
+
+       if ((type_ref->ctf_file != ctf) &&
+           type_ref->ctf_file != g_hash_table_lookup(module_to_ctf_file,
+                                                     "dtrace_ctf")) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: Internal error: lookup found in "
+                       "different file: %s/%s versus %s/%s.\n", locerrstr,
+                       type_ref->module_name, type_ref->file_name, module_name,
+                       file_name);
+#else
+               fprintf(stderr, "%s: Internal error: lookup found in different "
+                       "file.\n", locerrstr);
+#endif
+               exit(1);
+       }
+
+       return type_ref->ctf_id;
+}
+
+/* Assembly functions.  */
+
+#define CTF_DW_ENFORCE(attribute) do {                                                 \
+               if (!dwarf_hasattr(die, (DW_AT_##attribute))) {                 \
+                       fprintf(stderr, "%s: %s: %lx: skipping type, %s attribute not " \
+                               "present.\n", locerrstr, __func__,              \
+                               (unsigned long) dwarf_dieoffset(die), #attribute); \
+                       *skip = SKIP_ABORT;                                     \
+                       return CTF_ERROR_REPORTED;                              \
+               }                                                               \
+       } while (0)
+
+#define CTF_DW_ENFORCE_NOT(attribute) do {                                     \
+               if (dwarf_hasattr(die, (DW_AT_##attribute))) {                  \
+                       fprintf(stderr, "%s: %s: %lx: skipping type, %s attribute not " \
+                               "supported.\n", locerrstr, __func__,            \
+                               (unsigned long) dwarf_dieoffset(die), #attribute); \
+                       *skip = SKIP_ABORT;                                     \
+                       return CTF_ERROR_REPORTED;                              \
+               }                                                               \
+       } while (0)
+
+/*
+ * Assemble base types.
+ */
+static ctf_id_t assemble_ctf_base(const char *module_name,
+                                 const char *file_name, Dwarf_Die *die,
+                                 Dwarf_Die *parent_die, ctf_file_t *ctf,
+                                 ctf_id_t parent_ctf_id, const char *locerrstr,
+                                 int top_level_type, enum skip_type *skip)
+{
+       typedef ctf_id_t (*ctf_add_fun)(ctf_file_t *, uint_t,
+                                       const char *, const ctf_encoding_t *);
+
+       const char *name = dwarf_diename(die);
+       Dwarf_Attribute encoding_attr, size_attr;
+       Dwarf_Word encoding, size;
+       ctf_add_fun ctf_add_func;
+       ctf_encoding_t ctf_encoding;
+       size_t encoding_search;
+
+       struct dwarf_encoding_tab {
+               Dwarf_Word encoding;
+               ctf_add_fun func;
+               uint_t encoding_fixed;
+               struct type_encoding_t *size_lookup;
+       };
+
+       struct type_encoding_t float_encoding[] =
+               {{sizeof (float), CTF_FP_SINGLE },
+                {sizeof (double), CTF_FP_DOUBLE },
+                {sizeof (long double), CTF_FP_LDOUBLE },
+                {0, 0}};
+
+       struct type_encoding_t float_cplx_encoding[] =
+               {{sizeof (float), CTF_FP_CPLX },
+                {sizeof (double), CTF_FP_DCPLX },
+                {sizeof (long double), CTF_FP_LDCPLX },
+                {0, 0}};
+
+       struct type_encoding_t float_imagry_encoding[] =
+               {{sizeof (float), CTF_FP_IMAGRY },
+                {sizeof (double), CTF_FP_DIMAGRY },
+                {sizeof (long double), CTF_FP_LDIMAGRY },
+                {0, 0}};
+
+       struct dwarf_encoding_tab all_encodings[] =
+               {{DW_ATE_boolean, ctf_add_integer, CTF_INT_BOOL, NULL},
+                {DW_ATE_signed, ctf_add_integer, CTF_INT_SIGNED, NULL},
+                {DW_ATE_signed_char, ctf_add_integer,
+                 CTF_INT_SIGNED | CTF_INT_CHAR, NULL},
+                {DW_ATE_unsigned, ctf_add_integer, 0, NULL},
+                {DW_ATE_unsigned_char, ctf_add_integer, CTF_INT_CHAR, NULL},
+                {DW_ATE_float, ctf_add_float, 0, float_encoding},
+                {DW_ATE_complex_float, ctf_add_float, 0, float_cplx_encoding},
+                {DW_ATE_imaginary_float, ctf_add_float, 0,
+                 float_imagry_encoding},
+                {0, 0, 0, 0}};
+
+       CTF_DW_ENFORCE(name);
+       CTF_DW_ENFORCE(encoding);
+       CTF_DW_ENFORCE(byte_size);
+       CTF_DW_ENFORCE_NOT(bit_size);
+       CTF_DW_ENFORCE_NOT(endianity);
+
+       dwarf_attr(die, DW_AT_encoding, &encoding_attr);
+       dwarf_formudata(&encoding_attr, &encoding);
+
+       if ((dwarf_attr(die, DW_AT_byte_size, &size_attr) == NULL) ||
+           (dwarf_formudata(&size_attr, &size) < 0)) {
+               fprintf(stderr, "%s: skipping type, cannot get size: %s\n",
+                       locerrstr, dwarf_errmsg(dwarf_errno()));
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       for (encoding_search = 0; all_encodings[encoding_search].func != 0;
+            encoding_search++) {
+               if (all_encodings[encoding_search].encoding == encoding) {
+                       ctf_add_func = all_encodings[encoding_search].func;
+                       if (all_encodings[encoding_search].size_lookup != NULL)
+                               ctf_encoding.cte_format =
+                                       find_ctf_encoding(all_encodings[encoding_search].size_lookup,
+                                                         size);
+                       else
+                               ctf_encoding.cte_format =
+                                       all_encodings[encoding_search].encoding_fixed;
+                       break;
+               }
+       }
+
+       if (all_encodings[encoding_search].func == 0) {
+               fprintf(stderr, "%s: skipping type, base type %li "
+                       "not yet implemented.\n", locerrstr, (long) encoding);
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+       ctf_encoding.cte_offset = 0;
+       ctf_encoding.cte_bits = size * 8;
+
+       return ctf_add_func(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                           name, &ctf_encoding);
+}
+
+/*
+ * Assemble pointer types.
+ */
+static ctf_id_t assemble_ctf_pointer(const char *module_name,
+                                    const char *file_name,
+                                    Dwarf_Die *die, Dwarf_Die *parent_die,
+                                    ctf_file_t *ctf, ctf_id_t parent_ctf_id,
+                                    const char *locerrstr, int top_level_type,
+                                    enum skip_type *skip)
+{
+       ctf_id_t type_ref;
+
+       if ((type_ref = lookup_ctf_type(module_name, file_name, die, ctf,
+                                       locerrstr)) < 0) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       /*
+        * Pointers to functions are all the same type in CTF: don't bother
+        * adding it over again.
+        */
+       if (type_ref == ctf_funcptr_type)
+               return type_ref;
+
+       return ctf_add_pointer(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                              type_ref);
+}
+
+/*
+ * Assemble array types.  This function looks up the array type, but does not do
+ * any array construction: that is left to assemble_ctf_array_dimension().
+ */
+static ctf_id_t assemble_ctf_array(const char *module_name,
+                                  const char *file_name, Dwarf_Die *die,
+                                  Dwarf_Die *parent_die, ctf_file_t *ctf,
+                                  ctf_id_t parent_ctf_id,
+                                  const char *locerrstr, int top_level_type,
+                                  enum skip_type *skip)
+{
+       ctf_id_t type_ref;
+
+       CTF_DW_ENFORCE_NOT(name);
+       CTF_DW_ENFORCE_NOT(ordering);
+       CTF_DW_ENFORCE_NOT(bit_stride);
+       CTF_DW_ENFORCE_NOT(byte_stride);
+
+       if ((type_ref = lookup_ctf_type(module_name, file_name, die, ctf,
+                                       locerrstr)) < 0) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+       return type_ref;
+}
+
+/*
+ * Assemble an array dimension, wrapping an array round the parent_ctf_type and
+ * replacing it.
+ */
+static ctf_id_t assemble_ctf_array_dimension(const char *module_name,
+                                            const char *file_name,
+                                            Dwarf_Die *die,
+                                            Dwarf_Die *parent_die,
+                                            ctf_file_t *ctf,
+                                            ctf_id_t parent_ctf_id,
+                                            const char *locerrstr,
+                                            int top_level_type,
+                                            enum skip_type *skip)
+{
+       ctf_arinfo_t arinfo;
+       Dwarf_Attribute nelem_attr;
+       Dwarf_Word nelems;
+       long flexible_array = 0;
+
+       CTF_DW_ENFORCE_NOT(bit_size);
+       CTF_DW_ENFORCE_NOT(byte_size);
+       CTF_DW_ENFORCE_NOT(bit_stride);
+       CTF_DW_ENFORCE_NOT(byte_stride);
+       CTF_DW_ENFORCE_NOT(lower_bound);
+       CTF_DW_ENFORCE_NOT(threads_scaled);
+
+       arinfo.ctr_contents = parent_ctf_id;
+
+       if ((arinfo.ctr_index = lookup_ctf_type(module_name, file_name, die,
+                                               ctf, locerrstr)) < 0) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       /*
+        * Force number of elements to zero for a flexible array member:
+        * otherwise, count them.
+        */
+
+       if ((!dwarf_hasattr(die, DW_AT_type)) ||
+           (((dwarf_attr(die, DW_AT_upper_bound, &nelem_attr)) == NULL) &&
+            ((dwarf_attr(die, DW_AT_count, &nelem_attr))) == NULL)) {
+                       arinfo.ctr_nelems = 0;
+                       flexible_array = 1;
+               }
+
+       /*
+        * Since we don't support lower_bound, strides, or offset-1-based
+        * languages, we can treat upper_bound and count identically.
+        *
+        * FIXME: userspace tracing probably has to support these.
+        */
+       if (!flexible_array) {
+               dwarf_formudata(&nelem_attr, &nelems);
+               arinfo.ctr_nelems = nelems;
+       }
+
+       /*
+        * For each array dimension, construct an appropriate array of the
+        * type-so-far.
+        */
+
+       return ctf_add_array(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                            &arinfo);
+}
+
+/*
+ * Assemble an enumeration.
+ */
+static ctf_id_t assemble_ctf_enumeration(const char *module_name,
+                                        const char *file_name,
+                                        Dwarf_Die *die,
+                                        Dwarf_Die *parent_die,
+                                        ctf_file_t *ctf,
+                                        ctf_id_t parent_ctf_id,
+                                        const char *locerrstr,
+                                        int top_level_type,
+                                        enum skip_type *skip)
+{
+       const char *name = dwarf_diename(die);
+
+       return ctf_add_enum(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                           name);
+}
+
+/*
+ * Assemble an enumeration value.
+ */
+static ctf_id_t assemble_ctf_enumerator(const char *module_name,
+                                       const char *file_name,
+                                       Dwarf_Die *die,
+                                       Dwarf_Die *parent_die,
+                                       ctf_file_t *ctf,
+                                       ctf_id_t parent_ctf_id,
+                                       const char *locerrstr,
+                                       int top_level_type,
+                                       enum skip_type *skip)
+{
+       const char *name = dwarf_diename(die);
+       Dwarf_Attribute value_attr;
+       Dwarf_Word value;
+       int err;
+
+       CTF_DW_ENFORCE(name);
+       CTF_DW_ENFORCE(const_value);
+       CTF_DW_ENFORCE_NOT(bit_stride);
+       CTF_DW_ENFORCE_NOT(byte_stride);
+
+       dwarf_attr(die, DW_AT_const_value, &value_attr);
+       dwarf_formudata(&value_attr, &value);
+
+       err = ctf_add_enumerator(ctf, parent_ctf_id, name, value);
+
+       if (err != 0)
+               return err;
+
+       return parent_ctf_id;
+}
+
+/*
+ * Assemble a typedef.
+ */
+static ctf_id_t assemble_ctf_typedef(const char *module_name,
+                                    const char *file_name,
+                                    Dwarf_Die *die,
+                                    Dwarf_Die *parent_die,
+                                    ctf_file_t *ctf,
+                                    ctf_id_t parent_ctf_id,
+                                    const char *locerrstr,
+                                    int top_level_type,
+                                    enum skip_type *skip)
+{
+       const char *name = dwarf_diename(die);
+       ctf_id_t type_ref;
+
+       CTF_DW_ENFORCE(name);
+
+       if ((type_ref = lookup_ctf_type(module_name, file_name, die, ctf,
+                                       locerrstr)) < 0) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       return ctf_add_typedef(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                              name, type_ref);
+}
+
+/*
+ * Assemble a const/volatile/restrict qualifier.
+ */
+static ctf_id_t assemble_ctf_cvr_qual(const char *module_name,
+                                     const char *file_name,
+                                     Dwarf_Die *die,
+                                     Dwarf_Die *parent_die,
+                                     ctf_file_t *ctf,
+                                     ctf_id_t parent_ctf_id,
+                                     const char *locerrstr,
+                                     int top_level_type,
+                                     enum skip_type *skip)
+{
+       ctf_id_t (*ctf_cvr_fun)(ctf_file_t *, uint_t, ctf_id_t);
+       ctf_id_t type_ref;
+
+       switch (dwarf_tag(die)) {
+       case DW_TAG_const_type: ctf_cvr_fun = ctf_add_const; break;
+       case DW_TAG_volatile_type: ctf_cvr_fun = ctf_add_volatile; break;
+       case DW_TAG_restrict_type: ctf_cvr_fun = ctf_add_restrict; break;
+       default:
+               fprintf(stderr, "%s: internal error: assemble_ctf_cvr_qual() "
+                       "called with non-const/volatile/restrict: %i\n",
+                       locerrstr, dwarf_tag(die));
+               exit(1);
+       }
+
+       if ((type_ref = lookup_ctf_type(module_name, file_name, die, ctf,
+                                       locerrstr)) < 0) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       return ctf_cvr_fun(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                          type_ref);
+}
+
+/*
+ * Assemble a structure or union type.  This assembles only the type itself, not
+ * its constituent members: that is done by assemble_ctf_su_member().
+ *
+ * We assume that if a structure or union type is discovered with more members
+ * than an earlier-discovered type, that it is compatible with that earlier type
+ * and a superset of it.
+ */
+static ctf_id_t assemble_ctf_struct_union(const char *module_name,
+                                         const char *file_name,
+                                         Dwarf_Die *die,
+                                         Dwarf_Die *parent_die,
+                                         ctf_file_t *ctf,
+                                         ctf_id_t parent_ctf_id,
+                                         const char *locerrstr,
+                                         int top_level_type,
+                                         enum skip_type *skip)
+{
+       ctf_id_t (*ctf_add_sou)(ctf_file_t *, uint_t, const char *);
+
+       const char *name = dwarf_diename(die);
+       int is_union = (dwarf_tag(die) == DW_TAG_union_type);
+
+       /*
+        * FIXME: these both need handling for DWARF4 support.
+        */
+       CTF_DW_ENFORCE_NOT(specification);
+       CTF_DW_ENFORCE_NOT(signature);
+
+       /*
+        * Possibly we should ignore this entire structure, if we already know
+        * of one with the same name and at least as many members.  If we
+        * already know of one and it is shorter, we want to use its ID rather
+        * than creating a new one.
+        */
+
+       if (name != NULL) {
+               ctf_id_t existing;
+               char *structized_name = NULL;
+
+               structized_name = str_appendn(structized_name,
+                                             is_union ? "union " : "struct ",
+                                             name, NULL);
+
+               existing = ctf_lookup_by_name(ctf, structized_name);
+               free(structized_name);
+
+               if (existing >= 0) {
+                       if (count_ctf_members(ctf, existing) <
+                           count_dwarf_members(die))
+                               return existing;
+
+                       *skip = SKIP_SKIP;
+                       return existing;
+               }
+       }
+
+       if (is_union)
+               ctf_add_sou = ctf_add_union;
+       else
+               ctf_add_sou = ctf_add_struct;
+
+       return ctf_add_sou(ctf, top_level_type ? CTF_ADD_ROOT : CTF_ADD_NONROOT,
+                          name);
+}
+
+/*
+ * Assemble a structure or union member.
+ *
+ * We only assemble a member by a given name if a member by that name does not
+ * already exist.
+ */
+static ctf_id_t assemble_ctf_su_member(const char *module_name,
+                                      const char *file_name,
+                                      Dwarf_Die *die,
+                                      Dwarf_Die *parent_die,
+                                      ctf_file_t *ctf,
+                                      ctf_id_t parent_ctf_id,
+                                      const char *locerrstr,
+                                      int top_level_type,
+                                      enum skip_type *skip)
+{
+       const char *name = dwarf_diename(die);
+       ulong_t offset;
+       ctf_full_id_t *new_type;
+       Dwarf_Attribute type_attr;
+       Dwarf_Die type_die;
+       Dwarf_Die cu_die;
+
+       CTF_DW_ENFORCE(type);
+
+       /*
+        * Get the CTF ID of this member's type, by recursive lookup.
+        */
+       dwarf_attr(die, DW_AT_type, &type_attr);
+       if (dwarf_formref_die(&type_attr, &type_die) == NULL) {
+               fprintf(stderr, "%s: nonexistent type reference. "
+                       "Corrupted DWARF, cannot continue.\n", locerrstr);
+               exit(1);
+       }
+       dwarf_diecu(&type_die, &cu_die, NULL, NULL);
+
+       new_type = construct_ctf_id(module_name, file_name, &type_die, &cu_die);
+
+       if (new_type == NULL) {
+               *skip = SKIP_ABORT;
+               return CTF_ERROR_REPORTED;
+       }
+
+       if ((new_type->ctf_file != ctf) &&
+           (new_type->ctf_file != g_hash_table_lookup(module_to_ctf_file,
+                                                      "dtrace_ctf"))) {
+               fprintf(stderr, "%s:%s: internal error: referenced type lookup "
+                       "for member %s yields a different CTF file: %p versus "
+                       "%p", locerrstr, dwarf_diename(&cu_die),
+                       dwarf_diename(die), ctf, new_type->ctf_file);
+               exit(1);
+       }
+
+       /*
+        * Figure out the offset of this type, in bits.
+        *
+        * DW_AT_data_bit_offset is the simple case.  DW_AT_data_member_location
+        * is trickier, and, alas, the DWARF2 variation is the complex one.
+        */
+       if (dwarf_hasattr(die, DW_AT_data_bit_offset)) {
+               Dwarf_Attribute bit_offset_attr;
+               Dwarf_Word bit_offset;
+
+               dwarf_attr(die, DW_AT_data_bit_offset, &bit_offset_attr);
+               dwarf_formudata(&bit_offset_attr, &bit_offset);
+
+               offset = bit_offset;
+       } else if (dwarf_hasattr(die, DW_AT_data_member_location)) {
+               Dwarf_Attribute location_attr;
+
+               dwarf_attr(die, DW_AT_data_member_location, &location_attr);
+
+               switch (dwarf_whatform(&location_attr)) {
+               case DW_FORM_data2:
+               case DW_FORM_data4:
+               case DW_FORM_data8:
+               case DW_FORM_udata:
+               case DW_FORM_sdata:
+               {
+                       /*
+                        * Byte offset, with bit_offset of containing
+                        * structure/union added, if present.
+                        */
+                       if (dwarf_whatform(&location_attr) == DW_FORM_sdata) {
+                               Dwarf_Sword location;
+
+                               dwarf_formsdata(&location_attr, &location);
+                               offset = location * 8;
+                       } else {
+                               Dwarf_Word location;
+
+                               dwarf_formudata(&location_attr, &location);
+                               offset = location * 8;
+                       }
+
+                       if (dwarf_hasattr(parent_die, DW_AT_bit_offset)) {
+                               Dwarf_Attribute bit_attr;
+                               Dwarf_Word bit;
+
+                               dwarf_attr(parent_die, DW_AT_bit_offset,
+                                          &bit_attr);
+                               dwarf_formudata(&bit_attr, &bit);
+                               offset += bit;
+                       }
+                       break;
+               }
+               case DW_FORM_block1:
+               case DW_FORM_block2:
+               case DW_FORM_block4:
+               {
+                       Dwarf_Op *location;
+                       size_t nlocs;
+
+                       /*
+                        * DWARF 2 data_member_location.  This can be quite
+                        * complicated in some situations (notably C++ virtual
+                        * bases), but for normal structure members it is
+                        * simple.  FIXME for userspace tracing of C++.
+                        *
+                        * This is thoroughly specific to the forms of DWARF2
+                        * emitted by GCC.  We don't need to feel guilty about
+                        * this because elfutils does just the same thing.
+                        */
+
+                       if (dwarf_getlocation(&location_attr, &location,
+                                             &nlocs) < 0) {
+                               fprintf(stderr, "%s: offset not a valid "
+                                       "location expression: %s\n", locerrstr,
+                                       dwarf_errmsg(dwarf_errno()));
+                               *skip = SKIP_ABORT;
+                               return CTF_ERROR_REPORTED;
+                       }
+
+                       if ((nlocs != 1) ||
+                           ((location[0].atom != DW_OP_plus_uconst) &&
+                            (location[0].atom != DW_OP_constu))) {
+                               fprintf(stderr, "%s: complex location lists "
+                                       "not supported: either C++ or non-GCC "
+                                       "output: skipped\n", locerrstr);
+                               *skip = SKIP_ABORT;
+                               return CTF_ERROR_REPORTED;
+                       }
+
+                       offset = location[0].number * 8;
+                       break;
+               }
+               case DW_FORM_exprloc:
+               {
+                       /*
+                        * We need a full DWARF expression list interpreter to
+                        * handle this.
+                        */
+                       fprintf(stderr, "DWARF 4 expression location lists "
+                               "not supported.\n");
+                       exit(1);
+               }
+               default:
+               {
+                       fprintf(stderr, "%s: expression location lists in "
+                               "form %u not supported.\n", locerrstr,
+                               dwarf_whatform(&location_attr));
+                       exit(1);
+               }
+               }
+       } else { /* No offset.  */
+               offset = 0;
+       }
+
+       if (ctf_add_member_offset(ctf, parent_ctf_id, name, new_type->ctf_id,
+                                 offset) < 0) {
+               /*
+                * If we have seen this member before, as part of another
+                * definition somewhere else, that's fine.  We cannot
+                * recurse from this point, so we can just return the parent CTF
+                * ID, the ID of the containing structure.
+                */
+               if (ctf_errno(ctf) == ECTF_DUPMEMBER)
+                       return parent_ctf_id;
+
+               if (ctf_errno(ctf) == ECTF_BADID) {
+#ifdef DEBUG
+                       fprintf(stderr, "%s: Internal error: bad ID %s:%s:%i "
+                               "on member addition to ctf_file %p.\n",
+                               locerrstr, new_type->module_name,
+                               new_type->file_name, (int) new_type->ctf_id,
+                               new_type->ctf_file);
+#else
+                       fprintf(stderr, "%s: Internal error: bad ID %i on "
+                               "member addition to ctf_file %p.\n",
+                               locerrstr, (int) new_type->ctf_id,
+                               new_type->ctf_file);
+#endif
+                       return CTF_ERROR_REPORTED;
+               }
+
+               /*
+                * Another error: not fine.
+                */
+               return CTF_NO_ERROR_REPORTED;
+       }
+
+       return parent_ctf_id;
+}
+
+/* Writeout.  */
+
+static void write_types(void)
+{
+       GHashTableIter module_iter;
+       char *module;
+       ctf_file_t *ctf_file;
+
+       /*
+        * Work over all the modules and write their compressed CTF data out
+        * into a new .ctf directory.  Built-in modules get names ending in
+        * .builtin.ctf.new; others get names ending in .mod.ctf.new.  The
+        * makefile moves .ctf.new over the top of .ctf iff it has changed.
+        */
+
+       if ((mkdir(".ctf", 0777) < 0) && errno != EEXIST) {
+               fprintf(stderr, "Cannot create .ctf directory: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       g_hash_table_iter_init(&module_iter, module_to_ctf_file);
+       while (g_hash_table_iter_next(&module_iter, (void **) &module,
+                                     (void **)&ctf_file)) {
+               char *path = NULL;
+               gzFile fd;
+               int builtin_module = 0;
+
+               dw_ctf_trace("Writing out %s\n", module);
+
+               if ((strcmp(module, "dtrace_ctf") == 0) ||
+                   (strcmp(module, "vmlinux") == 0))
+                       builtin_module = 1;
+               else {
+                       size_t module_num;
+
+                       for (module_num = 0; module_num < builtin_modules_cnt;
+                            module_num++) {
+                               char *module_name = fn_to_module(builtin_modules[module_num]);
+
+                               if (strcmp(module_name, module) == 0)
+                                       builtin_module = 1;
+
+                               free(module_name);
+                       }
+               }
+
+               path = str_appendn(path, ".ctf/", module,
+                                  builtin_module ? ".builtin" : ".mod",
+                                  ".ctf.new", NULL);
+
+               dw_ctf_trace("Writeout path: %s\n", path);
+
+               if ((fd = gzopen(path, "wb")) == NULL) {
+                       fprintf(stderr, "Cannot open CTF file %s for writing: "
+                               "%s\n", path, strerror(errno));
+                       exit(1);
+               }
+               if (ctf_gzwrite(ctf_file, fd) < 0) {
+                       fprintf(stderr, "Cannot write to CTF file %s: "
+                               "%s\n", path, ctf_errmsg(ctf_errno(ctf_file)));
+                       exit(1);
+               }
+
+               if (gzclose(fd) != Z_OK) {
+                       fprintf(stderr, "Cannot close CTF file %s: %s\n",
+                               path, strerror(errno));
+                       exit(1);
+               }
+               free(path);
+       }
+}
+
+/* Utilities.  */
+
+/*
+ * Wrap up dwfl_new() complexities.
+ */
+static Dwfl *private_dwfl_new(const char *file_name)
+{
+       const char *err;
+       static Dwfl_Callbacks cb = { .find_debuginfo = no_debuginfo,
+                                    .section_address = dwfl_offline_section_address };
+       Dwfl *dwfl = dwfl_begin(&cb);
+
+       if (dwfl == NULL) {
+               err = "initialize libdwfl";
+               goto fail;
+       }
+
+       if (dwfl_report_elf(dwfl, "", file_name, -1, 0) == NULL) {
+               err = "open object file with libdwfl";
+               goto fail;
+       }
+
+       if (dwfl_report_end(dwfl, NULL, NULL) != 0) {
+               err = "finish opening object file with libdwfl";
+               goto fail;
+       }
+
+       return dwfl;
+ fail:
+       fprintf(stderr, "Cannot %s for %s: %s\n", err, file_name,
+               dwfl_errmsg(dwfl_errno()));
+       exit(1);
+}
+
+/*
+ * The converse of private_dwfl_new().
+ */
+static void private_dwfl_free(Dwfl *dwfl)
+{
+       dwfl_report_end(dwfl, NULL, NULL);
+       dwfl_end(dwfl);
+}
+
+/*
+ * Given a DIE that may contain a type attribute, look up the target of that
+ * attribute and return it, or NULL if none.
+ */
+static Dwarf_Die *private_dwarf_type(Dwarf_Die *die, Dwarf_Die *target_die)
+{
+       Dwarf_Attribute type_ref_attr;
+
+       if (dwarf_attr(die, DW_AT_type, &type_ref_attr) != NULL) {
+               if (dwarf_formref_die(&type_ref_attr, target_die) == NULL) {
+                       fprintf(stderr, "Corrupt DWARF at offset %lx: ref with "
+                               "no target.\n",
+                               (unsigned long) dwarf_dieoffset(die));
+                       exit(1);
+               }
+               return target_die;
+       }
+
+       return NULL;
+}
+
+/*
+ * An error checking strdup().
+ */
+static char *xstrdup(const char *s)
+{
+       char *s2 = strdup(s);
+
+       if (s2 == NULL) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return s2;
+}
+
+/*
+ * A string appender working on dynamic strings.
+ *
+ * FIXME: slightly inefficient, doing two strlen(s)'s.
+ */
+static char *str_append(char *s, const char *append)
+{
+       char *d;
+       size_t s_len = 0;
+
+       if (append == NULL)
+               return s;
+
+       if (s != NULL)
+               s_len = strlen(s);
+
+       d = realloc(s, s_len + strlen(append) + 1);
+
+       if (d == NULL) {
+               fprintf(stderr, "Out of memory appending a string of length "
+                       "%li to a string of length %li\n", strlen(append),
+                       s_len);
+               exit(1);
+       }
+
+       if (s == NULL)
+               d[0] = '\0';
+
+       strcat(d, append);
+       return d;
+}
+
+/*
+ * A vararg string appender.
+ */
+static char *str_appendn(char *s, ...)
+{
+       va_list ap;
+       const char *append;
+
+       va_start(ap, s);
+
+       append = va_arg(ap, const char *);
+       while (append != NULL) {
+               s = str_append(s, append);
+               append = va_arg(ap, char *);
+       }
+
+       va_end(ap);
+       return s;
+}
+
+/*
+ * Figure out the (pathless, suffixless) module name for a given module file (.o
+ * or .ko), and return it in a new dynamically allocated string.
+ */
+static char *fn_to_module(const char *file_name)
+{
+       char *module_name;
+       char *chop;
+
+       if ((chop = strrchr(file_name, '/')) != NULL)
+               module_name = xstrdup(++chop);
+       else
+               module_name = xstrdup(file_name);
+
+       if ((chop = strrchr(module_name, '.')) != NULL)
+               *chop = '\0';
+
+       return module_name;
+}
+
+/*
+ * Given a type encoding table, and a size, return the CTF encoding for that
+ * type, or 0 if none.
+ */
+static int find_ctf_encoding(struct type_encoding_t *type_tab, size_t size)
+{
+       size_t i;
+
+       for (i = 0; type_tab[i].size != 0; i++) {
+               if (type_tab[i].size == size)
+                       return type_tab[i].ctf_encoding;
+       }
+       return 0;
+}
+
+/*
+ * Count the number of members of a DWARF aggregate.
+ */
+static long count_dwarf_members(Dwarf_Die *d)
+{
+       const char *err;
+       Dwarf_Die die;
+
+       switch (dwarf_child(d, &die)) {
+       case -1:
+               err = "fetch first child of aggregate";
+               goto fail;
+       case 1: /* No DIEs at all in this aggregate */
+               return 0;
+       default: /* Child DIEs exist.  */
+               break;
+       }
+
+       /*
+        * We are only interested in children of type DW_TAG_member.
+        */
+       int sib_ret;
+       long count = 0;
+
+       do {
+               if (dwarf_tag(&die) == DW_TAG_member)
+                       count++;
+       } while ((sib_ret = dwarf_siblingof(&die, &die)) == 0);
+
+       if (sib_ret == -1) {
+               err = "count members";
+               goto fail;
+       }
+
+       return count;
+
+ fail:
+       fprintf(stderr, "Cannot %s: %s\n", err, dwarf_errmsg(dwarf_errno()));
+       exit(1);
+}
+
+ /*
+ * Count the number of members of a CTF aggregate.
+ */
+static long count_ctf_members(ctf_file_t *fp, ctf_id_t souid)
+{
+       long count = 0;
+
+       ctf_member_iter(fp, souid, count_ctf_members_internal, &count);
+
+       return count;
+}
+
+/*
+ * Increment said count.
+ */
+static int count_ctf_members_internal(const char *name, ctf_id_t member,
+                                     ulong_t offset, void *data)
+{
+       long *count = (long *) data;
+
+       (*count)++;
+       return 0;
+}
+
+/*
+ * Stub libdwfl callback, use only the ELF handle passed in.
+ */
+static int no_debuginfo(Dwfl_Module *mod __unused__,
+                       void **userdata __unused__,
+                       const char *modname __unused__,
+                       Dwarf_Addr base __unused__,
+                       const char *file_name __unused__,
+                       const char *debuglink_file __unused__,
+                       GElf_Word debuglink_crc __unused__,
+                       char **debuginfo_file_name __unused__)
+{
+       return -1;
+}
+
+/*
+ * Trivial wrapper, avoid an incompatible pointer type warning.
+ */
+static void private_ctf_free(void *ctf_file)
+{
+       ctf_close((ctf_file_t *)ctf_file);
+}
diff --git a/scripts/move-if-change b/scripts/move-if-change
new file mode 100755 (executable)
index 0000000..f6546c8
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if test -r "$2" && cmp -s "$1" "$2"; then
+  rm -f "$1"
+else
+  mv -f "$1" "$2"
+fi