]> Git Repo - binutils.git/blobdiff - libctf/ctf-archive.c
libctf, ld: prohibit getting the size or alignment of forwards
[binutils.git] / libctf / ctf-archive.c
index 457855207bffd349f95d0fb801b238e558c5eac2..d8c4845b643b93287036c6f2d535430b03c3f04a 100644 (file)
@@ -1,5 +1,5 @@
 /* CTF archive files.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
+   Copyright (C) 2019-2021 Free Software Foundation, Inc.
 
    This file is part of libctf.
 
 #include <sys/mman.h>
 #endif
 
-static off_t arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold);
-static ctf_file_t *ctf_arc_open_by_offset (const struct ctf_archive *arc,
-                                          const ctf_sect_t *symsect,
-                                          const ctf_sect_t *strsect,
-                                          size_t offset, int *errp);
+static off_t arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold);
+static ctf_dict_t *ctf_dict_open_by_offset (const struct ctf_archive *arc,
+                                           const ctf_sect_t *symsect,
+                                           const ctf_sect_t *strsect,
+                                           size_t offset, int little_endian,
+                                           int *errp);
 static int sort_modent_by_name (const void *one, const void *two, void *n);
 static void *arc_mmap_header (int fd, size_t headersz);
 static void *arc_mmap_file (int fd, size_t size);
 static int arc_mmap_writeout (int fd, void *header, size_t headersz,
                              const char **errmsg);
 static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg);
+static void ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp);
+
+/* Flag to indicate "symbol not present" in
+   ctf_archive_internal.ctfi_symdicts.  Never initialized.  */
+static ctf_dict_t enosym;
 
 /* Write out a CTF archive to the start of the file referenced by the passed-in
-   fd.  The entries in CTF_FILES are referenced by name: the names are passed in
-   the names array, which must have CTF_FILES entries.
+   fd.  The entries in CTF_DICTS are referenced by name: the names are passed in
+   the names array, which must have CTF_DICTS entries.
 
    Returns 0 on success, or an errno, or an ECTF_* value.  */
 int
-ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
+ctf_arc_write_fd (int fd, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
                  const char **names, size_t threshold)
 {
   const char *errmsg;
@@ -66,14 +72,14 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
   struct ctf_archive_modent *modent;
 
   ctf_dprintf ("Writing CTF archive with %lu files\n",
-              (unsigned long) ctf_file_cnt);
+              (unsigned long) ctf_dict_cnt);
 
   /* Figure out the size of the mmap()ed header, including the
      ctf_archive_modent array.  We assume that all of this needs no
      padding: a likely assumption, given that it's all made up of
      uint64_t's.  */
   headersz = sizeof (struct ctf_archive)
-    + (ctf_file_cnt * sizeof (uint64_t) * 2);
+    + (ctf_dict_cnt * sizeof (uint64_t) * 2);
   ctf_dprintf ("headersz is %lu\n", (unsigned long) headersz);
 
   /* From now on we work in two pieces: an mmap()ed region from zero up to the
@@ -82,26 +88,26 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
   ctf_startoffs = headersz;
   if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
     {
-      errmsg = "ctf_arc_write(): cannot extend file while writing: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot extend file while writing");
       goto err;
     }
 
   if (write (fd, &dummy, 1) < 0)
     {
-      errmsg = "ctf_arc_write(): cannot extend file while writing: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot extend file while writing");
       goto err;
     }
 
   if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
     {
-      errmsg = "ctf_arc_write(): Cannot mmap(): %s\n";
+      errmsg = N_("ctf_arc_write(): cannot mmap");
       goto err;
     }
 
   /* Fill in everything we can, which is everything other than the name
      table offset.  */
   archdr->ctfa_magic = htole64 (CTFA_MAGIC);
-  archdr->ctfa_nfiles = htole64 (ctf_file_cnt);
+  archdr->ctfa_ndicts = htole64 (ctf_dict_cnt);
   archdr->ctfa_ctfs = htole64 (ctf_startoffs);
 
   /* We could validate that all CTF files have the same data model, but
@@ -112,8 +118,8 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
      this case, but we must be sure not to dereference uninitialized
      memory.)  */
 
-  if (ctf_file_cnt > 0)
-    archdr->ctfa_model = htole64 (ctf_getmodel (ctf_files[0]));
+  if (ctf_dict_cnt > 0)
+    archdr->ctfa_model = htole64 (ctf_getmodel (ctf_dicts[0]));
 
   /* Now write out the CTFs: ctf_archive_modent array via the mapping,
      ctfs via write().  The names themselves have not been written yet: we
@@ -122,35 +128,35 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
 
     The name table is not sorted.  */
 
-  for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_nfiles); i++)
+  for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_ndicts); i++)
     namesz += strlen (names[i]) + 1;
 
   nametbl = malloc (namesz);
   if (nametbl == NULL)
     {
-      errmsg = "Error writing named CTF to archive: %s\n";
+      errmsg = N_("ctf_arc_write(): error writing named CTF to archive");
       goto err_unmap;
     }
 
   for (i = 0, namesz = 0,
        modent = (ctf_archive_modent_t *) ((char *) archdr
                                          + sizeof (struct ctf_archive));
-       i < le64toh (archdr->ctfa_nfiles); i++)
+       i < le64toh (archdr->ctfa_ndicts); i++)
     {
       off_t off;
 
       strcpy (&nametbl[namesz], names[i]);
 
-      off = arc_write_one_ctf (ctf_files[i], fd, threshold);
+      off = arc_write_one_ctf (ctf_dicts[i], fd, threshold);
       if ((off < 0) && (off > -ECTF_BASE))
        {
-         errmsg = "ctf_arc_write(): Cannot determine file "
-           "position while writing to archive: %s";
+         errmsg = N_("ctf_arc_write(): cannot determine file "
+                     "position while writing to archive");
          goto err_free;
        }
       if (off < 0)
        {
-         errmsg = "ctf_arc_write(): Cannot write CTF file to archive: %s\n";
+         errmsg = N_("ctf_arc_write(): cannot write CTF file to archive");
          errno = off * -1;
          goto err_free;
        }
@@ -163,7 +169,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
 
   ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr
                                         + sizeof (struct ctf_archive)),
-              le64toh (archdr->ctfa_nfiles),
+              le64toh (archdr->ctfa_ndicts),
               sizeof (struct ctf_archive_modent), sort_modent_by_name,
               nametbl);
 
@@ -171,8 +177,8 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
 
   if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
     {
-      errmsg = "ctf_arc_write(): Cannot get current file position "
-       "in archive: %s\n";
+      errmsg = N_("ctf_arc_write(): cannot get current file position "
+                 "in archive");
       goto err_free;
     }
   archdr->ctfa_names = htole64 (nameoffs);
@@ -182,7 +188,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt,
       ssize_t len;
       if ((len = write (fd, np, namesz)) < 0)
        {
-         errmsg = "ctf_arc_write(): Cannot write name table to archive: %s\n";
+         errmsg = N_("ctf_arc_write(): cannot write name table to archive");
          goto err_free;
        }
       namesz -= len;
@@ -201,19 +207,22 @@ err_free:
 err_unmap:
   arc_mmap_unmap (archdr, headersz, NULL);
 err:
-  ctf_dprintf (errmsg, errno < ECTF_BASE ? strerror (errno) :
-              ctf_errmsg (errno));
+  /* We report errors into the first file in the archive, if any: if this is a
+     zero-file archive, put it in the open-errors stream for lack of anywhere
+     else for it to go.  */
+  ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno, "%s",
+               gettext (errmsg));
   return errno;
 }
 
-/* Write out a CTF archive.  The entries in CTF_FILES are referenced by name:
-   the names are passed in the names array, which must have CTF_FILES entries.
+/* Write out a CTF archive.  The entries in CTF_DICTS are referenced by name:
+   the names are passed in the names array, which must have CTF_DICTS entries.
 
    If the filename is NULL, create a temporary file and return a pointer to it.
 
    Returns 0 on success, or an errno, or an ECTF_* value.  */
 int
-ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
+ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
               const char **names, size_t threshold)
 {
   int err;
@@ -221,18 +230,18 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
 
   if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
     {
-      ctf_dprintf ("ctf_arc_write(): cannot create %s: %s\n", file,
-                  strerror (errno));
+      ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
+                   _("ctf_arc_write(): cannot create %s"), file);
       return errno;
     }
 
-  err = ctf_arc_write_fd (fd, ctf_files, ctf_file_cnt, names, threshold);
+  err = ctf_arc_write_fd (fd, ctf_dicts, ctf_dict_cnt, names, threshold);
   if (err)
     goto err_close;
 
   if ((err = close (fd)) < 0)
-    ctf_dprintf ("ctf_arc_write(): Cannot close after writing to archive: "
-                "%s\n", strerror (errno));
+    ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
+                 _("ctf_arc_write(): cannot close after writing to archive"));
   goto err;
 
  err_close:
@@ -249,13 +258,13 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
    negative errno or ctf_errno value.  On error, the file position may no longer
    be at the end of the file.  */
 static off_t
-arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold)
+arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
 {
   off_t off, end_off;
   uint64_t ctfsz = 0;
   char *ctfszp;
   size_t ctfsz_len;
-  int (*writefn) (ctf_file_t * fp, int fd);
+  int (*writefn) (ctf_dict_t * fp, int fd);
 
   if (ctf_serialize (f) < 0)
     return f->ctf_errno * -1;
@@ -335,13 +344,13 @@ search_modent_by_name (const void *key, const void *ent, void *arg)
 }
 
 /* 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
+   ctf_dict.  Closes ARC and/or FP on error.  Arrange to free the SYMSECT or
    STRSECT, as needed, on close.  Possibly do not unmap on close.  */
 
 struct ctf_archive_internal *
 ctf_new_archive_internal (int is_archive, int unmap_on_close,
                          struct ctf_archive *arc,
-                         ctf_file_t *fp, const ctf_sect_t *symsect,
+                         ctf_dict_t *fp, const ctf_sect_t *symsect,
                          const ctf_sect_t *strsect,
                          int *errp)
 {
@@ -355,14 +364,14 @@ ctf_new_archive_internal (int is_archive, int unmap_on_close,
            ctf_arc_close_internal (arc);
        }
       else
-       ctf_file_close (fp);
+       ctf_dict_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;
+    arci->ctfi_dict = fp;
   if (symsect)
      memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
   if (strsect)
@@ -370,10 +379,40 @@ ctf_new_archive_internal (int is_archive, int unmap_on_close,
   arci->ctfi_free_symsect = 0;
   arci->ctfi_free_strsect = 0;
   arci->ctfi_unmap_on_close = unmap_on_close;
+  arci->ctfi_symsect_little_endian = -1;
 
   return arci;
 }
 
+/* Set the symbol-table endianness of an archive (defaulting the symtab
+   endianness of all ctf_file_t's opened from that archive).  */
+void
+ctf_arc_symsect_endianness (ctf_archive_t *arc, int little_endian)
+{
+  arc->ctfi_symsect_little_endian = !!little_endian;
+  if (!arc->ctfi_is_archive)
+    ctf_symsect_endianness (arc->ctfi_dict, arc->ctfi_symsect_little_endian);
+}
+
+/* Get the CTF preamble from data in a buffer, which may be either an archive or
+   a CTF dict.  If multiple dicts are present in an archive, the preamble comes
+   from an arbitrary dict.  The preamble is a pointer into the ctfsect passed
+   in.  */
+
+const ctf_preamble_t *
+ctf_arc_bufpreamble (const ctf_sect_t *ctfsect)
+{
+  if (ctfsect->cts_size > sizeof (uint64_t) &&
+      (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
+    {
+      struct ctf_archive *arc = (struct ctf_archive *) ctfsect->cts_data;
+      return (const ctf_preamble_t *) ((char *) arc + le64toh (arc->ctfa_ctfs)
+                                      + sizeof (uint64_t));
+    }
+  else
+    return (const ctf_preamble_t *) ctfsect->cts_data;
+}
+
 /* 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).  */
@@ -383,10 +422,10 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 {
   struct ctf_archive *arc = NULL;
   int is_archive;
-  ctf_file_t *fp = NULL;
+  ctf_dict_t *fp = NULL;
 
   if (ctfsect->cts_size > sizeof (uint64_t) &&
-      ((*(uint64_t *) ctfsect->cts_data) == CTFA_MAGIC))
+      (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
     {
       /* The archive is mmappable, so this operation is trivial.
 
@@ -404,8 +443,7 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       is_archive = 0;
       if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
        {
-         ctf_dprintf ("ctf_arc_bufopen(): cannot open CTF: %s\n",
-                      ctf_errmsg (*errp));
+         ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF"));
          return NULL;
        }
     }
@@ -426,24 +464,24 @@ ctf_arc_open_internal (const char *filename, int *errp)
   libctf_init_debug();
   if ((fd = open (filename, O_RDONLY)) < 0)
     {
-      errmsg = "ctf_arc_open(): cannot open %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot open %s");
       goto err;
     }
   if (fstat (fd, &s) < 0)
     {
-      errmsg = "ctf_arc_open(): cannot stat %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot stat %s");
       goto err_close;
     }
 
   if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
     {
-      errmsg = "ctf_arc_open(): Cannot read in %s: %s\n";
+      errmsg = N_("ctf_arc_open(): cannot read in %s");
       goto err_close;
     }
 
   if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
     {
-      errmsg = "ctf_arc_open(): Invalid magic number";
+      errmsg = N_("ctf_arc_open(): %s: invalid magic number");
       errno = ECTF_FMT;
       goto err_unmap;
     }
@@ -462,8 +500,7 @@ err_close:
 err:
   if (errp)
     *errp = errno;
-  ctf_dprintf (errmsg, filename, errno < ECTF_BASE ? strerror (errno) :
-              ctf_errmsg (errno));
+  ctf_err_warn (NULL, 0, errno, gettext (errmsg), filename);
   return NULL;
 }
 
@@ -491,7 +528,10 @@ ctf_arc_close (ctf_archive_t *arc)
        ctf_arc_close_internal (arc->ctfi_archive);
     }
   else
-    ctf_file_close (arc->ctfi_file);
+    ctf_dict_close (arc->ctfi_dict);
+  free (arc->ctfi_syms);
+  free (arc->ctfi_symdicts);
+  ctf_dynhash_destroy (arc->ctfi_dicts);
   if (arc->ctfi_free_symsect)
     free ((void *) arc->ctfi_symsect.cts_data);
   if (arc->ctfi_free_strsect)
@@ -502,13 +542,14 @@ ctf_arc_close (ctf_archive_t *arc)
   free (arc);
 }
 
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
    non-NULL.  A name of NULL means to open the default file.  */
-static ctf_file_t *
-ctf_arc_open_by_name_internal (const struct ctf_archive *arc,
-                              const ctf_sect_t *symsect,
-                              const ctf_sect_t *strsect,
-                              const char *name, int *errp)
+static ctf_dict_t *
+ctf_dict_open_internal (const struct ctf_archive *arc,
+                       const ctf_sect_t *symsect,
+                       const ctf_sect_t *strsect,
+                       const char *name, int little_endian,
+                       int *errp)
 {
   struct ctf_archive_modent *modent;
   const char *search_nametbl;
@@ -516,13 +557,13 @@ ctf_arc_open_by_name_internal (const struct ctf_archive *arc,
   if (name == NULL)
     name = _CTF_SECTION;                /* The default name.  */
 
-  ctf_dprintf ("ctf_arc_open_by_name(%s): opening\n", name);
+  ctf_dprintf ("ctf_dict_open_internal(%s): opening\n", name);
 
   modent = (ctf_archive_modent_t *) ((char *) arc
                                     + sizeof (struct ctf_archive));
 
   search_nametbl = (const char *) arc + le64toh (arc->ctfa_names);
-  modent = bsearch_r (name, modent, le64toh (arc->ctfa_nfiles),
+  modent = bsearch_r (name, modent, le64toh (arc->ctfa_ndicts),
                      sizeof (struct ctf_archive_modent),
                      search_modent_by_name, (void *) search_nametbl);
 
@@ -535,30 +576,35 @@ ctf_arc_open_by_name_internal (const struct ctf_archive *arc,
       return NULL;
     }
 
-  return ctf_arc_open_by_offset (arc, symsect, strsect,
-                                le64toh (modent->ctf_offset), errp);
+  return ctf_dict_open_by_offset (arc, symsect, strsect,
+                                 le64toh (modent->ctf_offset),
+                                 little_endian, errp);
 }
 
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
    non-NULL.  A name of NULL means to open the default file.
 
    Use the specified string and symbol table sections.
 
    Public entry point.  */
-ctf_file_t *
-ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
-                              const ctf_sect_t *symsect,
-                              const ctf_sect_t *strsect,
-                              const char *name,
-                              int *errp)
+ctf_dict_t *
+ctf_dict_open_sections (const ctf_archive_t *arc,
+                       const ctf_sect_t *symsect,
+                       const ctf_sect_t *strsect,
+                       const char *name,
+                       int *errp)
 {
   if (arc->ctfi_is_archive)
     {
-      ctf_file_t *ret;
-      ret = ctf_arc_open_by_name_internal (arc->ctfi_archive, symsect, strsect,
-                                          name, errp);
+      ctf_dict_t *ret;
+      ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
+                                   name, arc->ctfi_symsect_little_endian,
+                                   errp);
       if (ret)
-       ret->ctf_archive = (ctf_archive_t *) arc;
+       {
+         ret->ctf_archive = (ctf_archive_t *) arc;
+         ctf_arc_import_parent (arc, ret);
+       }
       return ret;
     }
 
@@ -568,19 +614,19 @@ ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
        *errp = ECTF_ARNNAME;
       return NULL;
     }
-  arc->ctfi_file->ctf_archive = (ctf_archive_t *) arc;
+  arc->ctfi_dict->ctf_archive = (ctf_archive_t *) arc;
 
-  /* Bump the refcount so that the user can ctf_file_close() it.  */
-  arc->ctfi_file->ctf_refcnt++;
-  return arc->ctfi_file;
+  /* Bump the refcount so that the user can ctf_dict_close() it.  */
+  arc->ctfi_dict->ctf_refcnt++;
+  return arc->ctfi_dict;
 }
 
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
    non-NULL.  A name of NULL means to open the default file.
 
    Public entry point.  */
-ctf_file_t *
-ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name, int *errp)
+ctf_dict_t *
+ctf_dict_open (const ctf_archive_t *arc, const char *name, int *errp)
 {
   const ctf_sect_t *symsect = &arc->ctfi_symsect;
   const ctf_sect_t *strsect = &arc->ctfi_strsect;
@@ -590,21 +636,82 @@ ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name, int *errp)
   if (strsect->cts_name == NULL)
     strsect = NULL;
 
-  return ctf_arc_open_by_name_sections (arc, symsect, strsect, name, errp);
+  return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
+}
+
+static void
+ctf_cached_dict_close (void *fp)
+{
+  ctf_dict_close ((ctf_dict_t *) fp);
+}
+
+/* Return the ctf_dict_t with the given name and cache it in the
+   archive's ctfi_dicts.  */
+static ctf_dict_t *
+ctf_dict_open_cached (ctf_archive_t *arc, const char *name, int *errp)
+{
+  ctf_dict_t *fp;
+  char *dupname;
+
+  /* Just return from the cache if possible.  */
+  if (arc->ctfi_dicts
+      && ((fp = ctf_dynhash_lookup (arc->ctfi_dicts, name)) != NULL))
+    {
+      fp->ctf_refcnt++;
+      return fp;
+    }
+
+  /* Not yet cached: open it.  */
+  fp = ctf_dict_open (arc, name, errp);
+  dupname = strdup (name);
+
+  if (!fp || !dupname)
+    goto oom;
+
+  if (arc->ctfi_dicts == NULL)
+    if ((arc->ctfi_dicts
+        = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
+                              free, ctf_cached_dict_close)) == NULL)
+      goto oom;
+
+  if (ctf_dynhash_insert (arc->ctfi_dicts, dupname, fp) < 0)
+    goto oom;
+  fp->ctf_refcnt++;
+
+  return fp;
+
+ oom:
+  ctf_dict_close (fp);
+  free (dupname);
+  if (errp)
+    *errp = ENOMEM;
+  return NULL;
+}
+
+/* Flush any caches the CTF archive may have open.  */
+void
+ctf_arc_flush_caches (ctf_archive_t *wrapper)
+{
+  free (wrapper->ctfi_symdicts);
+  free (wrapper->ctfi_syms);
+  ctf_dynhash_destroy (wrapper->ctfi_dicts);
+  wrapper->ctfi_symdicts = NULL;
+  wrapper->ctfi_syms = NULL;
+  wrapper->ctfi_dicts = NULL;
 }
 
-/* Return the ctf_file_t at the given ctfa_ctfs-relative offset, or NULL if
+/* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
    none, setting 'err' if non-NULL.  */
-static ctf_file_t *
-ctf_arc_open_by_offset (const struct ctf_archive *arc,
-                       const ctf_sect_t *symsect,
-                       const ctf_sect_t *strsect, size_t offset,
-                       int *errp)
+static ctf_dict_t *
+ctf_dict_open_by_offset (const struct ctf_archive *arc,
+                        const ctf_sect_t *symsect,
+                        const ctf_sect_t *strsect, size_t offset,
+                        int little_endian, int *errp)
 {
   ctf_sect_t ctfsect;
-  ctf_file_t *fp;
+  ctf_dict_t *fp;
 
-  ctf_dprintf ("ctf_arc_open_by_offset(%lu): opening\n", (unsigned long) offset);
+  ctf_dprintf ("ctf_dict_open_by_offset(%lu): opening\n", (unsigned long) offset);
 
   memset (&ctfsect, 0, sizeof (ctf_sect_t));
 
@@ -616,10 +723,51 @@ ctf_arc_open_by_offset (const struct ctf_archive *arc,
   ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
   fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
   if (fp)
-    ctf_setmodel (fp, le64toh (arc->ctfa_model));
+    {
+      ctf_setmodel (fp, le64toh (arc->ctfa_model));
+      if (little_endian >= 0)
+       ctf_symsect_endianness (fp, little_endian);
+    }
   return fp;
 }
 
+/* Backward compatibility.  */
+ctf_dict_t *
+ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name,
+                     int *errp)
+{
+  return ctf_dict_open (arc, name, errp);
+}
+
+ctf_dict_t *
+ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
+                              const ctf_sect_t *symsect,
+                              const ctf_sect_t *strsect,
+                              const char *name,
+                              int *errp)
+{
+  return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
+}
+
+/* Import the parent into a ctf archive, if this is a child, the parent is not
+   already set, and a suitable archive member exists.  No error is raised if
+   this is not possible: this is just a best-effort helper operation to give
+   people useful dicts to start with.  */
+static void
+ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp)
+{
+  if ((fp->ctf_flags & LCTF_CHILD) && fp->ctf_parname && !fp->ctf_parent)
+    {
+      ctf_dict_t *parent = ctf_dict_open_cached ((ctf_archive_t *) arc,
+                                                fp->ctf_parname, NULL);
+      if (parent)
+       {
+         ctf_import (fp, parent);
+         ctf_dict_close (parent);
+       }
+    }
+}
+
 /* Return the number of members in an archive.  */
 size_t
 ctf_archive_count (const ctf_archive_t *wrapper)
@@ -627,7 +775,140 @@ ctf_archive_count (const ctf_archive_t *wrapper)
   if (!wrapper->ctfi_is_archive)
     return 1;
 
-  return wrapper->ctfi_archive->ctfa_nfiles;
+  return wrapper->ctfi_archive->ctfa_ndicts;
+}
+
+/* Look up a symbol in an archive.  Return the dict in the archive that the
+   symbol is found in, and (optionally) the ctf_id_t of the symbol in that dict
+   (so you don't have to look it up yourself).  The dict and mapping are both
+   cached, so repeated lookups are nearly free.
+
+   As usual, you should ctf_dict_close() the returned dict once you are done
+   with it.
+
+   Returns NULL on error, and an error in errp (if set).  */
+
+ctf_dict_t *
+ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
+                      ctf_id_t *typep, int *errp)
+{
+  ctf_dict_t *fp;
+  ctf_id_t type;
+
+  /* The usual non-archive-transparent-wrapper special case.  */
+  if (!wrapper->ctfi_is_archive)
+    {
+      if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
+       {
+         if (errp)
+           *errp = ctf_errno (wrapper->ctfi_dict);
+         return NULL;
+       }
+      if (typep)
+       *typep = type;
+      wrapper->ctfi_dict->ctf_refcnt++;
+      return wrapper->ctfi_dict;
+    }
+
+  if (wrapper->ctfi_symsect.cts_name == NULL
+      || wrapper->ctfi_symsect.cts_data == NULL
+      || wrapper->ctfi_symsect.cts_size == 0
+      || wrapper->ctfi_symsect.cts_entsize == 0)
+    {
+      if (errp)
+       *errp = ECTF_NOSYMTAB;
+      return NULL;
+    }
+
+  /* Make enough space for all possible symbols, if not already done.
+     We cache both the ctf_id_t and the originating dictionary of all symbols.
+     The dict links are weak, to the dictionaries cached in ctfi_dicts: their
+     refcnts are *not* bumped.  */
+
+  if (!wrapper->ctfi_syms)
+    {
+      if ((wrapper->ctfi_syms = calloc (wrapper->ctfi_symsect.cts_size
+                                       / wrapper->ctfi_symsect.cts_entsize,
+                                       sizeof (ctf_id_t))) == NULL)
+       {
+         if (errp)
+           *errp = ENOMEM;
+         return NULL;
+       }
+    }
+  if (!wrapper->ctfi_symdicts)
+    {
+      if ((wrapper->ctfi_symdicts = calloc (wrapper->ctfi_symsect.cts_size
+                                           / wrapper->ctfi_symsect.cts_entsize,
+                                           sizeof (ctf_dict_t *))) == NULL)
+       {
+         if (errp)
+           *errp = ENOMEM;
+         return NULL;
+       }
+    }
+
+  /* Perhaps it's cached.  */
+  if (wrapper->ctfi_symdicts[symidx] != NULL)
+    {
+      if (wrapper->ctfi_symdicts[symidx] == &enosym)
+       {
+         if (errp)
+           *errp = ECTF_NOTYPEDAT;
+         if (typep)
+           *typep = CTF_ERR;
+         return NULL;
+       }
+
+      if (typep)
+       *typep = wrapper->ctfi_syms[symidx];
+      wrapper->ctfi_symdicts[symidx]->ctf_refcnt++;
+      return wrapper->ctfi_symdicts[symidx];
+    }
+
+  /* Not cached: find it and cache it.  We must track open errors ourselves even
+     if our caller doesn't, to be able to distinguish no-error end-of-iteration
+     from open errors.  */
+
+  int local_err;
+  int *local_errp;
+  ctf_next_t *i = NULL;
+  const char *name;
+
+  if (errp)
+    local_errp = errp;
+  else
+    local_errp = &local_err;
+
+  while ((fp = ctf_archive_next (wrapper, &i, &name, 0, local_errp)) != NULL)
+    {
+      if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
+       {
+         wrapper->ctfi_syms[symidx] = type;
+         wrapper->ctfi_symdicts[symidx] = fp;
+         ctf_next_destroy (i);
+
+         if (typep)
+           *typep = type;
+         return fp;
+       }
+      ctf_dict_close (fp);
+    }
+  if (*local_errp != ECTF_NEXT_END)
+    {
+      ctf_next_destroy (i);
+      return NULL;
+    }
+  /* Don't leak end-of-iteration to the caller.  */
+  *local_errp = 0;
+
+  wrapper->ctfi_symdicts[symidx] = &enosym;
+
+  if (errp)
+    *errp = ECTF_NOTYPEDAT;
+  if (typep)
+    *typep = CTF_ERR;
+  return NULL;
 }
 
 /* Raw iteration over all CTF files in an archive.  We pass the raw data for all
@@ -645,7 +926,7 @@ ctf_archive_raw_iter_internal (const struct ctf_archive *arc,
                                     + sizeof (struct ctf_archive));
   nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
 
-  for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+  for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
     {
       const char *name;
       char *fp;
@@ -685,7 +966,7 @@ ctf_archive_iter_internal (const ctf_archive_t *wrapper,
 {
   int rc;
   size_t i;
-  ctf_file_t *f;
+  ctf_dict_t *f;
   struct ctf_archive_modent *modent;
   const char *nametbl;
 
@@ -693,23 +974,26 @@ ctf_archive_iter_internal (const ctf_archive_t *wrapper,
                                     + sizeof (struct ctf_archive));
   nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
 
-  for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+  for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
     {
       const char *name;
 
       name = &nametbl[le64toh (modent[i].name_offset)];
-      if ((f = ctf_arc_open_by_name_internal (arc, symsect, strsect,
-                                             name, &rc)) == NULL)
+      if ((f = ctf_dict_open_internal (arc, symsect, strsect,
+                                      name,
+                                      wrapper->ctfi_symsect_little_endian,
+                                      &rc)) == NULL)
        return rc;
 
       f->ctf_archive = (ctf_archive_t *) wrapper;
+      ctf_arc_import_parent (wrapper, f);
       if ((rc = func (f, name, data)) != 0)
        {
-         ctf_file_close (f);
+         ctf_dict_close (f);
          return rc;
        }
 
-      ctf_file_close (f);
+      ctf_dict_close (f);
     }
   return 0;
 }
@@ -732,23 +1016,25 @@ ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
     return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect,
                                      func, data);
 
-  return func (arc->ctfi_file, _CTF_SECTION, data);
+  return func (arc->ctfi_dict, _CTF_SECTION, data);
 }
 
 /* Iterate over all CTF files in an archive, returning each dict in turn as a
-   ctf_file_t, and NULL on error or end of iteration.  It is the caller's
+   ctf_dict_t, and NULL on error or end of iteration.  It is the caller's
    responsibility to close it.  Parent dicts may be skipped.  Regardless of
    whether they are skipped or not, the caller must ctf_import the parent if
    need be.
 
+   The archive member is cached for rapid return on future calls.
+
    We identify parents by name rather than by flag value: for now, with the
    linker only emitting parents named _CTF_SECTION, this works well enough.  */
 
-ctf_file_t *
+ctf_dict_t *
 ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **name,
                  int skip_parent, int *errp)
 {
-  ctf_file_t *f;
+  ctf_dict_t *f;
   ctf_next_t *i = *it;
   struct ctf_archive *arc;
   struct ctf_archive_modent *modent;
@@ -782,7 +1068,7 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
       return NULL;
     }
 
-  /* Iteration is made a bit more complex by the need to handle ctf_file_t's
+  /* Iteration is made a bit more complex by the need to handle ctf_dict_t's
      transparently wrapped in a single-member archive.  These are parents: if
      skip_parent is on, they are skipped and the iterator terminates
      immediately.  */
@@ -792,8 +1078,8 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
       i->ctn_n++;
       if (!skip_parent)
        {
-         wrapper->ctfi_file->ctf_refcnt++;
-         return wrapper->ctfi_file;
+         wrapper->ctfi_dict->ctf_refcnt++;
+         return wrapper->ctfi_dict;
        }
     }
 
@@ -803,12 +1089,9 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
      is the parent (i.e. at most two iterations, but possibly an early return if
      *all* we have is a parent).  */
 
-  const ctf_sect_t *symsect;
-  const ctf_sect_t *strsect;
-
   do
     {
-      if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_nfiles)))
+      if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_ndicts)))
        {
          ctf_next_destroy (i);
          *it = NULL;
@@ -817,14 +1100,6 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
          return NULL;
        }
 
-      symsect = &wrapper->ctfi_symsect;
-      strsect = &wrapper->ctfi_strsect;
-
-      if (symsect->cts_name == NULL)
-       symsect = NULL;
-      if (strsect->cts_name == NULL)
-       strsect = NULL;
-
       modent = (ctf_archive_modent_t *) ((char *) arc
                                         + sizeof (struct ctf_archive));
       nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
@@ -836,9 +1111,7 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
   if (name)
     *name = name_;
 
-  f = ctf_arc_open_by_name_internal (arc, symsect, strsect,
-                                    name_, errp);
-  f->ctf_archive = (ctf_archive_t *) wrapper;
+  f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp);
   return f;
 }
 
@@ -872,7 +1145,8 @@ static int arc_mmap_writeout (int fd _libctf_unused_, void *header,
     if (msync (header, headersz, MS_ASYNC) < 0)
     {
       if (errmsg)
-       *errmsg = "arc_mmap_writeout(): Cannot sync after writing to %s: %s\n";
+       *errmsg = N_("arc_mmap_writeout(): cannot sync after writing "
+                    "to %s: %s");
       return -1;
     }
     return 0;
@@ -884,7 +1158,8 @@ static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg)
   if (munmap (header, headersz) < 0)
     {
       if (errmsg)
-       *errmsg = "arc_mmap_munmap(): Cannot unmap after writing to %s: %s\n";
+       *errmsg = N_("arc_mmap_munmap(): cannot unmap after writing "
+                    "to %s: %s");
       return -1;
     }
     return 0;
@@ -928,8 +1203,8 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz,
   if ((lseek (fd, 0, SEEK_SET)) < 0)
     {
       if (errmsg)
-       *errmsg = "arc_mmap_writeout(): Cannot seek while writing header to "
-         "%s: %s\n";
+       *errmsg = N_("arc_mmap_writeout(): cannot seek while writing header to "
+                    "%s: %s");
       return -1;
     }
 
@@ -938,7 +1213,7 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz,
       if ((len = write (fd, data, count)) < 0)
        {
          if (errmsg)
-           *errmsg = "arc_mmap_writeout(): Cannot write header to %s: %s\n";
+           *errmsg = N_("arc_mmap_writeout(): cannot write header to %s: %s");
          return len;
        }
       if (len == EINTR)
This page took 0.059045 seconds and 4 git commands to generate.