AArch64: Add gdbserver MTE support
[deliverable/binutils-gdb.git] / libctf / ctf-archive.c
index 25c30f64b50eba7963a1cc1a362c94e9bba24cd3..e0ceb80fa11fa1899c5c3e091192e3cf8f114a30 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.
 
@@ -36,13 +36,19 @@ 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 *errp);
+                                           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
+   and ctfi_symnamedicts.  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_DICTS are referenced by name: the names are passed in
@@ -373,10 +379,21 @@ 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
@@ -512,6 +529,9 @@ ctf_arc_close (ctf_archive_t *arc)
     }
   else
     ctf_dict_close (arc->ctfi_dict);
+  free (arc->ctfi_symdicts);
+  free (arc->ctfi_symnamedicts);
+  ctf_dynhash_destroy (arc->ctfi_dicts);
   if (arc->ctfi_free_symsect)
     free ((void *) arc->ctfi_symsect.cts_data);
   if (arc->ctfi_free_strsect)
@@ -528,7 +548,8 @@ 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 *errp)
+                       const char *name, int little_endian,
+                       int *errp)
 {
   struct ctf_archive_modent *modent;
   const char *search_nametbl;
@@ -556,7 +577,8 @@ ctf_dict_open_internal (const struct ctf_archive *arc,
     }
 
   return ctf_dict_open_by_offset (arc, symsect, strsect,
-                                 le64toh (modent->ctf_offset), errp);
+                                 le64toh (modent->ctf_offset),
+                                 little_endian, errp);
 }
 
 /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
@@ -576,9 +598,13 @@ ctf_dict_open_sections (const ctf_archive_t *arc,
     {
       ctf_dict_t *ret;
       ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
-                                   name, errp);
+                                   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;
     }
 
@@ -613,13 +639,79 @@ ctf_dict_open (const ctf_archive_t *arc, const char *name, int *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.  If this is the first cached dict, designate it the
+   crossdict_cache.  */
+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++;
+
+  if (arc->ctfi_crossdict_cache == NULL)
+    arc->ctfi_crossdict_cache = fp;
+
+  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_symnamedicts);
+  ctf_dynhash_destroy (wrapper->ctfi_dicts);
+  wrapper->ctfi_symdicts = NULL;
+  wrapper->ctfi_symnamedicts = NULL;
+  wrapper->ctfi_dicts = NULL;
+  wrapper->ctfi_crossdict_cache = NULL;
+}
+
 /* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
    none, setting 'err' if non-NULL.  */
 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 *errp)
+                        int little_endian, int *errp)
 {
   ctf_sect_t ctfsect;
   ctf_dict_t *fp;
@@ -636,7 +728,11 @@ ctf_dict_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;
 }
 
@@ -658,6 +754,25 @@ ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
   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)
@@ -668,6 +783,222 @@ ctf_archive_count (const ctf_archive_t *wrapper)
   return wrapper->ctfi_archive->ctfa_ndicts;
 }
 
+/* Look up a symbol in an archive by name or index (if the name is set, a lookup
+   by name is done).  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 is 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).  */
+
+static ctf_dict_t *
+ctf_arc_lookup_sym_or_name (ctf_archive_t *wrapper, unsigned long symidx,
+                           const char *symname, ctf_id_t *typep, int *errp)
+{
+  ctf_dict_t *fp;
+  void *fpkey;
+  ctf_id_t type;
+
+  /* The usual non-archive-transparent-wrapper special case.  */
+  if (!wrapper->ctfi_is_archive)
+    {
+      if (!symname)
+       {
+         if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
+           {
+             if (errp)
+               *errp = ctf_errno (wrapper->ctfi_dict);
+             return NULL;
+           }
+       }
+      else
+       {
+         if ((type = ctf_lookup_by_symbol_name (wrapper->ctfi_dict,
+                                                symname)) == 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 symbol indexes, if not already done.  We
+     cache the originating dictionary of all symbols.  The dict links are weak,
+     to the dictionaries cached in ctfi_dicts: their refcnts are *not* bumped.
+     We also cache similar mappings for symbol names: these are ordinary
+     dynhashes, with weak links to dicts.  */
+
+  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;
+       }
+    }
+  if (!wrapper->ctfi_symnamedicts)
+    {
+      if ((wrapper->ctfi_symnamedicts = ctf_dynhash_create (ctf_hash_string,
+                                                           ctf_hash_eq_string,
+                                                           free, NULL)) == NULL)
+       {
+         if (errp)
+           *errp = ENOMEM;
+         return NULL;
+       }
+    }
+
+  /* Perhaps the dict in which we found a previous lookup is cached.  If it's
+     supposed to be cached but we don't find it, pretend it was always not
+     found: this should never happen, but shouldn't be allowed to cause trouble
+     if it does.  */
+
+  if ((symname && ctf_dynhash_lookup_kv (wrapper->ctfi_symnamedicts,
+                                        symname, NULL, &fpkey))
+      || (!symname && wrapper->ctfi_symdicts[symidx] != NULL))
+    {
+      if (symname)
+       fp = (ctf_dict_t *) fpkey;
+      else
+       fp = wrapper->ctfi_symdicts[symidx];
+
+      if (fp == &enosym)
+       goto no_sym;
+
+      if (symname)
+       {
+         if ((type = ctf_lookup_by_symbol_name (fp, symname)) == CTF_ERR)
+           goto cache_no_sym;
+       }
+      else
+       {
+         if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR)
+           goto cache_no_sym;
+       }
+
+      if (typep)
+       *typep = type;
+      fp->ctf_refcnt++;
+      return fp;
+    }
+
+  /* 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 (!symname)
+       {
+         if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
+           wrapper->ctfi_symdicts[symidx] = fp;
+       }
+      else
+       {
+         if ((type = ctf_lookup_by_symbol_name (fp, symname)) != CTF_ERR)
+           {
+             char *tmp;
+             /* No error checking, as above.  */
+             if ((tmp = strdup (symname)) != NULL)
+               ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, fp);
+           }
+       }
+
+      if (type != CTF_ERR)
+       {
+         if (typep)
+           *typep = type;
+         ctf_next_destroy (i);
+         return fp;
+       }
+      if (ctf_errno (fp) != ECTF_NOTYPEDAT)
+       {
+         if (errp)
+           *errp = ctf_errno (fp);
+         ctf_next_destroy (i);
+         return NULL;                          /* errno is set for us.  */
+       }
+      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;
+
+ cache_no_sym:
+  if (!symname)
+    wrapper->ctfi_symdicts[symidx] = &enosym;
+  else
+    {
+      char *tmp;
+
+      /* No error checking: if caching fails, there is only a slight performance
+        impact.  */
+      if ((tmp = strdup (symname)) != NULL)
+       if (ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, &enosym) < 0)
+         free (tmp);
+    }
+
+ no_sym:
+  if (errp)
+    *errp = ECTF_NOTYPEDAT;
+  if (typep)
+    *typep = CTF_ERR;
+  return NULL;
+}
+
+/* The public API for looking up a symbol by index.  */
+ctf_dict_t *
+ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
+                      ctf_id_t *typep, int *errp)
+{
+  return ctf_arc_lookup_sym_or_name (wrapper, symidx, NULL, typep, errp);
+}
+
+/* The public API for looking up a symbol by name. */
+
+ctf_dict_t *
+ctf_arc_lookup_symbol_name (ctf_archive_t *wrapper, const char *symname,
+                           ctf_id_t *typep, int *errp)
+{
+  return ctf_arc_lookup_sym_or_name (wrapper, 0, symname, typep, errp);
+}
+
 /* Raw iteration over all CTF files in an archive.  We pass the raw data for all
    CTF files in turn to the specified callback function.  */
 static int
@@ -712,72 +1043,37 @@ ctf_archive_raw_iter (const ctf_archive_t *arc,
   return -EINVAL;                       /* Not supported. */
 }
 
-/* Iterate over all CTF files in an archive.  We pass all CTF files in turn to
-   the specified callback function.  */
-static int
-ctf_archive_iter_internal (const ctf_archive_t *wrapper,
-                          const struct ctf_archive *arc,
-                          const ctf_sect_t *symsect,
-                          const ctf_sect_t *strsect,
-                          ctf_archive_member_f *func, void *data)
+/* Iterate over all CTF files in an archive: public entry point.  We pass all
+   CTF files in turn to the specified callback function.  */
+int
+ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
+                 void *data)
 {
-  int rc;
-  size_t i;
-  ctf_dict_t *f;
-  struct ctf_archive_modent *modent;
-  const char *nametbl;
-
-  modent = (ctf_archive_modent_t *) ((char *) arc
-                                    + sizeof (struct ctf_archive));
-  nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
+  ctf_next_t *i = NULL;
+  ctf_dict_t *fp;
+  const char *name;
+  int err;
 
-  for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
+  while ((fp = ctf_archive_next (arc, &i, &name, 0, &err)) != NULL)
     {
-      const char *name;
+      int rc;
 
-      name = &nametbl[le64toh (modent[i].name_offset)];
-      if ((f = ctf_dict_open_internal (arc, symsect, strsect,
-                                   name, &rc)) == NULL)
-       return rc;
-
-      f->ctf_archive = (ctf_archive_t *) wrapper;
-      if ((rc = func (f, name, data)) != 0)
+      if ((rc = func (fp, name, data)) != 0)
        {
-         ctf_dict_close (f);
+         ctf_dict_close (fp);
+         ctf_next_destroy (i);
          return rc;
        }
-
-      ctf_dict_close (f);
+      ctf_dict_close (fp);
     }
   return 0;
 }
 
-/* Iterate over all CTF files in an archive: public entry point.  We pass all
-   CTF files in turn to the specified callback function.  */
-int
-ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
-                 void *data)
-{
-  const ctf_sect_t *symsect = &arc->ctfi_symsect;
-  const ctf_sect_t *strsect = &arc->ctfi_strsect;
-
-  if (symsect->cts_name == NULL)
-    symsect = NULL;
-  if (strsect->cts_name == NULL)
-    strsect = NULL;
-
-  if (arc->ctfi_is_archive)
-    return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect,
-                                     func, 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_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.
+   responsibility to close it.  Parent dicts may be skipped.
+
+   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.  */
@@ -831,6 +1127,8 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na
       if (!skip_parent)
        {
          wrapper->ctfi_dict->ctf_refcnt++;
+         if (name)
+           *name = _CTF_SECTION;
          return wrapper->ctfi_dict;
        }
     }
@@ -841,9 +1139,6 @@ 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_ndicts)))
@@ -855,27 +1150,19 @@ 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));
 
       name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)];
       i->ctn_n++;
-    } while (skip_parent && strcmp (name_, _CTF_SECTION) == 0);
+    }
+  while (skip_parent && strcmp (name_, _CTF_SECTION) == 0);
 
   if (name)
     *name = name_;
 
-  f = ctf_dict_open_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;
 }
 
This page took 0.028369 seconds and 4 git commands to generate.