]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - libctf/ctf-dump.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / libctf / ctf-dump.c
index 28f31e4872a07b44fec6cebcf2fbd0cef938f0fb..11179a61386f948f97ba2eca2dac935e3fae4b21 100644 (file)
@@ -1,5 +1,5 @@
 /* Textual dumping of CTF data.
-   Copyright (C) 2019 Free Software Foundation, Inc.
+   Copyright (C) 2019-2024 Free Software Foundation, Inc.
 
    This file is part of libctf.
 
@@ -20,6 +20,8 @@
 #include <ctf-impl.h>
 #include <string.h>
 
+#define str_append(s, a) ctf_str_append_noerr (s, a)
+
 /* One item to be dumped, in string form.  */
 
 typedef struct ctf_dump_item
@@ -34,7 +36,7 @@ typedef struct ctf_dump_item
 struct ctf_dump_state
 {
   ctf_sect_names_t cds_sect;
-  ctf_file_t *cds_fp;
+  ctf_dict_t *cds_fp;
   ctf_dump_item_t *cds_current;
   ctf_list_t cds_items;
 };
@@ -44,7 +46,8 @@ struct ctf_dump_state
 typedef struct ctf_dump_membstate
 {
   char **cdm_str;
-  ctf_file_t *cdm_fp;
+  ctf_dict_t *cdm_fp;
+  const char *cdm_toplevel_indent;
 } ctf_dump_membstate_t;
 
 static int
@@ -52,7 +55,7 @@ ctf_dump_append (ctf_dump_state_t *state, char *str)
 {
   ctf_dump_item_t *cdi;
 
-  if ((cdi = ctf_alloc (sizeof (struct ctf_dump_item))) == NULL)
+  if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
     return (ctf_set_errno (state->cds_fp, ENOMEM));
 
   cdi->cdi_item = str;
@@ -73,68 +76,157 @@ ctf_dump_free (ctf_dump_state_t *state)
     {
       free (cdi->cdi_item);
       next_cdi = ctf_list_next (cdi);
-      ctf_free (cdi);
+      free (cdi);
     }
 }
 
-/* Slices need special handling to distinguish them from their referenced
-   type.  */
+/* Return a dump for a single type, without member info: but do optionally show
+   the type's references.  */
 
-static int
-ctf_is_slice (ctf_file_t *fp, ctf_id_t id, ctf_encoding_t *enc)
-{
-  int kind = ctf_type_kind (fp, id);
-
-  return (((kind == CTF_K_INTEGER) || (kind == CTF_K_ENUM)
-          || (kind == CTF_K_FLOAT))
-         && ctf_type_reference (fp, id) != CTF_ERR
-         && ctf_type_encoding (fp, id, enc) != CTF_ERR);
-}
-
-/* Return a dump for a single type, without member info: but do show the
-   type's references.  */
+#define CTF_FT_REFS     0x2    /* Print referenced types.  */
+#define CTF_FT_BITFIELD 0x4    /* Print :BITS if a bitfield.  */
+#define CTF_FT_ID       0x8    /* Print "ID: " in front of type IDs.  */
 
 static char *
-ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id)
+ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
 {
   ctf_id_t new_id;
   char *str = NULL, *bit = NULL, *buf = NULL;
 
+  ctf_set_errno (fp, 0);
   new_id = id;
   do
     {
-      ctf_encoding_t enc;
+      ctf_encoding_t ep;
+      ctf_arinfo_t ar;
+      int kind, unsliced_kind;
+      ssize_t size, align;
+      const char *nonroot_leader = "";
+      const char *nonroot_trailer = "";
+      const char *idstr = "";
 
       id = new_id;
+      if (flag == CTF_ADD_NONROOT)
+       {
+         nonroot_leader = "{";
+         nonroot_trailer = "}";
+       }
+
       buf = ctf_type_aname (fp, id);
       if (!buf)
+       {
+         if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
+           {
+             ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
+             str = str_append (str, " (type not represented in CTF)");
+             return str;
+           }
+
+         goto err;
+       }
+
+      if (flag & CTF_FT_ID)
+       idstr = "ID ";
+      if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
+                   id, ctf_type_kind (fp, id)) < 0)
        goto oom;
+      str = str_append (str, bit);
+      free (bit);
+      bit = NULL;
+
+      if (buf[0] != '\0')
+       str = str_append (str, buf);
+
+      free (buf);
+      buf = NULL;
 
-      /* Slices get a different print representation.  */
+      unsliced_kind = ctf_type_kind_unsliced (fp, id);
+      kind = ctf_type_kind (fp, id);
 
-      if (ctf_is_slice (fp, id, &enc))
+      /* Report encodings of everything with an encoding other than enums:
+        base-type enums cannot have a nonzero cte_offset or cte_bits value.
+        (Slices of them can, but they are of kind CTF_K_SLICE.)  */
+      if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
        {
-         ctf_type_encoding (fp, id, &enc);
-         if (asprintf (&bit, " %lx: [slice 0x%x:0x%x]",
-                       id, enc.cte_offset, enc.cte_bits) < 0)
+         if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
+             && flag & CTF_FT_BITFIELD)
+           {
+             if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
+               goto oom;
+             str = str_append (str, bit);
+             free (bit);
+             bit = NULL;
+           }
+
+         if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
+             || ep.cte_offset != 0)
+           {
+             const char *slice = "";
+
+             if (unsliced_kind == CTF_K_SLICE)
+               slice = "slice ";
+
+             if (asprintf (&bit, " [%s0x%x:0x%x]",
+                           slice, ep.cte_offset, ep.cte_bits) < 0)
+               goto oom;
+             str = str_append (str, bit);
+             free (bit);
+             bit = NULL;
+           }
+
+         if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
            goto oom;
+         str = str_append (str, bit);
+         free (bit);
+         bit = NULL;
        }
-      else
+
+      size = ctf_type_size (fp, id);
+      if (kind != CTF_K_FUNCTION && size >= 0)
        {
-         if (asprintf (&bit, " %lx: %s (size %lx)", id, buf[0] == '\0' ?
-                       "(nameless)" : buf, ctf_type_size (fp, id)) < 0)
+         if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
            goto oom;
+
+         str = str_append (str, bit);
+         free (bit);
+         bit = NULL;
        }
-      free (buf);
-      buf = NULL;
-      str = ctf_str_append (str, bit);
-      free (bit);
-      bit = NULL;
 
-      new_id = ctf_type_reference (fp, id);
+      align = ctf_type_align (fp, id);
+      if (align >= 0)
+       {
+         if (asprintf (&bit, " (aligned at 0x%lx)",
+                       (unsigned long int) align) < 0)
+           goto oom;
+
+         str = str_append (str, bit);
+         free (bit);
+         bit = NULL;
+       }
+
+      if (nonroot_trailer[0] != 0)
+       str = str_append (str, nonroot_trailer);
+
+      /* Just exit after one iteration if we are not showing the types this type
+        references.  */
+      if (!(flag & CTF_FT_REFS))
+       return str;
+
+      /* Keep going as long as this type references another.  We consider arrays
+        to "reference" their element type. */
+
+      if (kind == CTF_K_ARRAY)
+       {
+         if (ctf_array_info (fp, id, &ar) < 0)
+           goto err;
+         new_id = ar.ctr_contents;
+       }
+      else
+       new_id = ctf_type_reference (fp, id);
       if (new_id != CTF_ERR)
-       str = ctf_str_append (str, " ->");
-    } while (new_id != CTF_ERR);
+       str = str_append (str, " -> ");
+    }
+  while (new_id != CTF_ERR);
 
   if (ctf_errno (fp) != ECTF_NOTREF)
     {
@@ -145,183 +237,243 @@ ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id)
   return str;
 
  oom:
+  ctf_set_errno (fp, errno);
+ err:
+  ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
   free (buf);
   free (str);
   free (bit);
-  ctf_set_errno (fp, ENOMEM);
   return NULL;
 }
 
-/* Dump a single label into the cds_items.  */
-
+/* Dump one string field from the file header into the cds_items.  */
 static int
-ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
-               void *arg)
+ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
+                         const char *name, uint32_t value)
 {
   char *str;
-  char *typestr;
-  ctf_dump_state_t *state = arg;
-
-  if (asprintf (&str, "%s -> ", name) < 0)
-    return (ctf_set_errno (state->cds_fp, ENOMEM));
-
-  if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type)) == NULL)
+  if (value)
     {
-      free (str);
-      return CTF_ERR;                  /* errno is set for us.  */
+      if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
+       goto err;
+      ctf_dump_append (state, str);
     }
+  return 0;
 
-  str = ctf_str_append (str, typestr);
-  free (typestr);
+ err:
+  return (ctf_set_errno (fp, errno));
+}
 
-  ctf_dump_append (state, str);
+/* Dump one section-offset field from the file header into the cds_items.  */
+static int
+ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
+                          const char *sect, uint32_t off, uint32_t nextoff)
+{
+  char *str;
+  if (nextoff - off)
+    {
+      if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
+                   (unsigned long) off, (unsigned long) (nextoff - 1),
+                   (unsigned long) (nextoff - off)) < 0)
+       goto err;
+      ctf_dump_append (state, str);
+    }
   return 0;
-}
 
-/* Dump all the object entries into the cds_items.  (There is no iterator for
-   this section, so we just do it in a loop, and this function handles all of
-   them, rather than only one.  */
+ err:
+  return (ctf_set_errno (fp, errno));
+}
 
+/* Dump the file header into the cds_items.  */
 static int
-ctf_dump_objts (ctf_file_t *fp, ctf_dump_state_t *state)
+ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
 {
-  size_t i;
-
-  for (i = 0; i < fp->ctf_nsyms; i++)
+  char *str;
+  char *flagstr = NULL;
+  const ctf_header_t *hp = fp->ctf_header;
+  const char *vertab[] =
     {
-      char *str;
-      char *typestr;
-      const char *sym_name;
-      ctf_id_t type;
-
-      if ((type = ctf_lookup_by_symbol (state->cds_fp, i)) < 0)
-       switch (ctf_errno (state->cds_fp))
-         {
-           /* Most errors are just an indication that this symbol is not a data
-              symbol, but this one indicates that we were called wrong, on a
-              CTF file with no associated symbol table.  */
-         case ECTF_NOSYMTAB:
-           return CTF_ERR;
-         case ECTF_NOTDATA:
-         case ECTF_NOTYPEDAT:
-           continue;
-         }
+     NULL, "CTF_VERSION_1",
+     "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
+     "boundaries)",
+     "CTF_VERSION_2",
+     "CTF_VERSION_3", NULL
+    };
+  const char *verstr = NULL;
+
+  if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
+      goto err;
+  ctf_dump_append (state, str);
 
-      /* Variable name.  */
-      sym_name = ctf_lookup_symbol_name (fp, i);
-      if (sym_name[0] == '\0')
-       {
-         if (asprintf (&str, "%lx -> ", i) < 0)
-           return (ctf_set_errno (fp, ENOMEM));
-       }
-      else
-       {
-         if (asprintf (&str, "%s (%lx) -> ", sym_name, i) < 0)
-           return (ctf_set_errno (fp, ENOMEM));
-       }
+  if (hp->cth_version <= CTF_VERSION)
+    verstr = vertab[hp->cth_version];
 
-      /* Variable type.  */
-      if ((typestr = ctf_dump_format_type (state->cds_fp, type)) == NULL)
-       {
-         free (str);
-         return CTF_ERR;               /* errno is set for us.  */
-       }
+  if (verstr == NULL)
+    verstr = "(not a valid version)";
 
-      str = ctf_str_append (str, typestr);
-      free (typestr);
+  if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
+               verstr) < 0)
+    goto err;
+  ctf_dump_append (state, str);
+
+  /* Everything else is only printed if present.  */
 
+  /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
+     flags representing compression, etc, are turned off as the file is
+     decompressed.  So we store a copy of the flags before they are changed, for
+     the dumper.  */
+
+  if (fp->ctf_openflags > 0)
+    {
+      if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
+                   fp->ctf_openflags & CTF_F_COMPRESS
+                   ? "CTF_F_COMPRESS": "",
+                   (fp->ctf_openflags & CTF_F_COMPRESS)
+                   && (fp->ctf_openflags & ~CTF_F_COMPRESS)
+                   ? ", " : "",
+                   fp->ctf_openflags & CTF_F_NEWFUNCINFO
+                   ? "CTF_F_NEWFUNCINFO" : "",
+                   (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
+                   && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
+                   ? ", " : "",
+                   fp->ctf_openflags & CTF_F_IDXSORTED
+                   ? "CTF_F_IDXSORTED" : "",
+                   fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
+                                        | CTF_F_IDXSORTED)
+                   && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
+                                             | CTF_F_IDXSORTED))
+                   ? ", " : "",
+                   fp->ctf_openflags & CTF_F_DYNSTR
+                   ? "CTF_F_DYNSTR" : "") < 0)
+       goto err;
+
+      if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
+       goto err;
       ctf_dump_append (state, str);
     }
+
+  if (ctf_dump_header_strfield (fp, state, "Parent label",
+                               hp->cth_parlabel) < 0)
+    goto err;
+
+  if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
+    goto err;
+
+  if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
+                               hp->cth_cuname) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
+                                hp->cth_objtoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Data object section",
+                                hp->cth_objtoff, hp->cth_funcoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Function info section",
+                                hp->cth_funcoff, hp->cth_objtidxoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Object index section",
+                                hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Function index section",
+                                hp->cth_funcidxoff, hp->cth_varoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Variable section",
+                                hp->cth_varoff, hp->cth_typeoff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "Type section",
+                                hp->cth_typeoff, hp->cth_stroff) < 0)
+    goto err;
+
+  if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
+                                hp->cth_stroff + hp->cth_strlen + 1) < 0)
+    goto err;
+
   return 0;
+ err:
+  free (flagstr);
+  return (ctf_set_errno (fp, errno));
 }
 
-/* Dump all the function entries into the cds_items.  (As above, there is no
-   iterator for this section.)  */
+/* Dump a single label into the cds_items.  */
 
 static int
-ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
+ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
+               void *arg)
 {
-  size_t i;
+  char *str;
+  char *typestr;
+  ctf_dump_state_t *state = arg;
 
-  for (i = 0; i < fp->ctf_nsyms; i++)
+  if (asprintf (&str, "%s -> ", name) < 0)
+    return (ctf_set_errno (state->cds_fp, errno));
+
+  if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
+                                      CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
     {
-      char *str ;
-      char *bit;
-      const char *sym_name;
-      ctf_funcinfo_t fi;
-      ctf_id_t type;
-      size_t j;
-      ctf_id_t *args;
-
-      if ((type = ctf_func_info (state->cds_fp, i, &fi)) < 0)
-       switch (ctf_errno (state->cds_fp))
-         {
-           /* Most errors are just an indication that this symbol is not a data
-              symbol, but this one indicates that we were called wrong, on a
-              CTF file with no associated symbol table.  */
-         case ECTF_NOSYMTAB:
-           return CTF_ERR;
-         case ECTF_NOTDATA:
-         case ECTF_NOTYPEDAT:
-           continue;
-         }
-      if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
-       return (ctf_set_errno (fp, ENOMEM));
+      free (str);
+      return 0;                                /* Swallow the error.  */
+    }
 
-      /* Return type.  */
-      if ((str = ctf_type_aname (state->cds_fp, type)) == NULL)
-       goto err;
+  str = str_append (str, typestr);
+  free (typestr);
 
-      str = ctf_str_append (str, " ");
-      free (bit);
+  ctf_dump_append (state, str);
+  return 0;
+}
 
-      /* Function name.  */
+/* Dump all the object or function entries into the cds_items.  */
 
-      sym_name = ctf_lookup_symbol_name (fp, i);
-      if (sym_name[0] == '\0')
+static int
+ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
+{
+  const char *name;
+  ctf_id_t id;
+  ctf_next_t *i = NULL;
+  char *str = NULL;
+
+  if ((functions && fp->ctf_funcidx_names)
+      || (!functions && fp->ctf_objtidx_names))
+    str = str_append (str, _("Section is indexed.\n"));
+  else if (fp->ctf_symtab.cts_data == NULL)
+    str = str_append (str, _("No symbol table.\n"));
+
+  while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
+    {
+      char *typestr = NULL;
+
+      /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
+        has a leading one.   */
+      if (name)
        {
-         if (asprintf (&bit, "%lx ", i) < 0)
+         if (asprintf (&str, "%s -> ", name) < 0)
            goto oom;
        }
       else
-       {
-         if (asprintf (&bit, "%s (%lx) ", sym_name, i) < 0)
-           goto oom;
-       }
-      str = ctf_str_append (str, bit);
-      str = ctf_str_append (str, " (");
+       str = xstrdup ("");
 
-      /* Function arguments.  */
-
-      if (ctf_func_args (state->cds_fp, i, fi.ctc_argc, args) < 0)
-       goto err;
-
-      for (j = 0; j < fi.ctc_argc; j++)
+      if ((typestr = ctf_dump_format_type (state->cds_fp, id,
+                                          CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
        {
-         if ((bit = ctf_type_aname (state->cds_fp, args[j])) == NULL)
-           goto err;
-         str = ctf_str_append (str, bit);
-         if ((j < fi.ctc_argc - 1) || (fi.ctc_flags & CTF_FUNC_VARARG))
-           str = ctf_str_append (str, ", ");
-         free (bit);
+         ctf_dump_append (state, str);
+         continue;                             /* Swallow the error.  */
        }
 
-      if (fi.ctc_flags & CTF_FUNC_VARARG)
-       str = ctf_str_append (str, "...");
-      str = ctf_str_append (str, ")");
-
-      free (args);
+      str = str_append (str, typestr);
+      free (typestr);
       ctf_dump_append (state, str);
       continue;
 
     oom:
-      free (args);
-      free (str);
-      return (ctf_set_errno (fp, ENOMEM));
-    err:
-      free (args);
-      free (str);
-      return CTF_ERR;          /* errno is set for us.  */
+      ctf_set_errno (fp, ENOMEM);
+      ctf_next_destroy (i);
+      return -1;
     }
   return 0;
 }
@@ -335,104 +487,180 @@ ctf_dump_var (const char *name, ctf_id_t type, void *arg)
   ctf_dump_state_t *state = arg;
 
   if (asprintf (&str, "%s -> ", name) < 0)
-    return (ctf_set_errno (state->cds_fp, ENOMEM));
+    return (ctf_set_errno (state->cds_fp, errno));
 
-  if ((typestr = ctf_dump_format_type (state->cds_fp, type)) == NULL)
+  if ((typestr = ctf_dump_format_type (state->cds_fp, type,
+                                      CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
     {
       free (str);
-      return CTF_ERR;                  /* errno is set for us.  */
+      return 0;                        /* Swallow the error.  */
     }
 
-  str = ctf_str_append (str, typestr);
+  str = str_append (str, typestr);
   free (typestr);
 
   ctf_dump_append (state, str);
   return 0;
 }
 
-/* Dump a single member into the string in the membstate.  */
+/* Dump a single struct/union member into the string in the membstate.  */
 static int
 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
-                 int depth, void *arg)
+                int depth, void *arg)
 {
   ctf_dump_membstate_t *state = arg;
   char *typestr = NULL;
   char *bit = NULL;
-  ctf_encoding_t ep;
-  ssize_t i;
 
-  for (i = 0; i < depth; i++)
-    *state->cdm_str = ctf_str_append (*state->cdm_str, "    ");
+  /* The struct/union itself has already been printed.  */
+  if (depth == 0)
+    return 0;
 
-  if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
+  if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
     goto oom;
+  *state->cdm_str = str_append (*state->cdm_str, bit);
+  free (bit);
 
-  if (asprintf (&bit, "    [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
-               offset, id, ctf_type_kind (state->cdm_fp, id), typestr, name,
-               ctf_type_align (state->cdm_fp, id)) < 0)
+  if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
+                                      CTF_ADD_ROOT | CTF_FT_BITFIELD
+                                      | CTF_FT_ID)) == NULL)
+    return -1;                         /* errno is set for us.  */
+
+  if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
     goto oom;
-  *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
+
+  *state->cdm_str = str_append (*state->cdm_str, bit);
   free (typestr);
   free (bit);
   typestr = NULL;
   bit = NULL;
 
-  if ((ctf_type_kind (state->cdm_fp, id) == CTF_K_INTEGER)
-      || (ctf_type_kind (state->cdm_fp, id) == CTF_K_FLOAT)
-      || (ctf_is_slice (state->cdm_fp, id, &ep) == CTF_K_ENUM))
-    {
-      ctf_type_encoding (state->cdm_fp, id, &ep);
-      if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
-                   ep.cte_offset, ep.cte_bits) < 0)
-       goto oom;
-      *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
-      free (bit);
-      bit = NULL;
-    }
-
-  *state->cdm_str = ctf_str_append (*state->cdm_str, ")\n");
   return 0;
 
  oom:
   free (typestr);
   free (bit);
-  return (ctf_set_errno (state->cdm_fp, ENOMEM));
+  return (ctf_set_errno (state->cdm_fp, errno));
 }
 
-/* Dump a single type into the cds_items.  */
+/* Report the number of digits in the hexadecimal representation of a type
+   ID.  */
 
 static int
-ctf_dump_type (ctf_id_t id, void *arg)
+type_hex_digits (ctf_id_t id)
+{
+  int i = 0;
+
+  if (id == 0)
+    return 1;
+
+  for (; id > 0; id >>= 4, i++);
+  return i;
+}
+
+/* Dump a single type into the cds_items.  */
+static int
+ctf_dump_type (ctf_id_t id, int flag, void *arg)
 {
   char *str;
+  char *indent;
   ctf_dump_state_t *state = arg;
-  ctf_dump_membstate_t membstate = { &str, state->cds_fp };
-  size_t len;
+  ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
 
-  if ((str = ctf_dump_format_type (state->cds_fp, id)) == NULL)
-    goto err;
+  /* Indent neatly.  */
+  if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
+    return (ctf_set_errno (state->cds_fp, ENOMEM));
 
-  str = ctf_str_append (str, "\n");
-  if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
+  /* Dump the type itself.  */
+  if ((str = ctf_dump_format_type (state->cds_fp, id,
+                                  flag | CTF_FT_REFS)) == NULL)
     goto err;
+  str = str_append (str, "\n");
+
+  membstate.cdm_toplevel_indent = indent;
+
+  /* Member dumping for structs, unions...  */
+  if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
+      || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
+    {
+      if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
+       {
+         if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
+           {
+             ctf_dump_append (state, str);
+             return 0;
+           }
+         ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
+                       _("cannot visit members dumping type 0x%lx"), id);
+         goto err;
+       }
+    }
+
+  /* ... and enums, for which we dump the first and last few members and skip
+     the ones in the middle.  */
+  if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
+    {
+      int enum_count = ctf_member_count (state->cds_fp, id);
+      ctf_next_t *it = NULL;
+      int i = 0;
+      const char *enumerand;
+      char *bit;
+      int value;
 
-  /* Trim off the last linefeed added by ctf_dump_member().  */
-  len = strlen (str);
-  if (str[len-1] == '\n')
-    str[len-1] = '\0';
+      while ((enumerand = ctf_enum_next (state->cds_fp, id,
+                                        &it, &value)) != NULL)
+       {
+         i++;
+         if ((i > 5) && (i < enum_count - 4))
+           continue;
+
+         str = str_append (str, indent);
+
+         if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
+           {
+             ctf_next_destroy (it);
+             goto oom;
+           }
+         str = str_append (str, bit);
+         free (bit);
+
+         if ((i == 5) && (enum_count > 10))
+           {
+             str = str_append (str, indent);
+             str = str_append (str, "...\n");
+           }
+       }
+      if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
+       {
+         ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
+                       _("cannot visit enumerands dumping type 0x%lx"), id);
+         goto err;
+       }
+    }
 
   ctf_dump_append (state, str);
+  free (indent);
+
   return 0;
 
  err:
+  free (indent);
   free (str);
-  return CTF_ERR;                      /* errno is set for us.  */
+
+  /* Swallow the error: don't cause an error in one type to abort all
+     type dumping.  */
+  return 0;
+
+ oom:
+  free (indent);
+  free (str);
+  return ctf_set_errno (state->cds_fp, ENOMEM);
 }
 
 /* Dump the string table into the cds_items.  */
 
 static int
-ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
+ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
 {
   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
 
@@ -440,9 +668,10 @@ ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
         fp->ctf_str[CTF_STRTAB_0].cts_len;)
     {
       char *str;
-      if (asprintf (&str, "%lx: %s", s - fp->ctf_str[CTF_STRTAB_0].cts_strs,
+      if (asprintf (&str, "0x%lx: %s",
+                   (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
                    s) < 0)
-       return (ctf_set_errno (fp, ENOMEM));
+       return (ctf_set_errno (fp, errno));
       ctf_dump_append (state, str);
       s += strlen (s) + 1;
     }
@@ -461,7 +690,7 @@ ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
    allocate a new one and return it if it likes).  */
 
 char *
-ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
+ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
          ctf_dump_decorate_f *func, void *arg)
 {
   char *str;
@@ -476,7 +705,7 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
         by bit.  The first call will take (much) longer than otherwise, but the
         amortized time needed is the same.  */
 
-      if ((*statep = ctf_alloc (sizeof (struct ctf_dump_state))) == NULL)
+      if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
        {
          ctf_set_errno (fp, ENOMEM);
          goto end;
@@ -490,8 +719,7 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
       switch (sect)
        {
        case CTF_SECT_HEADER:
-         /* Nothing doable (yet): entire header is discarded after read-phase.  */
-         str = strdup ("");
+         ctf_dump_header (fp, state);
          break;
        case CTF_SECT_LABEL:
          if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
@@ -502,11 +730,11 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
            }
          break;
        case CTF_SECT_OBJT:
-         if (ctf_dump_objts (fp, state) < 0)
+         if (ctf_dump_objts (fp, state, 0) < 0)
            goto end;                   /* errno is set for us.  */
          break;
        case CTF_SECT_FUNC:
-         if (ctf_dump_funcs (fp, state) < 0)
+         if (ctf_dump_objts (fp, state, 1) < 0)
            goto end;                   /* errno is set for us.  */
          break;
        case CTF_SECT_VAR:
@@ -514,7 +742,7 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
            goto end;                   /* errno is set for us.  */
          break;
        case CTF_SECT_TYPE:
-         if (ctf_type_iter (fp, ctf_dump_type, state) < 0)
+         if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
            goto end;                   /* errno is set for us.  */
          break;
        case CTF_SECT_STR:
@@ -561,8 +789,8 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
            nline[0] = '\0';
 
          ret = func (sect, line, arg);
-         str = ctf_str_append (str, ret);
-         str = ctf_str_append (str, "\n");
+         str = str_append (str, ret);
+         str = str_append (str, "\n");
          if (ret != line)
            free (ret);
 
@@ -581,14 +809,21 @@ ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
        str[len-1] = '\0';
     }
   else
-    str = strdup (state->cds_current->cdi_item);
+    {
+      str = strdup (state->cds_current->cdi_item);
+      if (!str)
+       {
+         ctf_set_errno (fp, ENOMEM);
+         return str;
+       }
+    }
 
   ctf_set_errno (fp, 0);
   return str;
 
  end:
   ctf_dump_free (state);
-  ctf_free (state);
+  free (state);
   ctf_set_errno (fp, 0);
   *statep = NULL;
   return NULL;