libctf, binutils: support CTF archives like objdump
authorNick Alcock <nick.alcock@oracle.com>
Fri, 13 Dec 2019 12:01:12 +0000 (12:01 +0000)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 26 Jun 2020 14:56:39 +0000 (15:56 +0100)
objdump and readelf have one major CTF-related behavioural difference:
objdump can read .ctf sections that contain CTF archives and extract and
dump their members, while readelf cannot.  Since the linker often emits
CTF archives, this means that readelf intermittently and (from the
user's perspective) randomly fails to read CTF in files that ld emits,
with a confusing error message wrongly claiming that the CTF content is
corrupt.  This is purely because the archive-opening code in libctf was
needlessly tangled up with the BFD code, so readelf couldn't use it.

Here, we disentangle it, moving ctf_new_archive_internal from
ctf-open-bfd.c into ctf-archive.c and merging it with the helper
function in ctf-archive.c it was already using.  We add a new public API
function ctf_arc_bufopen, that looks very like ctf_bufopen but returns
an archive given suitable section data rather than a ctf_file_t: the
archive is a ctf_archive_t, so it can be called on raw CTF dictionaries
(with no archive present) and will return a single-member synthetic
"archive".

There is a tiny lifetime tweak here: before now, the archive code could
assume that the symbol section in the ctf_archive_internal wrapper
structure was always owned by BFD if it was present and should always be
freed: now, the caller can pass one in via ctf_arc_bufopen, wihch has
the usual lifetime rules for such sections (caller frees): so we add an
extra field to track whether this is an internal call from ctf-open-bfd,
in which case we still free the symbol section.

include/
* ctf-api.h (ctf_arc_bufopen): New.
libctf/
* ctf-impl.h (ctf_new_archive_internal): Declare.
(ctf_arc_bufopen): Remove.
(ctf_archive_internal) <ctfi_free_symsect>: New.
* ctf-archive.c (ctf_arc_close): Use it.
(ctf_arc_bufopen): Fuse into...
(ctf_new_archive_internal): ... this, moved across from...
* ctf-open-bfd.c: ... here.
(ctf_bfdopen_ctfsect): Use ctf_arc_bufopen.
* libctf.ver: Add it.
binutils/
* readelf.c (dump_section_as_ctf): Support .ctf archives using
ctf_arc_bufopen.  Automatically load the .ctf member of such
archives as the parent of all other members, unless specifically
overridden via --ctf-parent.  Split out dumping code into...
(dump_ctf_archive_member): ... here, as in objdump, and call
it once per archive member.
(dump_ctf_indent_lines): Code style fix.

binutils/ChangeLog
binutils/readelf.c
include/ChangeLog
include/ctf-api.h
libctf/ChangeLog
libctf/ctf-archive.c
libctf/ctf-impl.h
libctf/ctf-open-bfd.c
libctf/libctf.ver

index 1532a37c390632c62c1b0073c527a76c8813012e..95a868ae684c1af8dc92d6890174f352f56ccb01 100644 (file)
@@ -1,3 +1,13 @@
+2020-06-26  Nick Alcock  <nick.alcock@oracle.com>
+
+       * readelf.c (dump_section_as_ctf): Support .ctf archives using
+       ctf_arc_bufopen.  Automatically load the .ctf member of such
+       archives as the parent of all other members, unless specifically
+       overridden via --ctf-parent.  Split out dumping code into...
+       (dump_ctf_archive_member): ... here, as in objdump, and call
+       it once per archive member.
+       (dump_ctf_indent_lines): Code style fix.
+
 2020-06-26  Nick Alcock  <nick.alcock@oracle.com>
 
        * configure.ac [--enable-libctf]: New, default yes.
index 2f0cd77ecfcd6af1bbce0b4d3706f2290bfcfad6..1d7cfbcf03106dcb23e0300364f660f0c2ff695e 100644 (file)
@@ -14151,8 +14151,9 @@ shdr_to_ctf_sect (ctf_sect_t *buf, Elf_Internal_Shdr *shdr, Filedata *filedata)
    it is passed, or a pointer to newly-allocated storage, in which case
    dump_ctf() will free it when it no longer needs it.  */
 
-static char *dump_ctf_indent_lines (ctf_sect_names_t sect ATTRIBUTE_UNUSED,
-                                   char *s, void *arg)
+static char *
+dump_ctf_indent_lines (ctf_sect_names_t sect ATTRIBUTE_UNUSED,
+                      char *s, void *arg)
 {
   const char *blanks = arg;
   char *new_s;
@@ -14162,6 +14163,55 @@ static char *dump_ctf_indent_lines (ctf_sect_names_t sect ATTRIBUTE_UNUSED,
   return new_s;
 }
 
+/* Dump one CTF archive member.  */
+
+static int
+dump_ctf_archive_member (ctf_file_t *ctf, const char *name, void *arg)
+{
+  ctf_file_t *parent = (ctf_file_t *) arg;
+  const char *things[] = {"Header", "Labels", "Data objects",
+                         "Function objects", "Variables", "Types", "Strings",
+                         ""};
+  const char **thing;
+  size_t i;
+
+  /* Only print out the name of non-default-named archive members.
+     The name .ctf appears everywhere, even for things that aren't
+     really archives, so printing it out is liable to be confusing.
+
+     The parent, if there is one, is the default-owned archive member:
+     avoid importing it into itself.  (This does no harm, but looks
+     confusing.)  */
+
+  if (strcmp (name, ".ctf") != 0)
+    {
+      printf (_("\nCTF archive member: %s:\n"), name);
+      ctf_import (ctf, parent);
+    }
+
+  for (i = 0, thing = things; *thing[0]; thing++, i++)
+    {
+      ctf_dump_state_t *s = NULL;
+      char *item;
+
+      printf ("\n  %s:\n", *thing);
+      while ((item = ctf_dump (ctf, &s, i, dump_ctf_indent_lines,
+                              (void *) "    ")) != NULL)
+       {
+         printf ("%s\n", item);
+         free (item);
+       }
+
+      if (ctf_errno (ctf))
+       {
+         error (_("Iteration failed: %s, %s\n"), *thing,
+                ctf_errmsg (ctf_errno (ctf)));
+         return 1;
+       }
+    }
+  return 0;
+}
+
 static bfd_boolean
 dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
 {
@@ -14175,16 +14225,12 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
   ctf_sect_t          ctfsect, symsect, strsect, parentsect;
   ctf_sect_t *        symsectp = NULL;
   ctf_sect_t *        strsectp = NULL;
-  ctf_file_t *        ctf = NULL;
-  ctf_file_t *        parent = NULL;
+  ctf_archive_t *      ctfa = NULL;
+  ctf_archive_t *      parenta = NULL, *lookparent;
+  ctf_file_t *         parent = NULL;
 
-  const char *things[] = {"Header", "Labels", "Data objects",
-                         "Function objects", "Variables", "Types", "Strings",
-                         ""};
-  const char **thing;
   int err;
   bfd_boolean ret = FALSE;
-  size_t i;
 
   shdr_to_ctf_sect (&ctfsect, section, filedata);
   data = get_section_contents (section, filedata);
@@ -14243,9 +14289,11 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
       parentsect.cts_data = parentdata;
     }
 
-  /* Load the CTF file and dump it.  */
+  /* Load the CTF file and dump it.  It may be a raw CTF section, or an archive:
+     libctf papers over the difference, so we can pretend it is always an
+     archive.  Possibly open the parent as well, if one was specified.  */
 
-  if ((ctf = ctf_bufopen (&ctfsect, symsectp, strsectp, &err)) == NULL)
+  if ((ctfa = ctf_arc_bufopen (&ctfsect, symsectp, strsectp, &err)) == NULL)
     {
       error (_("CTF open failure: %s\n"), ctf_errmsg (err));
       goto fail;
@@ -14253,13 +14301,24 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
 
   if (parentdata)
     {
-      if ((parent = ctf_bufopen (&parentsect, symsectp, strsectp, &err)) == NULL)
+      if ((parenta = ctf_arc_bufopen (&parentsect, symsectp, strsectp,
+                                     &err)) == NULL)
        {
          error (_("CTF open failure: %s\n"), ctf_errmsg (err));
          goto fail;
        }
+      lookparent = parenta;
+    }
+  else
+    lookparent = ctfa;
 
-      ctf_import (ctf, parent);
+  /* Assume that the applicable parent archive member is the default one.
+     (This is what all known implementations are expected to do, if they
+     put CTFs and their parents in archives together.)  */
+  if ((parent = ctf_arc_open_by_name (lookparent, NULL, &err)) == NULL)
+    {
+      error (_("CTF open failure: %s\n"), ctf_errmsg (err));
+      goto fail;
     }
 
   ret = TRUE;
@@ -14267,30 +14326,13 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
   printf (_("\nDump of CTF section '%s':\n"),
          printable_section_name (filedata, section));
 
-  for (i = 0, thing = things; *thing[0]; thing++, i++)
-    {
-      ctf_dump_state_t *s = NULL;
-      char *item;
-
-      printf ("\n  %s:\n", *thing);
-      while ((item = ctf_dump (ctf, &s, i, dump_ctf_indent_lines,
-                              (void *) "    ")) != NULL)
-       {
-         printf ("%s\n", item);
-         free (item);
-       }
-
-      if (ctf_errno (ctf))
-       {
-         error (_("Iteration failed: %s, %s\n"), *thing,
-                  ctf_errmsg (ctf_errno (ctf)));
-         ret = FALSE;
-       }
-    }
+  if (ctf_archive_iter (ctfa, dump_ctf_archive_member, parent) != 0)
+    ret = FALSE;
 
  fail:
-  ctf_file_close (ctf);
   ctf_file_close (parent);
+  ctf_close (ctfa);
+  ctf_close (parenta);
   free (parentdata);
   free (data);
   free (symdata);
index ab6d753ab5f1ef8460ae0e5fffedf7387a327630..dc7089ef80a05a9bce1f0c16593f84e4c1d56181 100644 (file)
@@ -1,3 +1,7 @@
+2020-06-26  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-api.h (ctf_arc_bufopen): New.
+
 2020-06-26  Pat Bernardi  <bernardi@adacore.com>
 
        * elf/m68k.h: Add enum for GNU object attribute with floating point
index efc49896c76e8132e23649d283cc3e70ca60028f..d6b05bc71fec6907c4f2ba42188e7a34626fb191 100644 (file)
@@ -263,6 +263,10 @@ extern void ctf_close (ctf_archive_t *);
 extern ctf_sect_t ctf_getdatasect (const ctf_file_t *);
 extern ctf_archive_t *ctf_get_arc (const ctf_file_t *);
 extern ctf_archive_t *ctf_arc_open (const char *, int *);
+extern ctf_archive_t *ctf_arc_bufopen (const ctf_sect_t *,
+                                      const ctf_sect_t *,
+                                      const ctf_sect_t *,
+                                      int *);
 extern void ctf_arc_close (ctf_archive_t *);
 extern ctf_file_t *ctf_arc_open_by_name (const ctf_archive_t *,
                                         const char *, int *);
index 3ab0a6c0376dd49df8343a585997410ca3fa9d8f..5e473a75cd021878b9d2c68d34c65fc294c15734 100644 (file)
@@ -1,3 +1,15 @@
+2020-06-26  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-impl.h (ctf_new_archive_internal): Declare.
+       (ctf_arc_bufopen): Remove.
+       (ctf_archive_internal) <ctfi_free_symsect>: New.
+       * ctf-archive.c (ctf_arc_close): Use it.
+       (ctf_arc_bufopen): Fuse into...
+       (ctf_new_archive_internal): ... this, moved across from...
+       * ctf-open-bfd.c: ... here.
+       (ctf_bfdopen_ctfsect): Use ctf_arc_bufopen.
+       * libctf.ver: Add it.
+
 2020-06-26  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-create.c (ctf_add_forward): Intern in the right namespace.
index 73d772ffc3846e2d7a4c1c12c9a78b74589f72ca..d27b27ef86c9f31cde139ebe20b72d9800a5820e 100644 (file)
@@ -343,21 +343,71 @@ search_modent_by_name (const void *key, const void *ent)
   return strcmp (k, &search_nametbl[le64toh (v->name_offset)]);
 }
 
-/* A trivial wrapper: open a CTF archive, from data in a buffer (which the
-   caller must preserve until ctf_arc_close() time).  Returns the archive, or
-   NULL and an error in *err (if not NULL).  */
-struct ctf_archive *
-ctf_arc_bufopen (const void *buf, size_t size _libctf_unused_, int *errp)
+/* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a
+   ctf_file.  Closes ARC and/or FP on error.  Arrange to free the SYMSECT or
+   STRSECT, as needed, on close.  */
+
+struct ctf_archive_internal *
+ctf_new_archive_internal (int is_archive, struct ctf_archive *arc,
+                         ctf_file_t *fp, const ctf_sect_t *symsect,
+                         const ctf_sect_t *strsect,
+                         int *errp)
 {
-  struct ctf_archive *arc = (struct ctf_archive *) buf;
+  struct ctf_archive_internal *arci;
 
-  if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
+  if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL)
     {
-      if (errp)
-       *errp = ECTF_FMT;
-      return NULL;
+      if (is_archive)
+       ctf_arc_close_internal (arc);
+      else
+       ctf_file_close (fp);
+      return (ctf_set_open_errno (errp, errno));
     }
-  return arc;
+  arci->ctfi_is_archive = is_archive;
+  if (is_archive)
+    arci->ctfi_archive = arc;
+  else
+    arci->ctfi_file = fp;
+  if (symsect)
+     memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
+  if (strsect)
+     memcpy (&arci->ctfi_strsect, strsect, sizeof (struct ctf_sect));
+  arci->ctfi_free_symsect = 0;
+
+  return arci;
+}
+
+/* Open a CTF archive or dictionary from data in a buffer (which the caller must
+   preserve until ctf_arc_close() time).  Returns the archive, or NULL and an
+   error in *err (if not NULL).  */
+ctf_archive_t *
+ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
+                const ctf_sect_t *strsect, int *errp)
+{
+  struct ctf_archive *arc = NULL;
+  int is_archive;
+  ctf_file_t *fp = NULL;
+
+  if (ctfsect->cts_size > sizeof (uint64_t) &&
+      ((*(uint64_t *) ctfsect->cts_data) == CTFA_MAGIC))
+    {
+      /* The archive is mmappable, so this operation is trivial.  */
+
+      is_archive = 1;
+      arc = (struct ctf_archive *) ctfsect->cts_data;
+    }
+  else
+    {
+      is_archive = 0;
+      if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
+       {
+         ctf_dprintf ("ctf_internal_open(): cannot open CTF: %s\n",
+                      ctf_errmsg (*errp));
+         return NULL;
+       }
+    }
+  return ctf_new_archive_internal (is_archive, arc, fp, symsect, strsect,
+                                  errp);
 }
 
 /* Open a CTF archive.  Returns the archive, or NULL and an error in *err (if
@@ -436,8 +486,8 @@ ctf_arc_close (ctf_archive_t *arc)
     ctf_arc_close_internal (arc->ctfi_archive);
   else
     ctf_file_close (arc->ctfi_file);
-  free ((void *) arc->ctfi_symsect.cts_data);
-  /* Do not free the ctfi_strsect: it is bound to the bfd.  */
+  if (arc->ctfi_free_symsect)
+    free ((void *) arc->ctfi_symsect.cts_data);
   free (arc->ctfi_data);
   if (arc->ctfi_bfd_close)
     arc->ctfi_bfd_close (arc);
index 56ef9a33ba21edcf0d2ae4debcfdb8f6a7b778b6..ade1a6e7cc453a760061391ea68294f3dd0b97dc 100644 (file)
@@ -309,6 +309,7 @@ struct ctf_archive_internal
   struct ctf_archive *ctfi_archive;
   ctf_sect_t ctfi_symsect;
   ctf_sect_t ctfi_strsect;
+  int ctfi_free_symsect;
   void *ctfi_data;
   bfd *ctfi_abfd;                  /* Optional source of section data.  */
   void (*ctfi_bfd_close) (struct ctf_archive_internal *);
@@ -435,8 +436,11 @@ extern void ctf_str_rollback (ctf_file_t *, ctf_snapshot_id_t);
 extern void ctf_str_purge_refs (ctf_file_t *);
 extern ctf_strs_writable_t ctf_str_write_strtab (ctf_file_t *);
 
+extern struct ctf_archive_internal *ctf_new_archive_internal
+       (int is_archive, struct ctf_archive *arc,
+        ctf_file_t *fp, const ctf_sect_t *symsect,
+        const ctf_sect_t *strsect, int *errp);
 extern struct ctf_archive *ctf_arc_open_internal (const char *, int *);
-extern struct ctf_archive *ctf_arc_bufopen (const void *, size_t, int *);
 extern void ctf_arc_close_internal (struct ctf_archive *);
 extern void *ctf_set_open_errno (int *, int);
 extern unsigned long ctf_set_errno (ctf_file_t *, int);
index 24c04038ad11c17259941fa9e9e56d659b1320b2..630a158af864eb8a1f209e2ed05da3572a414546 100644 (file)
 
 #include "elf-bfd.h"
 
-/* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a
-   ctf_file.  Closes ARC and/or FP on error.  Arrange to free the SYMSECT or
-   STRSECT, as needed, on close (though the STRSECT interior is bound to the bfd
-   * and is not actually freed by this machinery).  */
-
-static struct ctf_archive_internal *
-ctf_new_archive_internal (int is_archive, struct ctf_archive *arc,
-                         ctf_file_t *fp, const ctf_sect_t *symsect,
-                         const ctf_sect_t *strsect,
-                         int *errp)
-{
-  struct ctf_archive_internal *arci;
-
-  if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL)
-    {
-      if (is_archive)
-       ctf_arc_close_internal (arc);
-      else
-       ctf_file_close (fp);
-      return (ctf_set_open_errno (errp, errno));
-    }
-  arci->ctfi_is_archive = is_archive;
-  if (is_archive)
-    arci->ctfi_archive = arc;
-  else
-    arci->ctfi_file = fp;
-  if (symsect)
-     memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
-  if (strsect)
-     memcpy (&arci->ctfi_strsect, strsect, sizeof (struct ctf_sect));
-
-  return arci;
-}
-
 /* Free the BFD bits of a CTF file on ctf_arc_close().  */
 
 static void
@@ -107,6 +73,7 @@ ctf_bfdopen (struct bfd *abfd, int *errp)
 
   if ((arc = ctf_bfdopen_ctfsect (abfd, &ctfsect, errp)) != NULL)
     {
+      /* This frees the cts_data later.  */
       arc->ctfi_data = (void *) ctfsect.cts_data;
       return arc;
     }
@@ -116,20 +83,16 @@ ctf_bfdopen (struct bfd *abfd, int *errp)
 }
 
 /* Open a CTF file given the specified BFD and CTF section (which may contain a
-   CTF archive or a file).  Takes ownership of the ctfsect, and frees it
-   later.  */
+   CTF archive or a file).  */
 
 ctf_archive_t *
 ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
                     const ctf_sect_t *ctfsect, int *errp)
 {
-  struct ctf_archive *arc = NULL;
   ctf_archive_t *arci;
-  ctf_file_t *fp = NULL;
   ctf_sect_t *symsectp = NULL;
   ctf_sect_t *strsectp = NULL;
   const char *bfderrstr = NULL;
-  int is_archive;
 
 #ifdef HAVE_BFD_ELF
   ctf_sect_t symsect, strsect;
@@ -192,30 +155,13 @@ ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
     }
 #endif
 
-  if (ctfsect->cts_size > sizeof (uint64_t) &&
-      ((*(uint64_t *) ctfsect->cts_data) == CTFA_MAGIC))
-    {
-      is_archive = 1;
-      if ((arc = ctf_arc_bufopen ((void *) ctfsect->cts_data,
-                                 ctfsect->cts_size, errp)) == NULL)
-       goto err_free_str;
-    }
-  else
+  arci = ctf_arc_bufopen (ctfsect, symsectp, strsectp, errp);
+  if (arci)
     {
-      is_archive = 0;
-      if ((fp = ctf_bufopen (ctfsect, symsectp, strsectp, errp)) == NULL)
-       {
-         ctf_dprintf ("ctf_internal_open(): cannot open CTF: %s\n",
-                      ctf_errmsg (*errp));
-         goto err_free_str;
-       }
+      /* Request freeing of the symsect.  */
+      arci->ctfi_free_symsect = 1;
+      return arci;
     }
-  arci = ctf_new_archive_internal (is_archive, arc, fp, symsectp, strsectp,
-                                  errp);
-
-  if (arci)
-    return arci;
- err_free_str: ;
 #ifdef HAVE_BFD_ELF
  err_free_sym:
   free (symtab);
index 14ca4131ca8e30857da06a32728aedb46f81a43c..aad304bc0d9caf9f84e80b848bd09fa3cf9b7be1 100644 (file)
@@ -128,6 +128,7 @@ LIBCTF_1.0 {
        ctf_arc_write;
        ctf_arc_write_fd;
        ctf_arc_open;
+       ctf_arc_bufopen;
        ctf_arc_close;
        ctf_arc_open_by_name;
        ctf_arc_open_by_name_sections;
This page took 0.046634 seconds and 4 git commands to generate.