objcopy: bfd_alloc orelocation
[binutils-gdb.git] / libctf / ctf-open.c
index 456efa6546941a44ded9c0a4e4d88b1ef5d0c9b0..f0e203e0a16d7b6533a0db8cbc53f9d866cec2dd 100644 (file)
@@ -1,5 +1,5 @@
 /* Opening CTF files.
-   Copyright (C) 2019-2020 Free Software Foundation, Inc.
+   Copyright (C) 2019-2022 Free Software Foundation, Inc.
 
    This file is part of libctf.
 
 #include <string.h>
 #include <sys/types.h>
 #include <elf.h>
-#include <assert.h>
 #include "swap.h"
 #include <bfd.h>
 #include <zlib.h>
 
-#include "elf-bfd.h"
-
 static const ctf_dmodel_t _libctf_models[] = {
   {"ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4},
   {"LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8},
@@ -220,55 +217,95 @@ static const ctf_dictops_t ctf_dictops[] = {
   {get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
 };
 
-/* Initialize the symtab translation table by filling each entry with the
-  offset of the CTF type or function data corresponding to each STT_FUNC or
-  STT_OBJECT entry in the symbol table.  */
+/* Initialize the symtab translation table as appropriate for its indexing
+   state.  For unindexed symtypetabs, fill each entry with the offset of the CTF
+   type or function data corresponding to each STT_FUNC or STT_OBJECT entry in
+   the symbol table.  For indexed symtypetabs, do nothing: the needed
+   initialization for indexed lookups may be quite expensive, so it is done only
+   as needed, when lookups happen.  (In particular, the majority of indexed
+   symtypetabs come from the compiler, and all the linker does is iteration over
+   all entries, which doesn't need this initialization.)
+
+   The SP symbol table section may be NULL if there is no symtab.
+
+   If init_symtab works on one call, it cannot fail on future calls to the same
+   fp: ctf_symsect_endianness relies on this.  */
 
 static int
-init_symtab (ctf_dict_t *fp, const ctf_header_t *hp,
-            const ctf_sect_t *sp, const ctf_sect_t *strp)
+init_symtab (ctf_dict_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp)
 {
-  const unsigned char *symp = sp->cts_data;
+  const unsigned char *symp;
+  int skip_func_info = 0;
+  int i;
   uint32_t *xp = fp->ctf_sxlate;
-  uint32_t *xend = xp + fp->ctf_nsyms;
+  uint32_t *xend = PTR_ADD (xp, fp->ctf_nsyms);
 
   uint32_t objtoff = hp->cth_objtoff;
   uint32_t funcoff = hp->cth_funcoff;
 
-  uint32_t info, vlen;
-  Elf64_Sym sym, *gsp;
-  const char *name;
-
-  /* The CTF data object and function type sections are ordered to match
-     the relative order of the respective symbol types in the symtab.
-     If no type information is available for a symbol table entry, a
-     pad is inserted in the CTF section.  As a further optimization,
-     anonymous or undefined symbols are omitted from the CTF data.  */
-
-  for (; xp < xend; xp++, symp += sp->cts_entsize)
+  /* If the CTF_F_NEWFUNCINFO flag is not set, pretend the func info section
+     is empty: this compiler is too old to emit a function info section we
+     understand.  */
+
+  if (!(hp->cth_flags & CTF_F_NEWFUNCINFO))
+    skip_func_info = 1;
+
+  if (hp->cth_objtidxoff < hp->cth_funcidxoff)
+    fp->ctf_objtidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_objtidxoff);
+  if (hp->cth_funcidxoff < hp->cth_varoff && !skip_func_info)
+    fp->ctf_funcidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_funcidxoff);
+
+  /* Don't bother doing the rest if everything is indexed, or if we don't have a
+     symbol table: we will never use it.  */
+  if ((fp->ctf_objtidx_names && fp->ctf_funcidx_names) || !sp || !sp->cts_data)
+    return 0;
+
+  /* The CTF data object and function type sections are ordered to match the
+     relative order of the respective symbol types in the symtab, unless there
+     is an index section, in which case the order is arbitrary and the index
+     gives the mapping.  If no type information is available for a symbol table
+     entry, a pad is inserted in the CTF section.  As a further optimization,
+     anonymous or undefined symbols are omitted from the CTF data.  If an
+     index is available for function symbols but not object symbols, or vice
+     versa, we populate the xslate table for the unindexed symbols only.  */
+
+  for (i = 0, symp = sp->cts_data; xp < xend; xp++, symp += sp->cts_entsize,
+        i++)
     {
-      if (sp->cts_entsize == sizeof (Elf32_Sym))
-       gsp = ctf_sym_to_elf64 ((Elf32_Sym *) (uintptr_t) symp, &sym);
-      else
-       gsp = (Elf64_Sym *) (uintptr_t) symp;
+      ctf_link_sym_t sym;
 
-      if (gsp->st_name < strp->cts_size)
-       name = (const char *) strp->cts_data + gsp->st_name;
-      else
-       name = _CTF_NULLSTR;
+      switch (sp->cts_entsize)
+       {
+       case sizeof (Elf64_Sym):
+         {
+           const Elf64_Sym *symp64 = (Elf64_Sym *) (uintptr_t) symp;
+           ctf_elf64_to_link_sym (fp, &sym, symp64, i);
+         }
+         break;
+       case sizeof (Elf32_Sym):
+         {
+           const Elf32_Sym *symp32 = (Elf32_Sym *) (uintptr_t) symp;
+           ctf_elf32_to_link_sym (fp, &sym, symp32, i);
+         }
+         break;
+       default:
+         return ECTF_SYMTAB;
+       }
 
-      if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF
-         || strcmp (name, "_START_") == 0 || strcmp (name, "_END_") == 0)
+      /* This call may be led astray if our idea of the symtab's endianness is
+        wrong, but when this is fixed by a call to ctf_symsect_endianness,
+        init_symtab will be called again with the right endianness in
+        force.  */
+      if (ctf_symtab_skippable (&sym))
        {
          *xp = -1u;
          continue;
        }
 
-      switch (ELF64_ST_TYPE (gsp->st_info))
+      switch (sym.st_type)
        {
        case STT_OBJECT:
-         if (objtoff >= hp->cth_funcoff
-             || (gsp->st_shndx == SHN_EXTABS && gsp->st_value == 0))
+         if (fp->ctf_objtidx_names || objtoff >= hp->cth_funcoff)
            {
              *xp = -1u;
              break;
@@ -279,25 +316,15 @@ init_symtab (ctf_dict_t *fp, const ctf_header_t *hp,
          break;
 
        case STT_FUNC:
-         if (funcoff >= hp->cth_objtidxoff)
+         if (fp->ctf_funcidx_names || funcoff >= hp->cth_objtidxoff
+             || skip_func_info)
            {
              *xp = -1u;
              break;
            }
 
          *xp = funcoff;
-
-         info = *(uint32_t *) ((uintptr_t) fp->ctf_buf + funcoff);
-         vlen = LCTF_INFO_VLEN (fp, info);
-
-         /* If we encounter a zero pad at the end, just skip it.  Otherwise
-            skip over the function and its return type (+2) and the argument
-            list (vlen).
-          */
-         if (LCTF_INFO_KIND (fp, info) == CTF_K_UNKNOWN && vlen == 0)
-           funcoff += sizeof (uint32_t);       /* Skip pad.  */
-         else
-           funcoff += sizeof (uint32_t) * (vlen + 2);
+         funcoff += sizeof (uint32_t);
          break;
 
        default:
@@ -656,7 +683,7 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
 
   unsigned long pop[CTF_K_MAX + 1] = { 0 };
   const ctf_type_t *tp;
-  uint32_t id, dst;
+  uint32_t id;
   uint32_t *xp;
 
   /* We determine whether the dict is a child or a parent based on the value of
@@ -729,7 +756,8 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
     return ENOMEM;
 
   if ((fp->ctf_names.ctn_readonly
-       = ctf_hash_create (pop[CTF_K_INTEGER] +
+       = ctf_hash_create (pop[CTF_K_UNKNOWN] +
+                         pop[CTF_K_INTEGER] +
                          pop[CTF_K_FLOAT] +
                          pop[CTF_K_FUNCTION] +
                          pop[CTF_K_TYPEDEF] +
@@ -773,6 +801,7 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
 
       switch (kind)
        {
+       case CTF_K_UNKNOWN:
        case CTF_K_INTEGER:
        case CTF_K_FLOAT:
          /* Names are reused by bit-fields, which are differentiated by their
@@ -926,25 +955,6 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
   ctf_dprintf ("%u base type names hashed\n",
               ctf_hash_size (fp->ctf_names.ctn_readonly));
 
-  /* Make an additional pass through the pointer table to find pointers that
-     point to anonymous typedef nodes.  If we find one, modify the pointer table
-     so that the pointer is also known to point to the node that is referenced
-     by the anonymous typedef node.  */
-
-  for (id = 1; id <= fp->ctf_typemax; id++)
-    {
-      if ((dst = fp->ctf_ptrtab[id]) != 0)
-       {
-         tp = LCTF_INDEX_TO_TYPEPTR (fp, id);
-
-         if (LCTF_INFO_KIND (fp, tp->ctt_info) == CTF_K_TYPEDEF
-             && strcmp (ctf_strptr (fp, tp->ctt_name), "") == 0
-             && LCTF_TYPE_ISCHILD (fp, tp->ctt_type) == child
-             && LCTF_TYPE_TO_INDEX (fp, tp->ctt_type) <= fp->ctf_typemax)
-             fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, tp->ctt_type)] = dst;
-       }
-    }
-
   return 0;
 }
 
@@ -953,32 +963,10 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
    We flip everything, mindlessly, even 1-byte entities, so that future
    expansions do not require changes to this code.  */
 
-/* < C11? define away static assertions.  */
-
-#if !defined (__STDC_VERSION__) || __STDC_VERSION__ < 201112L
-#define _Static_assert(cond, err)
-#endif
-
-/* Swap the endianness of something.  */
-
-#define swap_thing(x)                                                  \
-  do {                                                                 \
-    _Static_assert (sizeof (x) == 1 || (sizeof (x) % 2 == 0            \
-                                       && sizeof (x) <= 8),            \
-                   "Invalid size, update endianness code");            \
-    switch (sizeof (x)) {                                              \
-    case 2: x = bswap_16 (x); break;                                   \
-    case 4: x = bswap_32 (x); break;                                   \
-    case 8: x = bswap_64 (x); break;                                   \
-    case 1: /* Nothing needs doing */                                  \
-      break;                                                           \
-    }                                                                  \
-  } while (0);
-
 /* Flip the endianness of the CTF header.  */
 
-static void
-flip_header (ctf_header_t *cth)
+void
+ctf_flip_header (ctf_header_t *cth)
 {
   swap_thing (cth->cth_preamble.ctp_magic);
   swap_thing (cth->cth_preamble.ctp_version);
@@ -1012,9 +1000,7 @@ flip_lbls (void *start, size_t len)
 }
 
 /* Flip the endianness of the data-object or function sections or their indexes,
-   all arrays of uint32_t.  (The function section has more internal structure,
-   but that structure is an array of uint32_t, so can be treated as one big
-   array for byte-swapping.)  */
+   all arrays of uint32_t.  */
 
 static void
 flip_objts (void *start, size_t len)
@@ -1045,26 +1031,48 @@ flip_vars (void *start, size_t len)
    ctf_stype followed by variable data.  */
 
 static int
-flip_types (ctf_dict_t *fp, void *start, size_t len)
+flip_types (ctf_dict_t *fp, void *start, size_t len, int to_foreign)
 {
   ctf_type_t *t = start;
 
   while ((uintptr_t) t < ((uintptr_t) start) + len)
     {
+      uint32_t kind;
+      size_t size;
+      uint32_t vlen;
+      size_t vbytes;
+
+      if (to_foreign)
+       {
+         kind = CTF_V2_INFO_KIND (t->ctt_info);
+         size = t->ctt_size;
+         vlen = CTF_V2_INFO_VLEN (t->ctt_info);
+         vbytes = get_vbytes_v2 (fp, kind, size, vlen);
+       }
+
       swap_thing (t->ctt_name);
       swap_thing (t->ctt_info);
       swap_thing (t->ctt_size);
 
-      uint32_t kind = CTF_V2_INFO_KIND (t->ctt_info);
-      size_t size = t->ctt_size;
-      uint32_t vlen = CTF_V2_INFO_VLEN (t->ctt_info);
-      size_t vbytes = get_vbytes_v2 (fp, kind, size, vlen);
+      if (!to_foreign)
+       {
+         kind = CTF_V2_INFO_KIND (t->ctt_info);
+         size = t->ctt_size;
+         vlen = CTF_V2_INFO_VLEN (t->ctt_info);
+         vbytes = get_vbytes_v2 (fp, kind, size, vlen);
+       }
 
       if (_libctf_unlikely_ (size == CTF_LSIZE_SENT))
        {
+         if (to_foreign)
+           size = CTF_TYPE_LSIZE (t);
+
          swap_thing (t->ctt_lsizehi);
          swap_thing (t->ctt_lsizelo);
-         size = CTF_TYPE_LSIZE (t);
+
+         if (!to_foreign)
+           size = CTF_TYPE_LSIZE (t);
+
          t = (ctf_type_t *) ((uintptr_t) t + sizeof (ctf_type_t));
        }
       else
@@ -1196,22 +1204,27 @@ flip_types (ctf_dict_t *fp, void *start, size_t len)
 }
 
 /* Flip the endianness of BUF, given the offsets in the (already endian-
-   converted) CTH.
+   converted) CTH.  If TO_FOREIGN is set, flip to foreign-endianness; if not,
+   flip away.
 
    All of this stuff happens before the header is fully initialized, so the
    LCTF_*() macros cannot be used yet.  Since we do not try to endian-convert v1
    data, this is no real loss.  */
 
-static int
-flip_ctf (ctf_dict_t *fp, ctf_header_t *cth, unsigned char *buf)
+int
+ctf_flip (ctf_dict_t *fp, ctf_header_t *cth, unsigned char *buf,
+         int to_foreign)
 {
+  ctf_dprintf("flipping endianness\n");
+
   flip_lbls (buf + cth->cth_lbloff, cth->cth_objtoff - cth->cth_lbloff);
   flip_objts (buf + cth->cth_objtoff, cth->cth_funcoff - cth->cth_objtoff);
   flip_objts (buf + cth->cth_funcoff, cth->cth_objtidxoff - cth->cth_funcoff);
   flip_objts (buf + cth->cth_objtidxoff, cth->cth_funcidxoff - cth->cth_objtidxoff);
   flip_objts (buf + cth->cth_funcidxoff, cth->cth_varoff - cth->cth_funcidxoff);
   flip_vars (buf + cth->cth_varoff, cth->cth_typeoff - cth->cth_varoff);
-  return flip_types (fp, buf + cth->cth_typeoff, cth->cth_stroff - cth->cth_typeoff);
+  return flip_types (fp, buf + cth->cth_typeoff,
+                    cth->cth_stroff - cth->cth_typeoff, to_foreign);
 }
 
 /* Set up the ctl hashes in a ctf_dict_t.  Called by both writable and
@@ -1379,8 +1392,9 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
         info.  We do not support dynamically upgrading such entries (none
         should exist in any case, since dwarf2ctf does not create them).  */
 
-      ctf_err_warn (NULL, 0, 0, _("ctf_bufopen: CTF version %d symsect not "
-                                 "supported"), pp->ctp_version);
+      ctf_err_warn (NULL, 0, ECTF_NOTSUP, _("ctf_bufopen: CTF version %d "
+                                           "symsect not supported"),
+                   pp->ctp_version);
       return (ctf_set_open_errno (errp, ECTF_NOTSUP));
     }
 
@@ -1388,7 +1402,12 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
     hdrsz = sizeof (ctf_header_v2_t);
 
   if (_libctf_unlikely_ (pp->ctp_flags > CTF_F_MAX))
-    return (ctf_set_open_errno (errp, ECTF_FLAGS));
+    {
+      ctf_err_warn (NULL, 0, ECTF_FLAGS, _("ctf_bufopen: invalid header "
+                                          "flags: %x"),
+                   (unsigned int) pp->ctp_flags);
+      return (ctf_set_open_errno (errp, ECTF_FLAGS));
+    }
 
   if (ctfsect->cts_size < hdrsz)
     return (ctf_set_open_errno (errp, ECTF_NOCTFBUF));
@@ -1412,7 +1431,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
     upgrade_header (hp);
 
   if (foreign_endian)
-    flip_header (hp);
+    ctf_flip_header (hp);
   fp->ctf_openflags = hp->cth_flags;
   fp->ctf_size = hp->cth_stroff + hp->cth_strlen;
 
@@ -1423,7 +1442,10 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       || hp->cth_funcoff > fp->ctf_size || hp->cth_objtidxoff > fp->ctf_size
       || hp->cth_funcidxoff > fp->ctf_size || hp->cth_typeoff > fp->ctf_size
       || hp->cth_stroff > fp->ctf_size)
-    return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("header offset exceeds CTF size"));
+      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    }
 
   if (hp->cth_lbloff > hp->cth_objtoff
       || hp->cth_objtoff > hp->cth_funcoff
@@ -1432,13 +1454,47 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       || hp->cth_objtidxoff > hp->cth_funcidxoff
       || hp->cth_funcidxoff > hp->cth_varoff
       || hp->cth_varoff > hp->cth_typeoff || hp->cth_typeoff > hp->cth_stroff)
-    return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("overlapping CTF sections"));
+      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    }
 
   if ((hp->cth_lbloff & 3) || (hp->cth_objtoff & 2)
       || (hp->cth_funcoff & 2) || (hp->cth_objtidxoff & 2)
       || (hp->cth_funcidxoff & 2) || (hp->cth_varoff & 3)
       || (hp->cth_typeoff & 3))
-    return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT,
+                   _("CTF sections not properly aligned"));
+      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    }
+
+  /* This invariant will be lifted in v4, but for now it is true.  */
+
+  if ((hp->cth_funcidxoff - hp->cth_objtidxoff != 0) &&
+      (hp->cth_funcidxoff - hp->cth_objtidxoff
+       != hp->cth_funcoff - hp->cth_objtoff))
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT,
+                   _("Object index section is neither empty nor the "
+                     "same length as the object section: %u versus %u "
+                     "bytes"), hp->cth_funcoff - hp->cth_objtoff,
+                   hp->cth_funcidxoff - hp->cth_objtidxoff);
+      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    }
+
+  if ((hp->cth_varoff - hp->cth_funcidxoff != 0) &&
+      (hp->cth_varoff - hp->cth_funcidxoff
+       != hp->cth_objtidxoff - hp->cth_funcoff) &&
+      (hp->cth_flags & CTF_F_NEWFUNCINFO))
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT,
+                   _("Function index section is neither empty nor the "
+                     "same length as the function section: %u versus %u "
+                     "bytes"), hp->cth_objtidxoff - hp->cth_funcoff,
+                   hp->cth_varoff - hp->cth_funcidxoff);
+      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+    }
 
   /* Once everything is determined to be valid, attempt to decompress the CTF
      data buffer if it is compressed, or copy it into new storage if it is not
@@ -1488,26 +1544,39 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
          goto bad;
        }
     }
-  else if (foreign_endian)
+  else
     {
-      if ((fp->ctf_base = malloc (fp->ctf_size)) == NULL)
+      if (_libctf_unlikely_ (ctfsect->cts_size < hdrsz + fp->ctf_size))
        {
-         err = ECTF_ZALLOC;
+         ctf_err_warn (NULL, 0, ECTF_CORRUPT,
+                       _("%lu byte long CTF dictionary overruns %lu byte long CTF section"),
+                       (unsigned long) ctfsect->cts_size,
+                       (unsigned long) (hdrsz + fp->ctf_size));
+         err = ECTF_CORRUPT;
          goto bad;
        }
-      fp->ctf_dynbase = fp->ctf_base;
-      memcpy (fp->ctf_base, ((unsigned char *) ctfsect->cts_data) + hdrsz,
-             fp->ctf_size);
-      fp->ctf_buf = fp->ctf_base;
-    }
-  else
-    {
-      /* We are just using the section passed in -- but its header may be an old
-        version.  Point ctf_buf past the old header, and never touch it
-        again.  */
-      fp->ctf_base = (unsigned char *) ctfsect->cts_data;
-      fp->ctf_dynbase = NULL;
-      fp->ctf_buf = fp->ctf_base + hdrsz;
+
+      if (foreign_endian)
+       {
+         if ((fp->ctf_base = malloc (fp->ctf_size)) == NULL)
+           {
+             err = ECTF_ZALLOC;
+             goto bad;
+           }
+         fp->ctf_dynbase = fp->ctf_base;
+         memcpy (fp->ctf_base, ((unsigned char *) ctfsect->cts_data) + hdrsz,
+                 fp->ctf_size);
+         fp->ctf_buf = fp->ctf_base;
+       }
+      else
+       {
+         /* We are just using the section passed in -- but its header may
+            be an old version.  Point ctf_buf past the old header, and
+            never touch it again.  */
+         fp->ctf_base = (unsigned char *) ctfsect->cts_data;
+         fp->ctf_dynbase = NULL;
+         fp->ctf_buf = fp->ctf_base + hdrsz;
+       }
     }
 
   /* Once we have uncompressed and validated the CTF data buffer, we can
@@ -1519,7 +1588,12 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
      ctf_set_base().  */
 
   ctf_set_version (fp, hp, hp->cth_version);
-  ctf_str_create_atoms (fp);
+  if (ctf_str_create_atoms (fp) < 0)
+    {
+      err = ENOMEM;
+      goto bad;
+    }
+
   fp->ctf_parmax = CTF_MAX_PTYPE;
   memcpy (&fp->ctf_data, ctfsect, sizeof (ctf_sect_t));
 
@@ -1563,9 +1637,9 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
   fp->ctf_syn_ext_strtab = syn_strtab;
 
   if (foreign_endian &&
-      (err = flip_ctf (fp, hp, fp->ctf_buf)) != 0)
+      (err = ctf_flip (fp, hp, fp->ctf_buf, 0)) != 0)
     {
-      /* We can be certain that flip_ctf() will have endian-flipped everything
+      /* We can be certain that ctf_flip() will have endian-flipped everything
         other than the types table when we return.  In particular the header
         is fine, so set it, to allow freeing to use the usual code path.  */
 
@@ -1586,10 +1660,18 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
   if ((err = init_types (fp, hp)) != 0)
     goto bad;
 
-  /* If we have a symbol table section, allocate and initialize
-     the symtab translation table, pointed to by ctf_sxlate.  This table may be
-     too large for the actual size of the object and function info sections: if
-     so, ctf_nsyms will be adjusted and the excess will never be used.  */
+  /* Allocate and initialize the symtab translation table, pointed to by
+     ctf_sxlate, and the corresponding index sections.  This table may be too
+     large for the actual size of the object and function info sections: if so,
+     ctf_nsyms will be adjusted and the excess will never be used.  It's
+     possible to do indexed symbol lookups even without a symbol table, so check
+     even in that case.  Initially, we assume the symtab is native-endian: if it
+     isn't, the caller will inform us later by calling ctf_symsect_endianness.  */
+#ifdef WORDS_BIGENDIAN
+  fp->ctf_symsect_little_endian = 0;
+#else
+  fp->ctf_symsect_little_endian = 1;
+#endif
 
   if (symsect != NULL)
     {
@@ -1601,11 +1683,11 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
          err = ENOMEM;
          goto bad;
        }
-
-      if ((err = init_symtab (fp, hp, symsect, strsect)) != 0)
-       goto bad;
     }
 
+  if ((err = init_symtab (fp, hp, symsect)) != 0)
+    goto bad;
+
   ctf_set_ctl_hashes (fp);
 
   if (symsect != NULL)
@@ -1649,6 +1731,7 @@ ctf_dict_close (ctf_dict_t *fp)
 {
   ctf_dtdef_t *dtd, *ntd;
   ctf_dvdef_t *dvd, *nvd;
+  ctf_in_flight_dynsym_t *did, *nid;
   ctf_err_warning_t *err, *nerr;
 
   if (fp == NULL)
@@ -1701,6 +1784,21 @@ ctf_dict_close (ctf_dict_t *fp)
       ctf_dvd_delete (fp, dvd);
     }
   ctf_dynhash_destroy (fp->ctf_dvhash);
+
+  ctf_dynhash_destroy (fp->ctf_symhash);
+  free (fp->ctf_funcidx_sxlate);
+  free (fp->ctf_objtidx_sxlate);
+  ctf_dynhash_destroy (fp->ctf_objthash);
+  ctf_dynhash_destroy (fp->ctf_funchash);
+  free (fp->ctf_dynsymidx);
+  ctf_dynhash_destroy (fp->ctf_dynsyms);
+  for (did = ctf_list_next (&fp->ctf_in_flight_dynsyms); did != NULL; did = nid)
+    {
+      nid = ctf_list_next (did);
+      ctf_list_delete (&fp->ctf_in_flight_dynsyms, did);
+      free (did);
+    }
+
   ctf_str_free_atoms (fp);
   free (fp->ctf_tmp_typeslice);
 
@@ -1738,6 +1836,7 @@ ctf_dict_close (ctf_dict_t *fp)
   free (fp->ctf_sxlate);
   free (fp->ctf_txlate);
   free (fp->ctf_ptrtab);
+  free (fp->ctf_pptrtab);
 
   free (fp->ctf_header);
   free (fp);
@@ -1776,6 +1875,34 @@ ctf_getdatasect (const ctf_dict_t *fp)
   return fp->ctf_data;
 }
 
+ctf_sect_t
+ctf_getsymsect (const ctf_dict_t *fp)
+{
+  return fp->ctf_symtab;
+}
+
+ctf_sect_t
+ctf_getstrsect (const ctf_dict_t *fp)
+{
+  return fp->ctf_strtab;
+}
+
+/* Set the endianness of the symbol table attached to FP.  */
+void
+ctf_symsect_endianness (ctf_dict_t *fp, int little_endian)
+{
+  int old_endianness = fp->ctf_symsect_little_endian;
+
+  fp->ctf_symsect_little_endian = !!little_endian;
+
+  /* If we already have a symtab translation table, we need to repopulate it if
+     our idea of the endianness has changed.  */
+
+  if (old_endianness != fp->ctf_symsect_little_endian
+      && fp->ctf_sxlate != NULL && fp->ctf_symtab.cts_data != NULL)
+    assert (init_symtab (fp, fp->ctf_header, &fp->ctf_symtab) == 0);
+}
+
 /* Return the CTF handle for the parent CTF dict, if one exists.  Otherwise
    return NULL to indicate this dict has no imported parent.  */
 ctf_dict_t *
@@ -1835,7 +1962,8 @@ ctf_cuname_set (ctf_dict_t *fp, const char *name)
 
 /* Import the types from the specified parent dict by storing a pointer to it in
    ctf_parent and incrementing its reference count.  Only one parent is allowed:
-   if a parent already exists, it is replaced by the new parent.  */
+   if a parent already exists, it is replaced by the new parent.  The pptrtab
+   is wiped, and will be refreshed by the next ctf_lookup_by_name call.  */
 int
 ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
 {
@@ -1849,6 +1977,11 @@ ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
     ctf_dict_close (fp->ctf_parent);
   fp->ctf_parent = NULL;
 
+  free (fp->ctf_pptrtab);
+  fp->ctf_pptrtab = NULL;
+  fp->ctf_pptrtab_len = 0;
+  fp->ctf_pptrtab_typemax = 0;
+
   if (pfp != NULL)
     {
       int err;
@@ -1883,6 +2016,10 @@ ctf_import_unref (ctf_dict_t *fp, ctf_dict_t *pfp)
     ctf_dict_close (fp->ctf_parent);
   fp->ctf_parent = NULL;
 
+  free (fp->ctf_pptrtab);
+  fp->ctf_pptrtab = NULL;
+  fp->ctf_pptrtab_len = 0;
+  fp->ctf_pptrtab_typemax = 0;
   if (pfp != NULL)
     {
       int err;