]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: types: struct/union member querying and iteration
authorNick Alcock <nick.alcock@oracle.com>
Thu, 24 Apr 2025 14:43:57 +0000 (15:43 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 25 Apr 2025 17:07:42 +0000 (18:07 +0100)
This commit revises ctf_member_next, ctf_member_iter, ctf_member_count, and
ctf_member_info for the new CTFv4 world.  This also pulls in a bunch of
infrastructure used by most of the type querying functions, and fundamental
changes to the way DTD records are represented in libctf (ctf-create not yet
adjusted).  Other type querying functions affected by changes in struct
representation are also changed.

There are some API changes here: new bit-width fields in ctf_member_f,
ctf_membinfo_t and ctf_member_next, and a fix to the type of the offset in
ctf_member_f, ctf_membinfo_t and and ctf_member_count.  (ctf_member_next got
the offset type right already.)

ctf_member_f also gets a new ctf_dict_t arg so that you can actually use
the member type it passes in without having to package up and pass in the
dict type yourself (a frequent need).  This change is later echoed in most
of the rest of the *_f typedefs.

 typedef struct ctf_membinfo
 {
   ctf_id_t ctm_type; /* Type of struct or union member.  */
-  unsigned long ctm_offset; /* Offset of member in bits.  */
+  size_t ctm_offset; /* Offset of member in bits.  */
+  int ctm_bit_width; /* Width of member in bits: -1: not bitfield */
 } ctf_membinfo_t;

-typedef int ctf_member_f (const char *name, ctf_id_t membtype,
-   unsigned long offset, void *arg);
+typedef int ctf_member_f (ctf_dict_t *, const char *name, ctf_id_t membtype,
+   size_t offset, int bit_width, void *arg);

 extern ssize_t ctf_member_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
  const char **name, ctf_id_t *membtype,
- int flags);
+ int *bit_width, int flags);

-int ctf_member_count (ctf_dict_t *, ctf_id_t);
+ssize_t ctf_member_count (ctf_dict_t *, ctf_id_t);

The DTD changes are that where before the ctf_dtdef_t had a dtd_data which
was the ctf_type_t type node for a type, and a separate dtd_vlen which was
the vlen buffer which (in the final serialized representation) would
directly follow that type, now it has one single buffer, dtd_buf, which
consists of a stream of one or more ctf_type_t nodes, followed by a vlen,
as it will appear in the final serialized form.  This buffer has internal
pointers into it: dtd_data is a pointer to the last ctf_type_t in the stream
(the true type node, after all prefixes), and dtd_vlen is a pointer to the
vlen (precisely one ctf_type_t after the dtd_data).  This representation is
nice because it means there is even less distinction between a dynamic type
added by ctf_add_*() and a static one read directly out of a dict: you can
traverse the entire type without caring where it came from, simplifying
most of the type querying functions.

(There are a few more things in there which will be useful mostly when
adding new types: their uses will be seen later.)

Two new nontrivial functions exist (one of which is annoyingly tangled up in
the diff, sorry about that): ctf_find_prefix, which hunts down a given
prefix (if it exists) among the possibly many that may exist on a type (so
you can ask it to find the CTF_K_BIG prefix for a type if it exists, and
it'll return you a pointer to its ctf_type_t record), and ctf_vlen, which
you hand a type ID and its ctf_type_t *, and it gives you back a pointer to
its vlen and tells you how long it is.  (This is one of only two places left
in ctf-types.c which cares whether a type is dynamic or not.  The other has
yet to be added).  Almost every function in ctf-types.c will end up calling
ctf_lookup_by_id and ctf_vlen in turn.

ctf_next_t has changed significantly: the ctn_type member is split in two so
that we can tell whether a given iterator works using types or indexes, and
we gain the ability to iterate over enum64s, DTDs themselves, and datasecs
(most of this will only be used in later commits).

The old internal function ctf_struct_member, which handled the distinction
between ctf_member_t and ctf_lmember_t, is gone.  Instead we have new code
that handles the different representation of bitfield versus non-bitfield
structs and unions, and more code to handle the different representation of
CTF_K_BIG structs and unions (their offsets are the distance from the last
offset, rather than the distance from the start of the structure).

include/ctf-api.h
libctf/ctf-impl.h
libctf/ctf-lookup.c
libctf/ctf-types.c

index a771de234f98a0a0b1061001b0778ea328f72531..2b58a4dd92db75d58c867ed7120b7874b4c00177 100644 (file)
@@ -144,10 +144,15 @@ typedef struct ctf_encoding
   uint32_t cte_bits;            /* Size of storage in bits.  */
 } ctf_encoding_t;
 
+/* "Not bitfield" here really means "not recorded as a bitfield in the
+   structure". ctf_type_encoding() may reveal that the base type or an
+   intervening slice has bitfield encoding nonetheless.  */
+
 typedef struct ctf_membinfo
 {
   ctf_id_t ctm_type;           /* Type of struct or union member.  */
-  unsigned long ctm_offset;    /* Offset of member in bits.  */
+  size_t ctm_offset;           /* Offset of member in bits.  */
+  int ctm_bit_width;           /* Width of member in bits: -1: not bitfield */
 } ctf_membinfo_t;
 
 typedef struct ctf_arinfo
@@ -296,13 +301,13 @@ _CTF_ERRORS
 
 typedef int ctf_visit_f (const char *name, ctf_id_t type, unsigned long offset,
                         int depth, void *arg);
-typedef int ctf_member_f (const char *name, ctf_id_t membtype,
-                         unsigned long offset, void *arg);
 typedef int ctf_enum_f (const char *name, int val, void *arg);
 typedef int ctf_variable_f (const char *name, ctf_id_t type, void *arg);
 typedef int ctf_type_f (ctf_id_t type, void *arg);
 typedef int ctf_type_all_f (ctf_id_t type, int flag, void *arg);
                         void *arg);
+typedef int ctf_member_f (ctf_dict_t *, const char *name, ctf_id_t membtype,
+                         size_t offset, int bit_width, void *arg);
 typedef int ctf_archive_member_f (ctf_dict_t *fp, const char *name, void *arg);
 typedef int ctf_archive_raw_member_f (const char *name, const void *content,
                                      size_t len, void *arg);
@@ -679,7 +684,7 @@ extern int ctf_array_info (ctf_dict_t *, ctf_id_t, ctf_arinfo_t *);
 
 extern int ctf_member_info (ctf_dict_t *, ctf_id_t, const char *,
                            ctf_membinfo_t *);
-extern int ctf_member_count (ctf_dict_t *, ctf_id_t);
+extern ssize_t ctf_member_count (ctf_dict_t *, ctf_id_t);
 
 /* Iterators.  */
 
@@ -690,7 +695,7 @@ extern int ctf_member_count (ctf_dict_t *, ctf_id_t);
 extern int ctf_member_iter (ctf_dict_t *, ctf_id_t, ctf_member_f *, void *);
 extern ssize_t ctf_member_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
                                const char **name, ctf_id_t *membtype,
-                               int flags);
+                               int *bit_width, int flags);
 
 /* Return all enumeration constants in a given enum type.  */
 extern int ctf_enum_iter (ctf_dict_t *, ctf_id_t, ctf_enum_f *, void *);
index 5bbfdf68d7d65465d55c58e5729922faf129f49a..95168ca7677d70c5725cc5d135a67a6e8e6dad21 100644 (file)
@@ -182,9 +182,15 @@ typedef struct ctf_dtdef
   ctf_id_t dtd_type;           /* Type identifier for this definition.  */
   ctf_id_t dtd_final_type;     /* Final (nonprovisional) id, if nonzero.  */
   ctf_list_t dtd_refs;         /* Refs to this DTD's dtd_type: see below.  */
-  ctf_type_t dtd_data;         /* Type node, including name.  */
-  size_t dtd_vlen_alloc;       /* Total vlen space allocated (vbytes).  */
-  unsigned char *dtd_vlen;     /* Variable-length data for this type.  */
+  ctf_type_t *dtd_buf;         /* Type nodes plus variable-length data for
+                                  this type.  */
+  size_t dtd_buf_size;         /* Length of dtd_buf in bytes.  */
+  ctf_type_t *dtd_data;                /* True type node, including name (pointer into
+                                  dtd_buf).  */
+  unsigned char *dtd_vlen;     /* Actual vlen data (pointer into dtd_buf).  */
+  size_t dtd_vlen_size;                /* Total vlen space so far (vbytes).  */
+  uint64_t dtd_last_offset;    /* Offset of the last struct field.  */
+  int dtd_flags;               /* Some of the DTD_F_ flags.  */
 } ctf_dtdef_t;
 
 typedef struct ctf_dvdef
@@ -549,11 +555,15 @@ typedef struct ctf_next_hkv
 struct ctf_next
 {
   void (*ctn_iter_fun) (void);
-  ctf_id_t ctn_type;
+  union
+  {
+    ctf_id_t ctn_type;
+    ctf_id_t ctn_idx;
+  } i;
   size_t ctn_size;
   ssize_t ctn_increment;
   const ctf_type_t *ctn_tp;
-  uint32_t ctn_n;
+  size_t ctn_n;
 
   /* Some iterators contain other iterators, in addition to their other
      state.  We allow for inner and outer iterators, for two-layer nested loops
@@ -563,13 +573,14 @@ struct ctf_next
 
   /* We can save space on this side of things by noting that a type is either
      dynamic or not, as a whole, and a given iterator can only iterate over one
-     kind of thing at once: so we can overlap the DTD and non-DTD members, and
-     the structure, variable and enum members, etc.  */
+     kind of thing at once: so we can overlap the DTD and non-DTD members, the
+     structure and enum members, etc.  */
   union
   {
-    unsigned char *ctn_vlen;
+    const ctf_dtdef_t *ctn_dtd;
     const ctf_enum_t *ctn_en;
-    const ctf_dvdef_t *ctn_dvd;
+    const ctf_enum64_t *ctn_en64;
+    const ctf_var_secinfo_t *ctn_datasec;
     ctf_next_hkv_t *ctn_sorted_hkv;
     void **ctn_hash_slot;
   } u;
@@ -633,6 +644,8 @@ extern ctf_dynhash_t *ctf_name_table (ctf_dict_t *, int);
 extern ctf_id_t ctf_lookup_variable_here (ctf_dict_t *fp, const char *name);
 extern const ctf_type_t *ctf_lookup_by_id (ctf_dict_t **, ctf_id_t,
                                           const ctf_type_t **suffix);
+extern const ctf_type_t *ctf_find_prefix (ctf_dict_t *, const ctf_type_t *,
+                                         int kind);
 extern ctf_id_t ctf_lookup_by_sym_or_name (ctf_dict_t *, unsigned long symidx,
                                           const char *symname, int try_parent,
                                           int is_function);
index ac874fca2006cd1e83ef2904ec21f388c5157e64..dcc189f0bdeb7a38d07bc478a351ed9e46909409 100644 (file)
@@ -426,6 +426,22 @@ ctf_lookup_by_id (ctf_dict_t **fpp, ctf_id_t type, const ctf_type_t **suffix)
     }
 }
 
+/* Find a given prefix in some type, if any.  */
+const ctf_type_t *
+ctf_find_prefix (ctf_dict_t *fp, const ctf_type_t *tp, int kind)
+{
+  uint32_t kind_ = kind;
+
+  while (LCTF_IS_PREFIXED_INFO (tp->ctt_info)
+        && CTF_INFO_KIND (tp->ctt_info) != kind_)
+    tp++;
+
+  if (LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info) != kind_)
+    return NULL;
+
+  return tp;
+}
+
 typedef struct ctf_lookup_idx_key
 {
   ctf_dict_t *clik_fp;
index 6a3dff3e4ca5813658720aaf327ec899d5b1b640..6b8ff8659d8e67c32674f50c0d7fe8cd58d0a29d 100644 (file)
@@ -134,34 +134,28 @@ ctf_index_to_type (const ctf_dict_t *fp, uint32_t idx)
     return fp->ctf_provtypemax + (fp->ctf_typemax - idx);
 }
 
-/* Expand a structure element into the passed-in ctf_lmember_t.  */
+/* Figure out the vlen and optionally vlen length for some type.  */
 
-static int
-ctf_struct_member (ctf_dict_t *fp, ctf_lmember_t *dst, const ctf_type_t *tp,
-                  unsigned char *vlen, size_t vbytes, size_t n)
+static unsigned char *
+ctf_vlen (ctf_dict_t *fp, ctf_id_t type, const ctf_type_t *tp, size_t *vlen_len)
 {
-  if (!ctf_assert (fp, n < LCTF_INFO_VLEN (fp, tp->ctt_info)))
-    return -1;                                 /* errno is set for us.  */
+  ctf_dtdef_t *dtd;
 
-  /* Already large.  */
-  if (tp->ctt_size == CTF_LSIZE_SENT)
+  if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
     {
-      ctf_lmember_t *lmp = (ctf_lmember_t *) vlen;
-
-      if (!ctf_assert (fp, (n + 1) * sizeof (ctf_lmember_t) <= vbytes))
-       return -1;                              /* errno is set for us.  */
-
-      memcpy (dst, &lmp[n], sizeof (ctf_lmember_t));
+      if (vlen_len)
+       *vlen_len = LCTF_VLEN (fp, dtd->dtd_buf);
+      return dtd->dtd_vlen;
     }
   else
     {
-      ctf_member_t *mp = (ctf_member_t *) vlen;
-      dst->ctlm_name = mp[n].ctm_name;
-      dst->ctlm_type = mp[n].ctm_type;
-      dst->ctlm_offsetlo = mp[n].ctm_offset;
-      dst->ctlm_offsethi = 0;
+      ssize_t size, increment;
+
+      ctf_get_ctt_size (fp, tp, &size, &increment);
+      if (vlen_len)
+       *vlen_len = LCTF_VLEN (fp, tp);
+      return (unsigned char *) tp + increment;
     }
-  return 0;
 }
 
 /* Iterate over the members of a STRUCT or UNION.  We pass the name, member
@@ -172,13 +166,15 @@ ctf_member_iter (ctf_dict_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
 {
   ctf_next_t *i = NULL;
   ssize_t offset;
+  int bit_width;
   const char *name;
   ctf_id_t membtype;
 
-  while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
+  while ((offset = ctf_member_next (fp, type, &i, &name, &membtype,
+                                   &bit_width, 0)) >= 0)
     {
       int rc;
-      if ((rc = func (name, membtype, offset, arg)) != 0)
+      if ((rc = func (fp, name, membtype, offset, bit_width, arg)) != 0)
        {
          ctf_next_destroy (i);
          return rc;
@@ -196,13 +192,16 @@ ctf_member_iter (ctf_dict_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
 
 ssize_t
 ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
-                const char **name, ctf_id_t *membtype, int flags)
+                const char **name, ctf_id_t *membtype,
+                int *bit_width, int flags)
 {
   ctf_dict_t *ofp = fp;
   uint32_t kind;
   ssize_t offset;
-  uint32_t max_vlen;
+  size_t nmemb;
+  unsigned char *vlen;
   ctf_next_t *i = *it;
+  const ctf_type_t *prefix, *tp;
 
   if (fp->ctf_flags & LCTF_NO_STR)
     return (ctf_set_errno (fp, ECTF_NOPARENT));
@@ -210,42 +209,25 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
   if (!i)
     {
       const ctf_type_t *tp;
-      ctf_dtdef_t *dtd;
-      ssize_t size;
-      ssize_t increment;
 
       if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
        return -1;                      /* errno is set for us.  */
 
-      if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
+      if ((tp = ctf_lookup_by_id (&fp, type, NULL)) == NULL)
        return -1;                      /* errno is set for us.  */
 
-      if ((i = ctf_next_create ()) == NULL)
-       return ctf_set_errno (ofp, ENOMEM);
-      i->cu.ctn_fp = ofp;
-      i->ctn_tp = tp;
-
-      ctf_get_ctt_size (fp, tp, &size, &increment);
-      kind = LCTF_INFO_KIND (fp, tp->ctt_info);
+      kind = LCTF_KIND (fp, tp);
 
       if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
-       {
-         ctf_next_destroy (i);
-         return (ctf_set_errno (ofp, ECTF_NOTSOU));
-       }
+       return (ctf_set_errno (ofp, ECTF_NOTSOU));
 
-      if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
-       {
-         i->u.ctn_vlen = dtd->dtd_vlen;
-         i->ctn_size = dtd->dtd_vlen_alloc;
-       }
-      else
-       {
-         unsigned long vlen = LCTF_INFO_VLEN (fp, tp->ctt_info);
+      if ((i = ctf_next_create ()) == NULL)
+       return ctf_set_errno (ofp, ENOMEM);
+
+      i->ctn_tp = tp;
+      i->cu.ctn_fp = ofp;
+      i->u.ctn_dtd = ctf_dynamic_type (fp, type);
 
-         i->u.ctn_vlen = (unsigned char *) tp + increment;
-         i->ctn_size = LCTF_VBYTES (fp, kind, size, vlen);;
-       }
       i->ctn_iter_fun = (void (*) (void)) ctf_member_next;
       i->ctn_n = 0;
       *it = i;
@@ -261,7 +243,20 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
   if ((fp = ctf_get_dict (ofp, type)) == NULL)
     return (ctf_set_errno (ofp, ECTF_NOPARENT));
 
-  max_vlen = LCTF_INFO_VLEN (fp, i->ctn_tp->ctt_info);
+  vlen = ctf_vlen (fp, type, i->ctn_tp, &nmemb);
+
+  /* Reset the tp on every iteration if this is a dynamic type: adding members
+     can move it, and hunt down any CTF_K_BIG prefix.  */
+
+  if (i->u.ctn_dtd)
+    i->ctn_tp = i->u.ctn_dtd->dtd_buf;
+
+  if ((prefix = ctf_find_prefix (fp, i->ctn_tp, CTF_K_BIG)) == NULL)
+    prefix = i->ctn_tp;
+  tp = prefix;
+
+  while (LCTF_IS_PREFIXED_KIND (LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info)))
+    tp++;
 
   /* When we hit an unnamed struct/union member, we set ctn_type to indicate
      that we are inside one, then return the unnamed member: on the next call,
@@ -269,46 +264,69 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
      the sub-struct until it later turns out that that iteration has ended.  */
 
  retry:
-  if (!i->ctn_type)
+  if (!i->i.ctn_type)
     {
-      ctf_lmember_t memb;
+      ctf_member_t *memb = (ctf_member_t *) vlen;
       const char *membname;
 
-      if (i->ctn_n == max_vlen)
+      if (i->ctn_n >= nmemb)
        goto end_iter;
 
-      if (ctf_struct_member (fp, &memb, i->ctn_tp, i->u.ctn_vlen, i->ctn_size,
-                            i->ctn_n) < 0)
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+      membname = ctf_strptr (fp, memb[i->ctn_n].ctm_name);
 
-      membname = ctf_strptr (fp, memb.ctlm_name);
+      /* Skip nameless padding types.  */
+      if (membname[0] == 0 && memb[i->ctn_n].ctm_type == 0)
+       {
+         i->ctn_n++;
+         goto retry;
+       }
 
       if (name)
        *name = membname;
       if (membtype)
-       *membtype = memb.ctlm_type;
-      offset = (unsigned long) CTF_LMEM_OFFSET (&memb);
+       *membtype = memb[i->ctn_n].ctm_type;
+      if (bit_width)
+       {
+         if (CTF_INFO_KFLAG (tp->ctt_info))
+           *bit_width = CTF_MEMBER_BIT_SIZE (memb[i->ctn_n].ctm_offset);
+         else
+           *bit_width = 0;
+       }
+
+      if (CTF_INFO_KFLAG (tp->ctt_info))
+       offset = CTF_MEMBER_BIT_OFFSET (memb[i->ctn_n].ctm_offset);
+      else
+       offset = memb[i->ctn_n].ctm_offset;
+
+      /* CTF_K_BIG offsets are gap sizes: convert to offset-from-start.
+        Keep track of the offset-so-far in ctn_size.  */
+
+      if (prefix != tp)
+       {
+         i->ctn_size += offset;
+         offset = i->ctn_size;
+       }
 
       if (membname[0] == 0
-         && (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT
-             || ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION))
-       i->ctn_type = memb.ctlm_type;
+         && (ctf_type_kind (fp, memb[i->ctn_n].ctm_type) == CTF_K_STRUCT
+             || ctf_type_kind (fp, memb[i->ctn_n].ctm_type) == CTF_K_UNION))
+       i->i.ctn_type = memb[i->ctn_n].ctm_type;
       i->ctn_n++;
 
       /* The callers might want automatic recursive sub-struct traversal.  */
       if (!(flags & CTF_MN_RECURSE))
-       i->ctn_type = 0;
+       i->i.ctn_type = 0;
 
       /* Sub-struct traversal starting?  Take note of the offset of this member,
         for later boosting of sub-struct members' offsets.  */
-      if (i->ctn_type)
+      if (i->i.ctn_type)
        i->ctn_increment = offset;
     }
   /* Traversing a sub-struct?  Just return it, with the offset adjusted.  */
   else
     {
-      ssize_t ret = ctf_member_next (fp, i->ctn_type, &i->ctn_next, name,
-                                    membtype, flags);
+      ssize_t ret = ctf_member_next (fp, i->i.ctn_type, &i->ctn_next, name,
+                                    membtype, bit_width, flags);
 
       if (ret >= 0)
        return ret + i->ctn_increment;
@@ -317,7 +335,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
        {
          ctf_next_destroy (i);
          *it = NULL;
-         i->ctn_type = 0;
+         i->i.ctn_type = 0;
          ctf_set_errno (ofp, ctf_errno (fp));
          return ret;
        }
@@ -325,7 +343,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
       if (!ctf_assert (fp, (i->ctn_next == NULL)))
         return (ctf_set_errno (ofp, ctf_errno (fp)));
 
-      i->ctn_type = 0;
+      i->i.ctn_type = 0;
       /* This sub-struct has ended: on to the next real member.  */
       goto retry;
     }
@@ -1113,35 +1131,20 @@ ctf_type_align (ctf_dict_t *fp, ctf_id_t type)
     case CTF_K_UNION:
       {
        size_t align = 0;
-       ctf_dtdef_t *dtd;
        unsigned char *vlen;
-       uint32_t i = 0, n = LCTF_INFO_VLEN (fp, tp->ctt_info);
-       ssize_t size, increment, vbytes;
-
-       ctf_get_ctt_size (fp, tp, &size, &increment);
+       uint32_t i = 0;
+       size_t n;
 
-       if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
-         {
-           vlen = dtd->dtd_vlen;
-           vbytes = dtd->dtd_vlen_alloc;
-         }
-       else
-         {
-           vlen = (unsigned char *) tp + increment;
-           vbytes = LCTF_VBYTES (fp, kind, size, n);
-         }
+       vlen = ctf_vlen (fp, type, tp, &n);
 
        if (kind == CTF_K_STRUCT)
          n = MIN (n, 1);       /* Only use first member for structs.  */
 
        for (; n != 0; n--, i++)
          {
-           ctf_lmember_t memb;
+           ctf_member_t *memb = (ctf_member_t *) vlen;
 
-           if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
-             return -1;                                /* errno is set for us.  */
-
-           ssize_t am = ctf_type_align (ofp, memb.ctlm_type);
+           ssize_t am = ctf_type_align (ofp, memb[i].ctm_type);
            align = MAX (align, (size_t) am);
          }
        return align;
@@ -1497,7 +1500,7 @@ ctf_type_compat (ctf_dict_t *lfp, ctf_id_t ltype,
 /* Return the number of members in a STRUCT or UNION, or the number of
    enumerators in an ENUM.  The count does not include unnamed sub-members.  */
 
-int
+ssize_t
 ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
 {
   ctf_dict_t *ofp = fp;
@@ -1507,15 +1510,15 @@ ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
   if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
     return -1;                 /* errno is set for us.  */
 
-  if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
+  if ((tp = ctf_lookup_by_id (&fp, type, NULL)) == NULL)
     return -1;                 /* errno is set for us.  */
 
-  kind = LCTF_INFO_KIND (fp, tp->ctt_info);
+  kind = LCTF_KIND (fp, tp);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION && kind != CTF_K_ENUM)
     return (ctf_set_errno (ofp, ECTF_NOTSUE));
 
-  return LCTF_INFO_VLEN (fp, tp->ctt_info);
+  return LCTF_VLEN (fp, tp);
 }
 
 /* Return the type and offset for a given member of a STRUCT or UNION.  */
@@ -1525,11 +1528,12 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
                 ctf_membinfo_t *mip)
 {
   ctf_dict_t *ofp = fp;
-  const ctf_type_t *tp;
-  ctf_dtdef_t *dtd;
+  const ctf_type_t *tp, *suffix;
+  int big = 0;
+  size_t total_offset = 0;
   unsigned char *vlen;
-  ssize_t size, increment, vbytes;
-  uint32_t kind, n, i = 0;
+  uint32_t kind, i = 0;
+  size_t n;
 
   if (fp->ctf_flags & LCTF_NO_STR)
     return (ctf_set_errno (fp, ECTF_NOPARENT));
@@ -1537,50 +1541,61 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
   if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
     return -1;                 /* errno is set for us.  */
 
-  if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
+  if ((tp = ctf_lookup_by_id (&fp, type, &suffix)) == NULL)
     return -1;                 /* errno is set for us.  */
 
-  ctf_get_ctt_size (fp, tp, &size, &increment);
-  kind = LCTF_INFO_KIND (fp, tp->ctt_info);
+  kind = LCTF_KIND (fp, tp);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
     return (ctf_set_errno (ofp, ECTF_NOTSOU));
 
-  n = LCTF_INFO_VLEN (fp, tp->ctt_info);
-  if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
-    {
-      vlen = dtd->dtd_vlen;
-      vbytes = dtd->dtd_vlen_alloc;
-    }
-  else
-    {
-      vlen = (unsigned char *) tp + increment;
-      vbytes = LCTF_VBYTES (fp, kind, size, n);
-    }
+  vlen = ctf_vlen (fp, type, tp, &n);
+
+  big = (ctf_find_prefix (fp, tp, CTF_K_BIG) != NULL);
 
   for (; n != 0; n--, i++)
     {
-      ctf_lmember_t memb;
+      ctf_member_t *memb = (ctf_member_t *) vlen;
       const char *membname;
+      size_t offset;
+      int bit_width = 0;
 
-      if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+      membname = ctf_strptr (fp, memb->ctm_name);
 
-      membname = ctf_strptr (fp, memb.ctlm_name);
+      if (CTF_INFO_KFLAG (suffix->ctt_info))
+       {
+         offset = CTF_MEMBER_BIT_OFFSET (memb->ctm_offset);
+         bit_width = CTF_MEMBER_BIT_SIZE (memb->ctm_offset);
+       }
+      else
+       offset = memb->ctm_offset;
+
+      total_offset += offset;
 
+      /* Unnamed struct/union member.  */
       if (membname[0] == 0
-         && (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT
-             || ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION)
-         && (ctf_member_info (fp, memb.ctlm_type, name, mip) == 0))
+         && (ctf_type_kind (fp, memb->ctm_type) == CTF_K_STRUCT
+             || ctf_type_kind (fp, memb->ctm_type) == CTF_K_UNION)
+         && (ctf_member_info (fp, memb->ctm_type, name, mip) == 0))
        {
-         mip->ctm_offset += (unsigned long) CTF_LMEM_OFFSET (&memb);
+         if (!big)
+           mip->ctm_offset += total_offset;
+         else
+           mip->ctm_offset += offset;
+
          return 0;
        }
 
+      /* Ordinary member.  */
       if (strcmp (membname, name) == 0)
        {
-         mip->ctm_type = memb.ctlm_type;
-         mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (&memb);
+         mip->ctm_type = memb->ctm_type;
+         if (big)
+           mip->ctm_offset = total_offset + memb->ctm_offset;
+         else
+           mip->ctm_offset = memb->ctm_offset;
+         mip->ctm_bit_width = bit_width;
+
          return 0;
        }
     }