]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: CTFv4: core opening (other than the type table)
authorNick Alcock <nick.alcock@oracle.com>
Thu, 24 Apr 2025 13:44:12 +0000 (14:44 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 25 Apr 2025 17:07:42 +0000 (18:07 +0100)
This commit modifies the core opening code to handle opening CTFv4 and BTF.
Much of the back-compatibility side is left for later and is currently
untested, as is the type table side of things.

We keep the v3 header (if any) stashed away in ctf_dict_t.ctf_v3_header, for
the sake of the CTF dumper; we "upgrade" the BTF header to CTF (so that the
rest of the code can ignore the distinction, and so that you can do CTFish
things like adding symtypetab entries even to things opened as BTF), but
keep note of the fact that it was opened as BTF in ctf_dict_t.ctf_opened_btf,
so that things like ctf_import can allow for the absence of the various
parent-length fields.

A couple of ctf_dict_t fields are renamed for consistency with the headers'
names for them (ctf_parname becomes ctf_parent_name; ctf_dynparname becomes
ctf_dyn_parent_name; ctf_cuname becomes ctf_cu_name).  Not all users are yet
adjusted.

libctf/ctf-impl.h
libctf/ctf-open.c

index df84911e99558f5ad1d1c3c5dd69b1200802dda2..a7ce6c80df0e3ef90fcbec42f23552144e90434e 100644 (file)
@@ -365,8 +365,11 @@ typedef struct ctf_dedup
 struct ctf_dict
 {
   const ctf_dictops_t *ctf_dictops; /* Version-specific dict operations.  */
-  struct ctf_header *ctf_header;    /* The header from this CTF dict.  */
+  ctf_header_t *ctf_header;        /* The header from this CTF dict.  */
+  ctf_header_v3_t *ctf_v3_header;   /* The header from an upgraded CTF dict.  */
   unsigned char ctf_openflags;     /* Flags the dict had when opened.  */
+  int ctf_opened_btf;              /* Whether this dict was pure BTF when
+                                      opened.  */
   ctf_sect_t ctf_data;             /* CTF data from object file.  */
   ctf_sect_t ctf_ext_symtab;       /* Symbol table from object file.  */
   ctf_sect_t ctf_ext_strtab;       /* String table from object file.  */
@@ -393,7 +396,8 @@ struct ctf_dict
   size_t ctf_str_prov_len;       /* Length of all unwritten provisional strings.  */
   unsigned char *ctf_base;       /* CTF file pointer.  */
   unsigned char *ctf_dynbase;    /* Freeable CTF file pointer. */
-  unsigned char *ctf_buf;        /* Uncompressed CTF data buffer.  */
+  unsigned char *ctf_buf;        /* Uncompressed CTF data buffer, including
+                                    CTFv4 header portion.  */
   size_t ctf_size;               /* Size of CTF header + uncompressed data.  */
   unsigned char *ctf_serializing_buf; /* CTF buffer in mid-serialization.  */
   size_t ctf_serializing_buf_size; /* Length of that buffer.  */
@@ -434,13 +438,12 @@ struct ctf_dict
                                     Counts down.  Parent only.  */
   uint32_t ctf_nprovtypes;       /* Number of provisional types (convenience).  */
   const ctf_dmodel_t *ctf_dmodel; /* Data model pointer (see above).  */
-  const char *ctf_cuname;        /* Compilation unit name (if any).  */
-  char *ctf_dyncuname;           /* Dynamically allocated name of CU.  */
+  const char *ctf_cu_name;       /* Compilation unit name (if any).  */
+  char *ctf_dyn_cu_name;         /* Dynamically allocated name of CU.  */
   struct ctf_dict *ctf_parent;   /* Parent CTF dict (if any).  */
   int ctf_parent_unreffed;       /* Parent set by ctf_import_unref?  */
-  const char *ctf_parlabel;      /* Label in parent dict (if any).  */
-  const char *ctf_parname;       /* Basename of parent (if any).  */
-  char *ctf_dynparname;                  /* Dynamically allocated name of parent.  */
+  const char *ctf_parent_name;   /* Basename of parent (if any).  */
+  char *ctf_dyn_parent_name;     /* Dynamically allocated name of parent.  */
   uint32_t ctf_refcnt;           /* Reference count (for parent links).  */
   uint32_t ctf_flags;            /* Libctf flags (see below).  */
   uint32_t ctf_max_children;     /* Max number of child dicts.  */
index a4b1827955567cd10cc9dbc285784e1e1e68e561..8ba65b9bae9ff1a599cce58a37845d263cfb6fd5 100644 (file)
@@ -422,20 +422,20 @@ init_symtab (ctf_dict_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp)
   uint32_t *xp = fp->ctf_sxlate;
   uint32_t *xend = PTR_ADD (xp, fp->ctf_nsyms);
 
-  uint32_t objtoff = hp->cth_objtoff;
-  uint32_t funcoff = hp->cth_funcoff;
+  uint32_t objtoff = hp->cth_objt_off;
+  uint32_t funcoff = hp->cth_func_off;
 
-  /* 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 this is a v3 dict, and 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))
+  if (fp->ctf_v3_header && (fp->ctf_v3_header->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);
+  if (hp->cth_objtidx_len > 0)
+    fp->ctf_objtidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_objtidx_off);
+  if (hp->cth_funcidx_len > 0 && !skip_func_info)
+    fp->ctf_funcidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_funcidx_off);
 
   /* Don't bother doing the rest if everything is indexed, or if we don't have a
      symbol table: we will never use it.  */
@@ -487,7 +487,7 @@ init_symtab (ctf_dict_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp)
       switch (sym.st_type)
        {
        case STT_OBJECT:
-         if (fp->ctf_objtidx_names || objtoff >= hp->cth_funcoff)
+         if (fp->ctf_objtidx_names || (objtoff - hp->cth_objt_off) > hp->cth_objt_len)
            {
              *xp = -1u;
              break;
@@ -498,7 +498,7 @@ init_symtab (ctf_dict_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp)
          break;
 
        case STT_FUNC:
-         if (fp->ctf_funcidx_names || funcoff >= hp->cth_objtidxoff
+         if (fp->ctf_funcidx_names || (funcoff - hp->cth_func_off) > hp->cth_func_len
              || skip_func_info)
            {
              *xp = -1u;
@@ -531,13 +531,10 @@ ctf_set_base (ctf_dict_t *fp, const ctf_header_t *hp, unsigned char *base)
 {
   fp->ctf_buf = base + (fp->ctf_buf - fp->ctf_base);
   fp->ctf_base = base;
-  fp->ctf_vars = (ctf_varent_t *) ((const char *) fp->ctf_buf +
-                                  hp->cth_varoff);
-  fp->ctf_nvars = (hp->cth_typeoff - hp->cth_varoff) / sizeof (ctf_varent_t);
 
   fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf
-    + hp->cth_stroff;
-  fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen;
+    + hp->btf.bth_str_off;
+  fp->ctf_str[CTF_STRTAB_0].cts_len = hp->btf.bth_str_len;
 
   /* If we have a parent dict name and label, store the relocated string
      pointers in the CTF dict for easy access later. */
@@ -551,20 +548,15 @@ ctf_set_base (ctf_dict_t *fp, const ctf_header_t *hp, unsigned char *base)
      the parent dict (and determining that this dict is a child) in the first
      place.  */
 
-  if (hp->cth_parlabel != 0)
-    fp->ctf_parlabel = ctf_strptr (fp, hp->cth_parlabel);
-  if (hp->cth_parname != 0)
-    fp->ctf_parname = ctf_strptr (fp, hp->cth_parname);
-  if (hp->cth_cuname != 0)
-    fp->ctf_cuname = ctf_strptr (fp, hp->cth_cuname);
-
-  if (fp->ctf_cuname)
-    ctf_dprintf ("ctf_set_base: CU name %s\n", fp->ctf_cuname);
-  if (fp->ctf_parname)
-    ctf_dprintf ("ctf_set_base: parent name %s (label %s)\n",
-              fp->ctf_parname,
-              fp->ctf_parlabel ? fp->ctf_parlabel : "<NULL>");
+  if (hp->cth_parent_name != 0)
+    fp->ctf_parent_name = ctf_strptr (fp, hp->cth_parent_name);
+  if (hp->cth_cu_name != 0)
+    fp->ctf_cu_name = ctf_strptr (fp, hp->cth_cu_name);
 
+  if (fp->ctf_cu_name)
+    ctf_dprintf ("ctf_set_base: CU name %s\n", fp->ctf_cu_name);
+  if (fp->ctf_parent_name)
+    ctf_dprintf ("ctf_set_base: parent name %s\n", fp->ctf_parent_name);
 }
 
 static int
@@ -583,7 +575,7 @@ init_static_types_names (ctf_dict_t *fp, ctf_header_t *cth);
    function that does all the actual work.  */
 
 static int
-init_static_types (ctf_dict_t *fp, ctf_header_t *cth)
+init_static_types (ctf_dict_t *fp, ctf_header_t *cth, int is_btf)
 {
   const ctf_type_t *tbuf;
   const ctf_type_t *tend;
@@ -1433,24 +1425,48 @@ ctf_dict_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size,
   return ctf_bufopen (ctfsectp, symsectp, strsectp, errp);
 }
 
-/* Decode the specified CTF buffer and optional symbol table, and create a new
-   CTF dict representing the symbolic debugging information.  This code can
-   be used directly by the debugger, or it can be used as the engine for
-   ctf_fdopen() or ctf_open(), below.  */
+/* Decode the specified CTF or BTF buffer and optional symbol table, and create
+   a new CTF dict representing the type information.  This code can be used
+   directly by users, or it can be used as the engine for ctf_fdopen() or
+   ctf_open(), below.  */
 
 ctf_dict_t *
 ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
             const ctf_sect_t *strsect, int *errp)
 {
-  const ctf_preamble_t *pp;
-  size_t hdrsz;
+  const ctf_preamble_v3_t *pp;
+  const ctf_btf_preamble_t *bp;
+  size_t hdrsz = 0;
   ctf_header_t *hp;
+  ctf_header_v3_t *header_v3 = NULL;
   ctf_dict_t *fp;
+  void *fliphp;
+  size_t ctf_adjustment = 0;
+
+  /* These match the CTF_VERSION definitions up to IS_BTF.  */
+  enum
+    {
+      IS_UNKNOWN = 0,
+      IS_CTFv1,
+      IS_CTFv1_UPGRADED_3,
+      IS_CTFv2,
+      IS_CTFv3,
+      IS_BTF,
+      IS_CTF
+    } format = IS_UNKNOWN;
+  int version = 0;
+
+  const char *desc;
   int foreign_endian = 0;
   int err;
 
   libctf_init_debug();
 
+  ctf_dprintf ("ctf_bufopen %zi+%zi+%zi bytes: validating\n",
+              ctfsect ? ctfsect->cts_size : 0,
+              symsect ? symsect->cts_size : 0,
+              strsect ? strsect->cts_size : 0);
+
   if ((ctfsect == NULL) || ((symsect != NULL) && (strsect == NULL)))
     return (ctf_set_open_errno (errp, EINVAL));
 
@@ -1465,183 +1481,365 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
     return (ctf_set_open_errno (errp, ECTF_STRBAD));
 
   if (ctfsect->cts_data == NULL
-      || ctfsect->cts_size < sizeof (ctf_preamble_t))
+      || ctfsect->cts_size < sizeof (ctf_btf_preamble_t))
     return (ctf_set_open_errno (errp, ECTF_NOCTFBUF));
 
-  pp = (const ctf_preamble_t *) ctfsect->cts_data;
+  /* Validate each part of the CTF header.
 
-  ctf_dprintf ("ctf_bufopen: magic=0x%x version=%u\n",
-              pp->ctp_magic, pp->ctp_version);
+     This is more complex than it used to be, because CTFv3 and below, BTF,
+     and CTFv4 have distinct preambles with distinct layouts.
 
-  /* Validate each part of the CTF header.
+     First, we validate the preamble.  At that point, we know the endianness and
+     specific header version, and can possibly swap and validate the
+     version-specific parts including section offsets and alignments.  None of
+     these portions are compressed, thankfully: compression starts after the
+     headers.  */
+
+  /* First, deal with the unprefixed cases: BTF, and CTFv1--v3: swap them into
+     native endianness if need be.  Verify the prefixed endiannesses first.  */
+
+  bp = (const ctf_btf_preamble_t *) ctfsect->cts_data;
+  pp = (const ctf_preamble_v3_t *) ctfsect->cts_data; /* CTFv3 or below.  */
 
-     First, we validate the preamble (common to all versions).  At that point,
-     we know the endianness and specific header version, and can validate the
-     version-specific parts including section offsets and alignments.  */
+  if (_libctf_unlikely_ (bp->btf_magic != CTF_BTF_MAGIC))
+    {
+      if (bp->btf_magic == bswap_16 (CTF_BTF_MAGIC))
+       {
+         format = IS_BTF;
+         foreign_endian = 1;
+         hdrsz = sizeof (struct ctf_btf_header);
+         version = bp->btf_version;
+       }
+    }
+    else
+      {
+       format = IS_BTF;
+       hdrsz = sizeof (struct ctf_btf_header);
+       version = bp->btf_version;
+      }
 
-  if (_libctf_unlikely_ (pp->ctp_magic != CTF_MAGIC))
+  if (format == IS_UNKNOWN)
     {
-      if (pp->ctp_magic == bswap_16 (CTF_MAGIC))
-       foreign_endian = 1;
+      if (pp->ctp_magic == CTF_MAGIC)
+       format = pp->ctp_version;
+      else if (format == IS_UNKNOWN && pp->ctp_magic == bswap_16 (CTF_MAGIC))
+       {
+         format = pp->ctp_version;
+         foreign_endian = 1;
+       }
       else
        return (ctf_set_open_errno (errp, ECTF_NOCTFBUF));
     }
 
-  if (_libctf_unlikely_ ((pp->ctp_version < CTF_VERSION_1)
-                        || (pp->ctp_version > CTF_VERSION_4)))
-    return (ctf_set_open_errno (errp, ECTF_CTFVERS));
+  if (format != IS_UNKNOWN && format < IS_BTF)
+    {
+      switch (format)
+       {
+       case IS_CTFv1:
+       case IS_CTFv2:
+         hdrsz = sizeof (ctf_header_v2_t);
+         break;
+       case IS_CTFv1_UPGRADED_3:
+       case IS_CTFv3:
+         hdrsz = sizeof (ctf_header_v3_t);
+         break;
+       default:
+         /* Cannot happen: placate the compiler.  */
+         hdrsz = sizeof (ctf_header_t);
+       }
+      version = pp->ctp_version;
+    }
+
+  /* Check for modern CTF.  If found, set the adjustment value that lets us
+     compensate for BTF offsets being relative to the end of the BTF header,
+     while everything else is relative to the end of the CTF header.  */
 
-  if ((symsect != NULL) && (pp->ctp_version < CTF_VERSION_2))
+  if (format == IS_BTF && ctfsect->cts_size >= sizeof (ctf_header_t))
     {
-      /* The symtab can contain function entries which contain embedded ctf
-        info.  We do not support dynamically upgrading such entries (none
-        should exist in any case, since dwarf2ctf does not create them).  */
+      hp = (ctf_header_t *) ctfsect->cts_data;
 
-      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));
+      if (CTH_MAGIC (hp) == CTFv4_MAGIC)
+       {
+         format = IS_CTF;
+         version = CTH_VERSION (hp);
+         hdrsz = sizeof (ctf_header_t);
+         ctf_adjustment = sizeof (ctf_header_t) - sizeof (ctf_btf_header_t);
+       }
+      else if (bswap_64 (hp->cth_preamble.ctp_magic_version) >> 16 == CTFv4_MAGIC)
+       {
+         format = IS_CTF;
+         foreign_endian = 1;
+         version = bswap_64 (CTH_VERSION (hp));
+         hdrsz = sizeof (ctf_header_t);
+         ctf_adjustment = sizeof (ctf_header_t) - sizeof (ctf_btf_header_t);
+       }
+      /* Neither hit: confirmed BTF, not CTFv4.  */
     }
 
-  switch (pp->ctp_version)
+  if (ctfsect->cts_size < hdrsz)
     {
-    case CTF_VERSION_1:
-    case CTF_VERSION_2:
-      hdrsz = sizeof (ctf_header_v2_t);
-      break;
-    case CTF_VERSION_1_UPGRADED_3:
-    case CTF_VERSION_3:
-      hdrsz = sizeof (ctf_header_v3_t);
-      break;
-    case CTF_VERSION_4:
-      hdrsz = sizeof (ctf_header_t);
-      break;
-    default:
-      ctf_err_warn (NULL, ECTF_INTERNAL, 0, "ctf_bufopen: CTF version %d "
-                   "not handled in header-length switch", pp->ctp_version);
-      return (ctf_set_open_errno (errp, ECTF_INTERNAL));
+      ctf_err_warn (NULL, 0, ECTF_NOCTFBUF,
+                   _("ctf_bufopen: size %zi too small for expected header size %zi"),
+                   ctfsect->cts_size, hdrsz);
+      return (ctf_set_open_errno (errp, ECTF_NOCTFBUF));
     }
 
-  if (_libctf_unlikely_ (pp->ctp_flags > CTF_F_MAX))
+  switch (format)
     {
-      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));
+    case IS_CTFv1: desc = "CTFv1"; break;
+    case IS_CTFv1_UPGRADED_3: desc = "CTFv1 upgraded to v3"; break;
+    case IS_CTFv2: desc = "CTFv2"; break;
+    case IS_CTFv3: desc = "CTFv3"; break;
+    case IS_BTF: desc = "BTF"; break;
+    case IS_CTF: desc = "CTFv4"; break;
+    default: desc = "unknown";
     }
 
-  /* UPTODO: v4 flags: none valid, new header field likely needed.  */
+  ctf_dprintf ("ctf_bufopen: %s, swap=%i\n", desc, foreign_endian);
 
-  if (ctfsect->cts_size < hdrsz)
-    return (ctf_set_open_errno (errp, ECTF_NOCTFBUF));
+  /* Endian-flip and upgrade the header, if necessary.  Preserve it after
+     flipping (for possible later dumping, etc).  */
 
-  if ((fp = malloc (sizeof (ctf_dict_t))) == NULL)
+  if ((fliphp = malloc (hdrsz)) == NULL)
     return (ctf_set_open_errno (errp, ENOMEM));
 
-  memset (fp, 0, sizeof (ctf_dict_t));
+  memcpy (fliphp, ctfsect->cts_data, hdrsz);
 
-  if ((fp->ctf_header = malloc (sizeof (struct ctf_header))) == NULL)
+  if (foreign_endian)
     {
-      free (fp);
+      if (ctf_flip_header (fliphp, format >= IS_BTF, version) < 0)
+       {
+         free (fliphp);
+         return (ctf_set_open_errno (errp, ECTF_INTERNAL));
+       }
+    }
+
+  if ((hp = malloc (sizeof (ctf_header_t))) == NULL)
+    {
+      free (fliphp);
       return (ctf_set_open_errno (errp, ENOMEM));
     }
-  hp = fp->ctf_header;
-  memcpy (hp, ctfsect->cts_data, hdrsz);
-  if (pp->ctp_version < CTF_VERSION_3)
-    upgrade_header_v2 (hp);
-  else if (pp->ctp_version < CTF_VERSION_4)
-    upgrade_header_v3 (hp);
 
-  /* UPTODO: header "upgrade" from BTF -> CTFv4.  */
+  memset (hp, 0, sizeof (ctf_header_t));
+  memcpy (hp, fliphp, hdrsz);
+  free (fliphp);
 
-  if (foreign_endian)
-    ctf_flip_header (hp);
-  fp->ctf_openflags = hp->cth_flags;
-  fp->ctf_size = hp->cth_stroff + hp->cth_strlen;
+  if (format < CTF_VERSION_3)
+    {
+      ctf_err_warn (NULL, 0, ECTF_INTERNAL,
+                   "Implementation of backward-compatible CTF reading still underway\n");
+      return (ctf_set_open_errno (errp, ECTF_INTERNAL));
+#if 0
+      upgrade_header_v2 (hp); /* Upgrades to v3 */
+#endif
+    }
 
-  ctf_dprintf ("ctf_bufopen: uncompressed size=%lu\n",
-              (unsigned long) fp->ctf_size);
+  if (format < CTF_VERSION_4)
+    {
+      header_v3 = (ctf_header_v3_t *) hp;
+
+      if ((hp = malloc (sizeof (ctf_header_t))) == NULL)
+       {
+         free (header_v3);
+         return (ctf_set_open_errno (errp, ENOMEM));
+       }
+      memcpy (hp, header_v3, sizeof (ctf_header_t));
+
+      ctf_err_warn (NULL, 0, ECTF_INTERNAL,
+                   "Implementation of backward-compatible CTF reading still underway\n");
+      return (ctf_set_open_errno (errp, ECTF_INTERNAL));
+#if 0
+      upgrade_header_v3 (hp);
+#endif
+    }
+
+  /* Validation.  */
+
+  if (_libctf_unlikely_ (format == IS_UNKNOWN))
+    {
+      ctf_set_open_errno (errp, ECTF_CTFVERS);
+      goto validation_fail;
+    }
+
+  if (_libctf_unlikely_ (format != IS_BTF
+                        && ((version < CTF_VERSION_1)
+                            || (version > CTF_VERSION_4))))
+    {
+      ctf_set_open_errno (errp, ECTF_CTFVERS);
+      goto validation_fail;
+    }
+
+  if (_libctf_unlikely_ (format == IS_BTF
+                        && (version != 1 || hp->btf.bth_hdr_len
+                            != sizeof (struct ctf_btf_header))))
+    {
+      ctf_err_warn (NULL, 0, ECTF_CTFVERS,
+                   _("BTF version %i or header length %i unknown: expecting 1, %zi\n"),
+                  version, hp->btf.bth_hdr_len,
+                  sizeof (struct ctf_btf_header));
+      ctf_set_open_errno (errp, ECTF_CTFVERS);
+      goto validation_fail;
+    }
 
-  if (hp->cth_lbloff > fp->ctf_size || hp->cth_objtoff > fp->ctf_size
-      || 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)
+  if (_libctf_unlikely_ (format != IS_BTF && (symsect != NULL)
+                        && (version < CTF_VERSION_2)))
     {
-      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("header offset exceeds CTF size"));
-      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+      /* The symtab can contain function entries which contain embedded CTF
+        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, ECTF_NOTSUP, _("ctf_bufopen: CTF version %d "
+                                           "symsect not supported"),
+                   version);
+      ctf_set_open_errno (errp, ECTF_NOTSUP);
+      goto validation_fail;
+    }
+
+  if (_libctf_unlikely_ (format < IS_BTF && pp->ctp_flags > CTF_F_MAX_3))
+    {
+      ctf_err_warn (NULL, 0, ECTF_FLAGS, _("ctf_bufopen: invalid header "
+                                          "flags: %x"),
+                   (unsigned int) pp->ctp_flags);
+      ctf_set_open_errno (errp, ECTF_FLAGS);
+      goto validation_fail;
+    }
+
+  if (_libctf_unlikely_ (format >= IS_BTF && bp->btf_flags != 0))
+    {
+      ctf_err_warn (NULL, 0, ECTF_FLAGS, _("ctf_bufopen: nonzero BTF header "
+                                          "flags: %x"),
+                   (unsigned int) bp->btf_flags);
+      ctf_set_open_errno (errp, ECTF_FLAGS);
+      goto validation_fail;
     }
 
-  if (hp->cth_lbloff > hp->cth_objtoff
-      || hp->cth_objtoff > hp->cth_funcoff
-      || hp->cth_funcoff > hp->cth_typeoff
-      || hp->cth_funcoff > hp->cth_objtidxoff
-      || hp->cth_objtidxoff > hp->cth_funcidxoff
-      || hp->cth_funcidxoff > hp->cth_varoff
-      || hp->cth_varoff > hp->cth_typeoff || hp->cth_typeoff > hp->cth_stroff)
+  if (_libctf_unlikely_ (format == IS_CTF && (hp->cth_flags & ~(CTF_F_MAX)) != 0))
     {
-      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("overlapping CTF sections"));
-      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+      ctf_err_warn (NULL, 0, ECTF_FLAGS, _("ctf_bufopen: invalid header "
+                                          "flags: %llx"),
+                   (unsigned long long) hp->cth_flags);
+      ctf_set_open_errno (errp, ECTF_FLAGS);
+      goto validation_fail;
     }
 
-  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))
+  /* Check for overlaps. BTF and v4 have a lot more freedom to reorder sections
+     than earlier versions do, but we assume for simplicity that they are
+     always emitted in the same order.  Because all the offsets are relative to
+     the end of the BTF header, we must also check that they don't overlap the
+     CTF header that can follow it.  We do allow such overlaps if the section
+     length is zero, since that lets us treat a BTF header stuffed into a
+     ctf_header_t as valid (with all remaining offsets zero).  */
+
+  if (_libctf_unlikely_
+      (hp->cth_objt_off + hp->cth_objt_len > hp->cth_func_off
+       || hp->cth_func_off + hp->cth_func_len > hp->cth_objtidx_off
+       || hp->cth_objtidx_off + hp->cth_objtidx_len > hp->cth_funcidx_off
+       || hp->cth_funcidx_off + hp->cth_funcidx_len > hp->btf.bth_type_off
+       || hp->btf.bth_type_off + hp->btf.bth_type_len > hp->btf.bth_str_off
+       || (hp->cth_objt_off < ctf_adjustment && hp->cth_objt_len != 0)
+       || (hp->cth_func_off < ctf_adjustment && hp->cth_func_len != 0)
+       || (hp->cth_objtidx_off < ctf_adjustment && hp->cth_objtidx_len != 0)
+       || (hp->cth_funcidx_off < ctf_adjustment && hp->cth_funcidx_len != 0)
+       || (hp->btf.bth_type_off < ctf_adjustment && hp->btf.bth_type_len != 0)
+       || (hp->btf.bth_str_off < ctf_adjustment && hp->btf.bth_str_len != 0)))
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("overlapping or misordered BTF/CTF sections"));
+      ctf_set_open_errno (errp, ECTF_CORRUPT);
+      goto validation_fail;
+    }
+
+  if (_libctf_unlikely_
+      ((hp->cth_objt_off & 2)
+       || (hp->cth_func_off & 2) || (hp->cth_objtidx_off & 2)
+       || (hp->cth_funcidx_off & 2) || (hp->btf.bth_type_off & 3)))
     {
       ctf_err_warn (NULL, 0, ECTF_CORRUPT,
                    _("CTF sections not properly aligned"));
-      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+      ctf_set_open_errno (errp, ECTF_CORRUPT);
+      goto validation_fail;
     }
 
   /* This invariant may be lifted in v5, 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))
+  if (_libctf_unlikely_ ((hp->cth_objtidx_len != 0) &&
+                        (hp->cth_objtidx_len != hp->cth_objt_len)))
     {
       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));
+                     "bytes"), hp->cth_objt_len, hp->cth_objtidx_len);
+      ctf_set_open_errno (errp, ECTF_CORRUPT);
+      goto validation_fail;
     }
 
-  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))
+  /* v3 only needs this invariant if CTF_F_NEWFUNCINFO is set: if it's not, the
+     section is ignored anyway.  Always true for v4.  */
+  if (_libctf_unlikely_ ((hp->cth_funcidx_len != 0) &&
+                        (hp->cth_funcidx_len != hp->cth_func_len) &&
+                        ((header_v3 && hp->cth_flags & CTF_F_NEWFUNCINFO)
+                         || !header_v3)))
     {
       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));
+                     "bytes"), hp->cth_func_len, hp->cth_funcidx_len);
+      ctf_set_open_errno (errp, ECTF_CORRUPT);
+      goto validation_fail;
     }
 
-  if ((hp->cth_parname != 0 && hp->cth_parname < hp->cth_parent_strlen)
-      || (hp->cth_cuname != 0 && hp->cth_cuname < hp->cth_parent_strlen))
+  if (_libctf_unlikely_
+      (hp->cth_parent_strlen != 0 &&
+       ((hp->cth_parent_name != 0 && hp->cth_parent_name < hp->cth_parent_strlen)
+       || (hp->cth_cu_name != 0 && hp->cth_cu_name < hp->cth_parent_strlen))))
     {
       ctf_err_warn (NULL, 0, ECTF_CORRUPT,
                    _("Parent dict or CU name string offsets "
                      "(at %x and %x, respectively) are themselves "
                      "within the parent (upper bound: %x), thus "
-                     "unreachable.\n"), hp->cth_parname, hp->cth_cuname,
+                     "unreachable.\n"), hp->cth_parent_name, hp->cth_cu_name,
                      hp->cth_parent_strlen);
-      return (ctf_set_open_errno (errp, ECTF_CORRUPT));
+      ctf_set_open_errno (errp, ECTF_CORRUPT);
+      goto validation_fail;
     }
 
+  /* Start to fill out the in-memory dict.  */
+
+  if ((fp = malloc (sizeof (ctf_dict_t))) == NULL)
+    return (ctf_set_open_errno (errp, ENOMEM));
+
+  memset (fp, 0, sizeof (ctf_dict_t));
+
+  fp->ctf_header = hp;
+  fp->ctf_v3_header = header_v3;
+
+  fp->ctf_openflags = hp->cth_flags;
+  fp->ctf_opened_btf = (format == IS_BTF);
+
+  /* The ctf_size includes the entire CTFv4 header excepting only the portion
+     shared with BTF (see ctf_adjustment).  */
+  fp->ctf_size = hp->btf.bth_str_off + hp->btf.bth_str_len;
+
+  if (_libctf_unlikely_ (hp->cth_objt_off > fp->ctf_size
+                        || hp->cth_func_off > fp->ctf_size
+                        || hp->cth_objtidx_off > fp->ctf_size
+                        || hp->cth_funcidx_off > fp->ctf_size
+                        || hp->btf.bth_type_off > fp->ctf_size))
+    {
+      ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("header offset or length exceeds CTF size"));
+      err = ECTF_CORRUPT;
+      goto bad;
+    }
+
+  ctf_dprintf ("ctf_bufopen: uncompressed size=%lu\n",
+              (unsigned long) fp->ctf_size);
+
   /* 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
      compressed but needs endian-flipping.  Otherwise we just put the data
      section's buffer pointer into ctf_buf, below.  */
 
-  /* Note: if this is a v1 buffer, it will be reallocated and expanded by
-     init_static_types().  */
-
-  /* UPTODO: may need to try unconditionally for BTF, and get flags from
-     somewhere else.  */
+  /* Note: if this is a v1 -- v3 buffer, it will be reallocated and expanded by
+     upgrade_types(), invoked by init_static_types().  */
 
   if (hp->cth_flags & CTF_F_COMPRESS)
     {
@@ -1650,9 +1848,6 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       const void *src;
       int rc = Z_OK;
 
-      /* We are allocating this ourselves, so we can drop the ctf header
-        copy in favour of ctf->ctf_header.  */
-
       if ((fp->ctf_base = malloc (fp->ctf_size)) == NULL)
        {
          err = ECTF_ZALLOC;
@@ -1663,10 +1858,16 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 
       src = (unsigned char *) ctfsect->cts_data + hdrsz;
       srclen = ctfsect->cts_size - hdrsz;
-      dstlen = fp->ctf_size;
+      dstlen = fp->ctf_size - ctf_adjustment;
       fp->ctf_buf = fp->ctf_base;
 
-      if ((rc = uncompress (fp->ctf_base, &dstlen, src, srclen)) != Z_OK)
+      /* Stick the CTF-only header portion into the buffer.  Not much use, but
+        it makes sure all the header offsets are right. */
+      memcpy (fp->ctf_base, (unsigned char *) ctfsect->cts_data
+             + sizeof (ctf_btf_header_t), ctf_adjustment);
+
+      if ((rc = uncompress (fp->ctf_base + ctf_adjustment, &dstlen,
+                           src, srclen)) != Z_OK)
        {
          ctf_err_warn (NULL, 0, ECTF_DECOMPRESS, _("zlib inflate err: %s"),
                        zError (rc));
@@ -1674,23 +1875,24 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
          goto bad;
        }
 
-      if ((size_t) dstlen != fp->ctf_size)
+      if ((size_t) dstlen != (fp->ctf_size - ctf_adjustment))
        {
          ctf_err_warn (NULL, 0, ECTF_CORRUPT,
                        _("zlib inflate short: got %lu of %lu bytes"),
-                       (unsigned long) dstlen, (unsigned long) fp->ctf_size);
+                       (unsigned long) dstlen,
+                       (unsigned long) (fp->ctf_size - ctf_adjustment));
          err = ECTF_CORRUPT;
          goto bad;
        }
     }
   else
     {
-      if (_libctf_unlikely_ (ctfsect->cts_size < hdrsz + fp->ctf_size))
+      if (_libctf_unlikely_ (ctfsect->cts_size < fp->ctf_size + hdrsz - ctf_adjustment))
        {
          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));
+                       (unsigned long) (fp->ctf_size + hdrsz - ctf_adjustment));
          err = ECTF_CORRUPT;
          goto bad;
        }
@@ -1703,38 +1905,51 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
              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;
+         memcpy (fp->ctf_base, ((unsigned char *) ctfsect->cts_data)
+                 + sizeof (ctf_btf_header_t), fp->ctf_size);
        }
       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;
        }
+
+      /* Ensure that the buffer we are using has offset 0 at the end of the BTF
+        header, if there is one, to avoid breaking all offset lookups.  */
+
+      fp->ctf_buf = fp->ctf_base + hdrsz - ctf_adjustment;
     }
 
-  /* Once we have uncompressed and validated the CTF data buffer, we can
-     proceed with initializing the ctf_dict_t we allocated above.
+  /* Once we have uncompressed and validated the (BTF or) CTF data buffer, we
+     can proceed with initializing the ctf_dict_t we allocated above.
 
      Nothing that depends on buf or base should be set directly in this function
      before the init_static_types() call, because it may be reallocated during
      transparent upgrade if this recension of libctf is so configured: see
      ctf_set_base().  */
 
-  ctf_set_version (fp, hp, hp->cth_version);
+  /* We declare BTF to be a kind of CTF so that we can use a single incrementing
+     integer to describe all CTF field access operations.  This obliges CTF to
+     remain BTF-compatible, come what may.  We also note that, as far as we
+     know so far, this CTF dict can be written as BTF without losing
+     information.  */
+
+  if (format == IS_BTF)
+    {
+      hp->cth_preamble.ctp_magic_version = (CTFv4_MAGIC << 16) | CTF_VERSION;
+      version = CTF_VERSION;
+    }
+
+  fp->ctf_version = version;
+  fp->ctf_dictops = &ctf_dictops[fp->ctf_version];
 
   /* Temporary assignment, just enough to be able to initialize the atoms table.
      This does not guarantee that we can look up strings: in v4, child dicts
      cannot reliably look up strings until after ctf_import.  */
 
   fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf
-    + hp->cth_stroff;
-  fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen;
+    + hp->btf.bth_str_off;
+  fp->ctf_str[CTF_STRTAB_0].cts_len = hp->btf.bth_str_len;
   if (ctf_str_create_atoms (fp) < 0)
     {
       err = ENOMEM;
@@ -1802,7 +2017,7 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
     }
 
   if (foreign_endian &&
-      (err = ctf_flip (fp, hp, fp->ctf_buf, 0)) != 0)
+      (err = ctf_flip (fp, hp, fp->ctf_buf, format == IS_BTF, 0)) != 0)
     {
       /* We can be certain that ctf_flip() will have endian-flipped everything
         other than the types table when we return.  In particular the header
@@ -1814,7 +2029,7 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 
   ctf_set_base (fp, hp, fp->ctf_base);
 
-  if ((err = init_static_types (fp, hp)) != 0)
+  if ((err = init_static_types (fp, hp, format == IS_BTF)) != 0)
     goto bad;
 
   /* Allocate and initialize the symtab translation table, pointed to by
@@ -1860,6 +2075,11 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
   fp->ctf_refcnt = 1;
   return fp;
 
+validation_fail:
+  free (header_v3);
+  free (hp);
+  return NULL;
+
 bad:
   ctf_set_open_errno (errp, err);
   ctf_err_warn_to_open (fp);
@@ -1913,10 +2133,10 @@ ctf_dict_close (ctf_dict_t *fp)
     return;
 
   fp->ctf_refcnt--;
-  free (fp->ctf_dyncuname);
-  free (fp->ctf_dynparname);
   if (fp->ctf_parent && !fp->ctf_parent_unreffed)
     ctf_dict_close (fp->ctf_parent);
+  free (fp->ctf_dyn_cu_name);
+  free (fp->ctf_dyn_parent_name);
 
   for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL; dtd = ntd)
     {
@@ -2087,7 +2307,7 @@ ctf_parent_file (ctf_dict_t *fp)
 const char *
 ctf_parent_name (ctf_dict_t *fp)
 {
-  return fp->ctf_parname;
+  return fp->ctf_parent_name;
 }
 
 /* Set the parent name.  It is an error to call this routine without calling
@@ -2095,12 +2315,12 @@ ctf_parent_name (ctf_dict_t *fp)
 int
 ctf_parent_name_set (ctf_dict_t *fp, const char *name)
 {
-  if (fp->ctf_dynparname != NULL)
-    free (fp->ctf_dynparname);
+  if (fp->ctf_dyn_parent_name != NULL)
+    free (fp->ctf_dyn_parent_name);
 
-  if ((fp->ctf_dynparname = strdup (name)) == NULL)
+  if ((fp->ctf_dyn_parent_name = strdup (name)) == NULL)
     return (ctf_set_errno (fp, ENOMEM));
-  fp->ctf_parname = fp->ctf_dynparname;
+  fp->ctf_parent_name = fp->ctf_dyn_parent_name;
   return 0;
 }
 
@@ -2109,19 +2329,19 @@ ctf_parent_name_set (ctf_dict_t *fp, const char *name)
 const char *
 ctf_cuname (ctf_dict_t *fp)
 {
-  return fp->ctf_cuname;
+  return fp->ctf_cu_name;
 }
 
 /* Set the compilation unit name.  */
 int
 ctf_cuname_set (ctf_dict_t *fp, const char *name)
 {
-  if (fp->ctf_dyncuname != NULL)
-    free (fp->ctf_dyncuname);
+  if (fp->ctf_dyn_cu_name != NULL)
+    free (fp->ctf_dyn_cu_name);
 
-  if ((fp->ctf_dyncuname = strdup (name)) == NULL)
+  if ((fp->ctf_dyn_cu_name = strdup (name)) == NULL)
     return (ctf_set_errno (fp, ENOMEM));
-  fp->ctf_cuname = fp->ctf_dyncuname;
+  fp->ctf_cu_name = fp->ctf_dyn_cu_name;
   return 0;
 }
 
@@ -2132,7 +2352,7 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
   int no_strings = fp->ctf_flags & LCTF_NO_STR;
   int old_flags = fp->ctf_flags;
   ctf_dict_t *old_parent = fp->ctf_parent;
-  const char *old_parname = fp->ctf_parname;
+  const char *old_parent_name = fp->ctf_parent_name;
   int old_unreffed = fp->ctf_parent_unreffed;
 
   if (pfp == NULL || pfp == fp->ctf_parent)
@@ -2203,7 +2423,7 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
   fp->ctf_pptrtab_len = 0;
   fp->ctf_pptrtab_typemax = 0;
 
-  if (fp->ctf_parname == NULL)
+  if (fp->ctf_parent_name == NULL)
     if ((err = ctf_parent_name_set (fp, "PARENT")) < 0)
       return err;                              /* errno is set for us.  */
 
@@ -2226,7 +2446,7 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
       fp->ctf_flags = old_flags;
       fp->ctf_parent_unreffed = old_unreffed;
       fp->ctf_parent = old_parent;
-      fp->ctf_parname = old_parname;
+      fp->ctf_parent_name = old_parent_name;
 
       if (fp->ctf_parent_unreffed)
        old_parent->ctf_refcnt++;