[13/16] libctf: eliminate dtd_u, part 5: structs / unions

Message ID 20210306004023.164154-14-nick.alcock@oracle.com
State New
Headers show
Series
  • libctf: mostly cleanups and refactoring
Related show

Commit Message

Nick Clifton via Binutils March 6, 2021, 12:40 a.m.
Eliminate the dynamic member storage for structs and unions as we have
for other dynamic types.  This is much like the previous enum
elimination, except that structs and unions are the only types for which
a full-sized ctf_type_t might be needed.  Up to now, this decision has
been made in the individual ctf_add_{struct,union}_sized functions and
duplicated in ctf_add_member_offset.  The vlen machinery lets us
simplify this, always allocating a ctf_lmember_t and setting the
dtd_data's ctt_size to CTF_LSIZE_SENT: we figure out whether this is
really justified and (almost always) repack things down into a
ctf_stype_t at ctf_serialize time.

This allows us to eliminate the dynamic member paths from the iterators and
query functions in ctf-types.c in favour of always using the large-structure
vlen stuff for dynamic types (the diff is ugly but that's just because of the
volume of reindentation this calls for).  This also means the large-structure
vlen stuff gets more heavily tested, which is nice because it was an almost
totally unused code path before now (it only kicked in for structures of size
>4GiB, and how often do you see those?)


The only extra complexity here is ctf_add_type.  Back in the days of the
nondeduplicating linker this was called a ridiculous number of times for
countless identical copies of structures: eschewing the repeated lookups of the
dtd in ctf_add_member_offset and adding the members directly saved an amazing
amount of time.  Now the nondeduplicating linker is gone, this is extreme
overoptimization: we can rip out the direct addition and use ctf_member_next and
ctf_add_member_offset, just like ctf_dedup_emit does.

We augment a ctf_add_type test to try adding a self-referential struct, the only
thing the ctf_add_type part of this change really perturbs.

This completes the elimination of dtd_u.

libctf/ChangeLog
2021-03-04  Nick Alcock  <nick.alcock@oracle.com>

	* ctf-impl.h (ctf_dtdef_t) <dtu_members>: Remove.
	<dtd_u>: Likewise.
	(ctf_dmdef_t): Remove.
	(struct ctf_next) <u.ctn_dmd>: Remove.
	* ctf-create.c (INITIAL_VLEN): New, more-or-less arbitrary initial
	vlen size.
	(ctf_add_enum): Use it.
	(ctf_dtd_delete): Do not free the (removed) dmd; remove string
	refs from the vlen on struct deletion.
	(ctf_add_struct_sized): Populate the vlen: do it by hand if
	promoting forwards.  Always populate the full-size
	lsizehi/lsizelo members.
	(ctf_add_union_sized): Likewise.
	(ctf_add_member_offset): Set up the vlen rather than the dmd.
	Expand it as needed, repointing string refs via
	ctf_str_move_pending. Add the member names as pending strings.
	Always populate the full-size lsizehi/lsizelo members.
	(membadd): Remove, folding back into...
	(ctf_add_type_internal): ... here, adding via an ordinary
	ctf_add_struct_sized and _next iteration rather than doing
	everything by hand.
	* ctf-serialize.c (ctf_copy_smembers): Remove this...
	(ctf_copy_lmembers): ... and this...
	(ctf_emit_type_sect): ... folding into here. Figure out if a
	ctf_stype_t is needed here, not in ctf_add_*_sized.
	(ctf_type_sect_size): Figure out the ctf_stype_t stuff the same
	way here.
	* ctf-types.c (ctf_member_next): Remove the dmd path and always
	use the vlen.  Force large-structure usage for dynamic types.
	(ctf_type_align): Likewise.
	(ctf_member_info): Likewise.
	(ctf_type_rvisit): Likewise.
	* testsuite/libctf-regression/type-add-unnamed-struct-ctf.c: Add a
	self-referential type to this test.
	* testsuite/libctf-regression/type-add-unnamed-struct.c: Adjusted
	accordingly.
	* testsuite/libctf-regression/type-add-unnamed-struct.lk: Likewise.
---
 libctf/ctf-create.c                           | 260 ++++++---------
 libctf/ctf-impl.h                             |  14 -
 libctf/ctf-serialize.c                        | 117 +++----
 libctf/ctf-types.c                            | 298 +++++++-----------
 .../type-add-unnamed-struct-ctf.c             |   1 +
 .../type-add-unnamed-struct.c                 |   2 +-
 .../type-add-unnamed-struct.lk                |   1 +
 7 files changed, 283 insertions(+), 410 deletions(-)

-- 
2.30.0.252.gc27e85e57d

Patch

diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index 3218e3e31ea..e87b91e928e 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -30,6 +30,12 @@ 
 #define roundup(x, y)  ((((x) + ((y) - 1)) / (y)) * (y))
 #endif
 
+/* The initial size of a dynamic type's vlen in members.  Arbitrary: the bigger
+   this is, the less allocation needs to be done for small structure
+   initialization, and the more memory is wasted for small structures during CTF
+   construction.  No effect on generated CTF or ctf_open()ed CTF. */
+#define INITIAL_VLEN 16
+
 /* Make sure the ptrtab has enough space for at least one more type.
 
    We start with 4KiB of ptrtab, enough for a thousand types, then grow it 25%
@@ -244,7 +250,6 @@  ctf_dtd_insert (ctf_dict_t *fp, ctf_dtdef_t *dtd, int flag, int kind)
 void
 ctf_dtd_delete (ctf_dict_t *fp, ctf_dtdef_t *dtd)
 {
-  ctf_dmdef_t *dmd, *nmd;
   int kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
   size_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
   int name_kind = kind;
@@ -256,14 +261,14 @@  ctf_dtd_delete (ctf_dict_t *fp, ctf_dtdef_t *dtd)
     {
     case CTF_K_STRUCT:
     case CTF_K_UNION:
-      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-	   dmd != NULL; dmd = nmd)
-	{
-	  if (dmd->dmd_name != NULL)
-	      free (dmd->dmd_name);
-	  nmd = ctf_list_next (dmd);
-	  free (dmd);
-	}
+      {
+	ctf_lmember_t *memb = (ctf_lmember_t *) dtd->dtd_vlen;
+	size_t i;
+
+	for (i = 0; i < vlen; i++)
+	  ctf_str_remove_ref (fp, ctf_strraw (fp, memb[i].ctlm_name),
+			      &memb[i].ctlm_name);
+      }
       break;
     case CTF_K_ENUM:
       {
@@ -797,6 +802,7 @@  ctf_add_struct_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
 {
   ctf_dtdef_t *dtd;
   ctf_id_t type = 0;
+  size_t initial_vlen = sizeof (ctf_lmember_t) * INITIAL_VLEN;
 
   /* Promote root-visible forwards to structs.  */
   if (name != NULL)
@@ -805,19 +811,21 @@  ctf_add_struct_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
   if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
     dtd = ctf_dtd_lookup (fp, type);
   else if ((type = ctf_add_generic (fp, flag, name, CTF_K_STRUCT,
-				    0, &dtd)) == CTF_ERR)
+				    initial_vlen, &dtd)) == CTF_ERR)
     return CTF_ERR;		/* errno is set for us.  */
 
-  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_STRUCT, flag, 0);
-
-  if (size > CTF_MAX_SIZE)
+  /* Forwards won't have any vlen yet.  */
+  if (dtd->dtd_vlen_alloc == 0)
     {
-      dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
-      dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (size);
-      dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (size);
+      if ((dtd->dtd_vlen = calloc (1, initial_vlen)) == NULL)
+	return (ctf_set_errno (fp, ENOMEM));
+      dtd->dtd_vlen_alloc = initial_vlen;
     }
-  else
-    dtd->dtd_data.ctt_size = (uint32_t) size;
+
+  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_STRUCT, flag, 0);
+  dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+  dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (size);
+  dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (size);
 
   return type;
 }
@@ -834,6 +842,7 @@  ctf_add_union_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
 {
   ctf_dtdef_t *dtd;
   ctf_id_t type = 0;
+  size_t initial_vlen = sizeof (ctf_lmember_t) * INITIAL_VLEN;
 
   /* Promote root-visible forwards to unions.  */
   if (name != NULL)
@@ -842,19 +851,21 @@  ctf_add_union_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
   if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
     dtd = ctf_dtd_lookup (fp, type);
   else if ((type = ctf_add_generic (fp, flag, name, CTF_K_UNION,
-				    0, &dtd)) == CTF_ERR)
+				    initial_vlen, &dtd)) == CTF_ERR)
     return CTF_ERR;		/* errno is set for us */
 
-  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_UNION, flag, 0);
-
-  if (size > CTF_MAX_SIZE)
+  /* Forwards won't have any vlen yet.  */
+  if (dtd->dtd_vlen_alloc == 0)
     {
-      dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
-      dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (size);
-      dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (size);
+      if ((dtd->dtd_vlen = calloc (1, initial_vlen)) == NULL)
+	return (ctf_set_errno (fp, ENOMEM));
+      dtd->dtd_vlen_alloc = initial_vlen;
     }
-  else
-    dtd->dtd_data.ctt_size = (uint32_t) size;
+
+  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_UNION, flag, 0);
+  dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+  dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (size);
+  dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (size);
 
   return type;
 }
@@ -870,7 +881,7 @@  ctf_add_enum (ctf_dict_t *fp, uint32_t flag, const char *name)
 {
   ctf_dtdef_t *dtd;
   ctf_id_t type = 0;
-  size_t initial_vlen = sizeof (ctf_enum_t) * 16;
+  size_t initial_vlen = sizeof (ctf_enum_t) * INITIAL_VLEN;
 
   /* Promote root-visible forwards to enums.  */
   if (name != NULL)
@@ -1066,12 +1077,13 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 		       ctf_id_t type, unsigned long bit_offset)
 {
   ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, souid);
-  ctf_dmdef_t *dmd;
 
   ssize_t msize, malign, ssize;
   uint32_t kind, vlen, root;
-  char *s = NULL;
+  size_t i;
   int is_incomplete = 0;
+  unsigned char *old_vlen;
+  ctf_lmember_t *memb;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
     return (ctf_set_errno (fp, ECTF_RDONLY));
@@ -1092,14 +1104,26 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   if (vlen == CTF_MAX_VLEN)
     return (ctf_set_errno (fp, ECTF_DTFULL));
 
+  old_vlen = dtd->dtd_vlen;
+  if (ctf_grow_vlen (fp, dtd, sizeof (ctf_lmember_t) * (vlen + 1)) < 0)
+    return -1;					/* errno is set for us.  */
+  memb = (ctf_lmember_t *) dtd->dtd_vlen;
+
+  if (dtd->dtd_vlen != old_vlen)
+    {
+      ptrdiff_t move = (signed char *) dtd->dtd_vlen - (signed char *) old_vlen;
+
+      /* Remove pending refs in the old vlen region and reapply them.  */
+
+      for (i = 0; i < vlen; i++)
+	ctf_str_move_pending (fp, &memb[i].ctlm_name, move);
+    }
+
   if (name != NULL)
     {
-      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-	   dmd != NULL; dmd = ctf_list_next (dmd))
-	{
-	  if (dmd->dmd_name != NULL && strcmp (dmd->dmd_name, name) == 0)
-	    return (ctf_set_errno (fp, ECTF_DUPLICATE));
-	}
+      for (i = 0; i < vlen; i++)
+	if (strcmp (ctf_strptr (fp, memb[i].ctlm_name), name) == 0)
+	  return (ctf_set_errno (fp, ECTF_DUPLICATE));
     }
 
   if ((msize = ctf_type_size (fp, type)) < 0 ||
@@ -1124,18 +1148,10 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 	return -1;		/* errno is set for us.  */
     }
 
-  if ((dmd = malloc (sizeof (ctf_dmdef_t))) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
-
-  if (name != NULL && (s = strdup (name)) == NULL)
-    {
-      free (dmd);
-      return (ctf_set_errno (fp, EAGAIN));
-    }
-
-  dmd->dmd_name = s;
-  dmd->dmd_type = type;
-  dmd->dmd_value = -1;
+  memb[vlen].ctlm_name = ctf_str_add_pending (fp, name, &memb[vlen].ctlm_name);
+  memb[vlen].ctlm_type = type;
+  if (memb[vlen].ctlm_name == 0 && name != NULL && name[0] != '\0')
+    return -1;			/* errno is set for us.  */
 
   if (kind == CTF_K_STRUCT && vlen != 0)
     {
@@ -1143,9 +1159,8 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 	{
 	  /* Natural alignment.  */
 
-	  ctf_dmdef_t *lmd = ctf_list_prev (&dtd->dtd_u.dtu_members);
-	  ctf_id_t ltype = ctf_type_resolve (fp, lmd->dmd_type);
-	  size_t off = lmd->dmd_offset;
+	  ctf_id_t ltype = ctf_type_resolve (fp, memb[vlen - 1].ctlm_type);
+	  size_t off = CTF_LMEM_OFFSET(&memb[vlen - 1]);
 
 	  ctf_encoding_t linfo;
 	  ssize_t lsize;
@@ -1155,10 +1170,7 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 	     cannot insert right after such a member without explicit offset
 	     specification, because its alignment and size is not known.  */
 	  if (ltype == CTF_ERR)
-	    {
-	      free (dmd);
-	      return -1;	/* errno is set for us.  */
-	    }
+	    return -1;	/* errno is set for us.  */
 
 	  if (is_incomplete)
 	    {
@@ -1176,14 +1188,15 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 	    off += lsize * CHAR_BIT;
 	  else if (lsize == -1 && ctf_errno (fp) == ECTF_INCOMPLETE)
 	    {
+	      const char *lname = ctf_strraw (fp, memb[vlen - 1].ctlm_name);
+
 	      ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
 			    _("ctf_add_member_offset: cannot add member %s of "
 			      "type %lx to struct %lx without specifying "
 			      "explicit offset after member %s of type %lx, "
 			      "which is an incomplete type\n"),
 			    name ? name : _("(unnamed member)"), type, souid,
-			    lmd->dmd_name ? lmd->dmd_name
-			    : _("(unnamed member)"), ltype);
+			    lname ? lname : _("(unnamed member)"), ltype);
 	      return -1;			/* errno is set for us.  */
 	    }
 
@@ -1198,36 +1211,32 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 
 	  off = roundup (off, CHAR_BIT) / CHAR_BIT;
 	  off = roundup (off, MAX (malign, 1));
-	  dmd->dmd_offset = off * CHAR_BIT;
+	  memb[vlen].ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (off * CHAR_BIT);
+	  memb[vlen].ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (off * CHAR_BIT);
 	  ssize = off + msize;
 	}
       else
 	{
 	  /* Specified offset in bits.  */
 
-	  dmd->dmd_offset = bit_offset;
+	  memb[vlen].ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (bit_offset);
+	  memb[vlen].ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (bit_offset);
 	  ssize = ctf_get_ctt_size (fp, &dtd->dtd_data, NULL, NULL);
 	  ssize = MAX (ssize, ((signed) bit_offset / CHAR_BIT) + msize);
 	}
     }
   else
     {
-      dmd->dmd_offset = 0;
+      memb[vlen].ctlm_offsethi = 0;
+      memb[vlen].ctlm_offsetlo = 0;
       ssize = ctf_get_ctt_size (fp, &dtd->dtd_data, NULL, NULL);
       ssize = MAX (ssize, msize);
     }
 
-  if ((size_t) ssize > CTF_MAX_SIZE)
-    {
-      dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
-      dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (ssize);
-      dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (ssize);
-    }
-  else
-    dtd->dtd_data.ctt_size = (uint32_t) ssize;
-
+  dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+  dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (ssize);
+  dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (ssize);
   dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1);
-  ctf_list_append (&dtd->dtd_u.dtu_members, dmd);
 
   fp->ctf_flags |= LCTF_DIRTY;
   return 0;
@@ -1411,41 +1420,6 @@  membcmp (const char *name, ctf_id_t type _libctf_unused_, unsigned long offset,
   return 0;
 }
 
-static int
-membadd (const char *name, ctf_id_t type, unsigned long offset, void *arg)
-{
-  ctf_bundle_t *ctb = arg;
-  ctf_dmdef_t *dmd;
-  char *s = NULL;
-
-  if ((dmd = malloc (sizeof (ctf_dmdef_t))) == NULL)
-    return (ctf_set_errno (ctb->ctb_dict, EAGAIN));
-
-  /* Unnamed members in non-dynamic dicts have a name of "", while dynamic dicts
-     use NULL.  Adapt.  */
-
-  if (name[0] == 0)
-    name = NULL;
-
-  if (name != NULL && (s = strdup (name)) == NULL)
-    {
-      free (dmd);
-      return (ctf_set_errno (ctb->ctb_dict, EAGAIN));
-    }
-
-  /* For now, dmd_type is copied as the src_fp's type; it is reset to an
-    equivalent dst_fp type by a final loop in ctf_add_type(), below.  */
-  dmd->dmd_name = s;
-  dmd->dmd_type = type;
-  dmd->dmd_offset = offset;
-  dmd->dmd_value = -1;
-
-  ctf_list_append (&ctb->ctb_dtd->dtd_u.dtu_members, dmd);
-
-  ctb->ctb_dict->ctf_flags |= LCTF_DIRTY;
-  return 0;
-}
-
 /* Record the correspondence between a source and ctf_add_type()-added
    destination type: both types are translated into parent type IDs if need be,
    so they relate to the actual dictionary they are in.  Outside controlled
@@ -1828,11 +1802,10 @@  ctf_add_type_internal (ctf_dict_t *dst_fp, ctf_dict_t *src_fp, ctf_id_t src_type
     case CTF_K_STRUCT:
     case CTF_K_UNION:
       {
-	ctf_dmdef_t *dmd;
-	int errs = 0;
-	size_t size;
-	ssize_t ssize;
-	ctf_dtdef_t *dtd;
+	ctf_next_t *i = NULL;
+	ssize_t offset;
+	const char *membname;
+	ctf_id_t src_membtype;
 
 	/* Technically to match a struct or union we need to check both
 	   ways (src members vs. dst, dst members vs. src) but we make
@@ -1867,67 +1840,44 @@  ctf_add_type_internal (ctf_dict_t *dst_fp, ctf_dict_t *src_fp, ctf_id_t src_type
 	    break;
 	  }
 
-	/* Unlike the other cases, copying structs and unions is done
-	   manually so as to avoid repeated lookups in ctf_add_member
-	   and to ensure the exact same member offsets as in src_type.  */
-
-	dst_type = ctf_add_generic (dst_fp, flag, name, kind, 0, &dtd);
+	dst_type = ctf_add_struct_sized (dst_fp, flag, name,
+					 ctf_type_size (src_fp, src_type));
 	if (dst_type == CTF_ERR)
 	  return CTF_ERR;			/* errno is set for us.  */
 
-	dst.ctb_type = dst_type;
-	dst.ctb_dtd = dtd;
-
 	/* Pre-emptively add this struct to the type mapping so that
 	   structures that refer to themselves work.  */
 	ctf_add_type_mapping (src_fp, src_type, dst_fp, dst_type);
 
-	if (ctf_member_iter (src_fp, src_type, membadd, &dst) != 0)
-	  errs++;	       /* Increment errs and fail at bottom of case.  */
-
-	if ((ssize = ctf_type_size (src_fp, src_type)) < 0)
-	  return CTF_ERR;			/* errno is set for us.  */
-
-	size = (size_t) ssize;
-	if (size > CTF_MAX_SIZE)
-	  {
-	    dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
-	    dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (size);
-	    dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (size);
-	  }
-	else
-	  dtd->dtd_data.ctt_size = (uint32_t) size;
-
-	dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, flag, vlen);
-
-	/* Make a final pass through the members changing each dmd_type (a
-	   src_fp type) to an equivalent type in dst_fp.  We pass through all
-	   members, leaving any that fail set to CTF_ERR, unless they fail
-	   because they are marking a member of type not representable in this
-	   version of CTF, in which case we just want to silently omit them:
-	   no consumer can do anything with them anyway.  */
-	for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-	     dmd != NULL; dmd = ctf_list_next (dmd))
+	while ((offset = ctf_member_next (src_fp, src_type, &i, &membname,
+					  &src_membtype, 0)) >= 0)
 	  {
 	    ctf_dict_t *dst = dst_fp;
-	    ctf_id_t memb_type;
+	    ctf_id_t dst_membtype = ctf_type_mapping (src_fp, src_membtype, &dst);
 
-	    memb_type = ctf_type_mapping (src_fp, dmd->dmd_type, &dst);
-	    if (memb_type == 0)
+	    if (dst_membtype == 0)
 	      {
-		if ((dmd->dmd_type =
-		     ctf_add_type_internal (dst_fp, src_fp, dmd->dmd_type,
-					    proc_tracking_fp)) == CTF_ERR)
+		dst_membtype = ctf_add_type_internal (dst_fp, src_fp,
+						      src_membtype,
+						      proc_tracking_fp);
+		if (dst_membtype == CTF_ERR)
 		  {
 		    if (ctf_errno (dst_fp) != ECTF_NONREPRESENTABLE)
-		      errs++;
+		      {
+			ctf_next_destroy (i);
+			break;
+		      }
 		  }
 	      }
-	    else
-	      dmd->dmd_type = memb_type;
-	  }
 
-	if (errs)
+	    if (ctf_add_member_offset (dst_fp, dst_type, membname,
+				       dst_membtype, offset) < 0)
+	      {
+		ctf_next_destroy (i);
+		break;
+	      }
+	  }
+	if (ctf_errno (src_fp) != ECTF_NEXT_END)
 	  return CTF_ERR;			/* errno is set for us.  */
 	break;
       }
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 9a82d953ae8..87dd03b78fc 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -179,15 +179,6 @@  typedef struct ctf_decl
   int cd_enomem;		     /* Nonzero if OOM during printing.  */
 } ctf_decl_t;
 
-typedef struct ctf_dmdef
-{
-  ctf_list_t dmd_list;		/* List forward/back pointers.  */
-  char *dmd_name;		/* Name of this member.  */
-  ctf_id_t dmd_type;		/* Type of this member (for sou).  */
-  unsigned long dmd_offset;	/* Offset of this member in bits (for sou).  */
-  int dmd_value;		/* Value of this member (for enum).  */
-} ctf_dmdef_t;
-
 typedef struct ctf_dtdef
 {
   ctf_list_t dtd_list;		/* List forward/back pointers.  */
@@ -195,10 +186,6 @@  typedef struct ctf_dtdef
   ctf_type_t dtd_data;		/* Type node, including name.  */
   size_t dtd_vlen_alloc;	/* Total vlen space allocated.  */
   unsigned char *dtd_vlen;	/* Variable-length data for this type.  */
-  union
-  {
-    ctf_list_t dtu_members;	/* struct, union, or enum */
-  } dtd_u;
 } ctf_dtdef_t;
 
 typedef struct ctf_dvdef
@@ -557,7 +544,6 @@  struct ctf_next
   {
     const ctf_member_t *ctn_mp;
     const ctf_lmember_t *ctn_lmp;
-    const ctf_dmdef_t *ctn_dmd;
     const ctf_enum_t *ctn_en;
     const ctf_dvdef_t *ctn_dvd;
     ctf_next_hkv_t *ctn_sorted_hkv;
diff --git a/libctf/ctf-serialize.c b/libctf/ctf-serialize.c
index 5d94f44b0d0..0811b7b6efe 100644
--- a/libctf/ctf-serialize.c
+++ b/libctf/ctf-serialize.c
@@ -719,57 +719,6 @@  symerr:
 
 /* Type section.  */
 
-static unsigned char *
-ctf_copy_smembers (ctf_dict_t *fp, ctf_dtdef_t *dtd, unsigned char *t)
-{
-  ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-  ctf_member_t ctm;
-
-  for (; dmd != NULL; dmd = ctf_list_next (dmd))
-    {
-      ctf_member_t *copied;
-
-      ctm.ctm_name = 0;
-      ctm.ctm_type = (uint32_t) dmd->dmd_type;
-      ctm.ctm_offset = (uint32_t) dmd->dmd_offset;
-
-      memcpy (t, &ctm, sizeof (ctm));
-      copied = (ctf_member_t *) t;
-      if (dmd->dmd_name)
-	ctf_str_add_ref (fp, dmd->dmd_name, &copied->ctm_name);
-
-      t += sizeof (ctm);
-    }
-
-  return t;
-}
-
-static unsigned char *
-ctf_copy_lmembers (ctf_dict_t *fp, ctf_dtdef_t *dtd, unsigned char *t)
-{
-  ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-  ctf_lmember_t ctlm;
-
-  for (; dmd != NULL; dmd = ctf_list_next (dmd))
-    {
-      ctf_lmember_t *copied;
-
-      ctlm.ctlm_name = 0;
-      ctlm.ctlm_type = (uint32_t) dmd->dmd_type;
-      ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (dmd->dmd_offset);
-      ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (dmd->dmd_offset);
-
-      memcpy (t, &ctlm, sizeof (ctlm));
-      copied = (ctf_lmember_t *) t;
-      if (dmd->dmd_name)
-	ctf_str_add_ref (fp, dmd->dmd_name, &copied->ctlm_name);
-
-      t += sizeof (ctlm);
-    }
-
-  return t;
-}
-
 /* Iterate through the dynamic type definition list and compute the
    size of the CTF type section.  */
 
@@ -784,8 +733,20 @@  ctf_type_sect_size (ctf_dict_t *fp)
     {
       uint32_t kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
       uint32_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
+      size_t type_ctt_size = dtd->dtd_data.ctt_size;
+
+      /* Shrink ctf_type_t-using types from a ctf_type_t to a ctf_stype_t
+	 if possible.  */
+
+      if (kind == CTF_K_STRUCT || kind == CTF_K_UNION)
+	{
+	  size_t lsize = CTF_TYPE_LSIZE (&dtd->dtd_data);
+
+	  if (lsize <= CTF_MAX_SIZE)
+	    type_ctt_size = lsize;
+	}
 
-      if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+      if (type_ctt_size != CTF_LSIZE_SENT)
 	type_size += sizeof (ctf_stype_t);
       else
 	type_size += sizeof (ctf_type_t);
@@ -807,7 +768,7 @@  ctf_type_sect_size (ctf_dict_t *fp)
 	  break;
 	case CTF_K_STRUCT:
 	case CTF_K_UNION:
-	  if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
+	  if (type_ctt_size < CTF_LSTRUCT_THRESH)
 	    type_size += sizeof (ctf_member_t) * vlen;
 	  else
 	    type_size += sizeof (ctf_lmember_t) * vlen;
@@ -836,13 +797,24 @@  ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr)
     {
       uint32_t kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
       uint32_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
-
+      size_t type_ctt_size = dtd->dtd_data.ctt_size;
       size_t len;
       ctf_stype_t *copied;
       const char *name;
       size_t i;
 
-      if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+      /* Shrink ctf_type_t-using types from a ctf_type_t to a ctf_stype_t
+	 if possible.  */
+
+      if (kind == CTF_K_STRUCT || kind == CTF_K_UNION)
+	{
+	  size_t lsize = CTF_TYPE_LSIZE (&dtd->dtd_data);
+
+	  if (lsize <= CTF_MAX_SIZE)
+	    type_ctt_size = lsize;
+	}
+
+      if (type_ctt_size != CTF_LSIZE_SENT)
 	len = sizeof (ctf_stype_t);
       else
 	len = sizeof (ctf_type_t);
@@ -855,6 +827,7 @@  ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr)
 	  ctf_str_add_ref (fp, name, &copied->ctt_name);
 	  ctf_str_add_ref (fp, name, &dtd->dtd_data.ctt_name);
 	}
+      copied->ctt_size = type_ctt_size;
       t += len;
 
       switch (kind)
@@ -880,12 +853,40 @@  ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr)
 	  t += sizeof (uint32_t) * (vlen + (vlen & 1));
 	  break;
 
+	  /* These need to be copied across element by element, depending on
+	     their ctt_size.  */
 	case CTF_K_STRUCT:
 	case CTF_K_UNION:
-	  if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
-	    t = ctf_copy_smembers (fp, dtd, t);
+	  {
+	    ctf_lmember_t *dtd_vlen = (ctf_lmember_t *) dtd->dtd_vlen;
+	    ctf_lmember_t *t_lvlen = (ctf_lmember_t *) t;
+	    ctf_member_t *t_vlen = (ctf_member_t *) t;
+
+	    for (i = 0; i < vlen; i++)
+	      {
+		const char *name = ctf_strraw (fp, dtd_vlen[i].ctlm_name);
+
+		ctf_str_add_ref (fp, name, &dtd_vlen[i].ctlm_name);
+
+		if (type_ctt_size < CTF_LSTRUCT_THRESH)
+		  {
+		    t_vlen[i].ctm_name = dtd_vlen[i].ctlm_name;
+		    t_vlen[i].ctm_type = dtd_vlen[i].ctlm_type;
+		    t_vlen[i].ctm_offset = CTF_LMEM_OFFSET (&dtd_vlen[i]);
+		    ctf_str_add_ref (fp, name, &t_vlen[i].ctm_name);
+		  }
+		else
+		  {
+		    t_lvlen[i] = dtd_vlen[i];
+		    ctf_str_add_ref (fp, name, &t_lvlen[i].ctlm_name);
+		  }
+	      }
+	  }
+
+	  if (type_ctt_size < CTF_LSTRUCT_THRESH)
+	    t += sizeof (ctf_member_t) * vlen;
 	  else
-	    t = ctf_copy_lmembers (fp, dtd, t);
+	    t += sizeof (ctf_lmember_t) * vlen;
 	  break;
 
 	case CTF_K_ENUM:
diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c
index 49264ebf5ab..df7673ecd2e 100644
--- a/libctf/ctf-types.c
+++ b/libctf/ctf-types.c
@@ -101,24 +101,17 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
 
       dtd = ctf_dynamic_type (fp, type);
       i->ctn_iter_fun = (void (*) (void)) ctf_member_next;
-
-      /* We depend below on the RDWR state indicating whether the DTD-related
-	 fields or the DMD-related fields have been initialized.  */
-
-      assert ((dtd && (fp->ctf_flags & LCTF_RDWR))
-	      || (!dtd && (!(fp->ctf_flags & LCTF_RDWR))));
+      i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
 
       if (dtd == NULL)
 	{
-	  i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
-
 	  if (i->ctn_size < CTF_LSTRUCT_THRESH)
 	    i->u.ctn_mp = (const ctf_member_t *) ((uintptr_t) tp + increment);
 	  else
 	    i->u.ctn_lmp = (const ctf_lmember_t *) ((uintptr_t) tp + increment);
 	}
       else
-	i->u.ctn_dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
+	i->u.ctn_lmp = (const ctf_lmember_t *) dtd->dtd_vlen;
 
       *it = i;
     }
@@ -141,71 +134,46 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
  retry:
   if (!i->ctn_type)
     {
-      if (!(fp->ctf_flags & LCTF_RDWR))
-	{
-	  if (i->ctn_n == 0)
-	    goto end_iter;
-
-	  if (i->ctn_size < CTF_LSTRUCT_THRESH)
-	    {
-	      const char *membname = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
-
-	      if (name)
-		*name = membname;
-	      if (membtype)
-		*membtype = i->u.ctn_mp->ctm_type;
-	      offset = i->u.ctn_mp->ctm_offset;
-
-	      if (membname[0] == 0
-		  && (ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_STRUCT
-		      || ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_UNION))
-		i->ctn_type = i->u.ctn_mp->ctm_type;
+      if (i->ctn_n == 0)
+	goto end_iter;
 
-	      i->u.ctn_mp++;
-	    }
-	  else
-	    {
-	      const char *membname = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+      /* Dynamic structures in read-write dicts always use lmembers.  */
+      if (i->ctn_size < CTF_LSTRUCT_THRESH
+	  && !(fp->ctf_flags & LCTF_RDWR))
+	{
+	  const char *membname = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
 
-	      if (name)
-		*name = membname;
-	      if (membtype)
-		*membtype = i->u.ctn_lmp->ctlm_type;
-	      offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
+	  if (name)
+	    *name = membname;
+	  if (membtype)
+	    *membtype = i->u.ctn_mp->ctm_type;
+	  offset = i->u.ctn_mp->ctm_offset;
 
-	      if (membname[0] == 0
-		  && (ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_STRUCT
-		      || ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_UNION))
-		i->ctn_type = i->u.ctn_lmp->ctlm_type;
+	  if (membname[0] == 0
+	      && (ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_STRUCT
+		  || ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_UNION))
+	    i->ctn_type = i->u.ctn_mp->ctm_type;
 
-	      i->u.ctn_lmp++;
-	    }
-	  i->ctn_n--;
+	  i->u.ctn_mp++;
 	}
       else
 	{
-	  if (i->u.ctn_dmd == NULL)
-	    goto end_iter;
-	  /* The dmd contains a NULL for unnamed dynamic members.  Don't inflict
-	     this on our callers.  */
+	  const char *membname = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+
 	  if (name)
-	    {
-	      if (i->u.ctn_dmd->dmd_name)
-		*name = i->u.ctn_dmd->dmd_name;
-	      else
-		*name = "";
-	    }
+	    *name = membname;
 	  if (membtype)
-	    *membtype = i->u.ctn_dmd->dmd_type;
-	  offset = i->u.ctn_dmd->dmd_offset;
+	    *membtype = i->u.ctn_lmp->ctlm_type;
+	  offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
 
-	  if (i->u.ctn_dmd->dmd_name == NULL
-	      && (ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_STRUCT
-		  || ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_UNION))
-	    i->ctn_type = i->u.ctn_dmd->dmd_type;
+	  if (membname[0] == 0
+	      && (ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_STRUCT
+		  || ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_UNION))
+	    i->ctn_type = i->u.ctn_lmp->ctlm_type;
 
-	  i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
+	  i->u.ctn_lmp++;
 	}
+      i->ctn_n--;
 
       /* The callers might want automatic recursive sub-struct traversal.  */
       if (!(flags & CTF_MN_RECURSE))
@@ -996,53 +964,44 @@  ctf_type_align (ctf_dict_t *fp, ctf_id_t type)
     case CTF_K_UNION:
       {
 	size_t align = 0;
+	int dynamic = 0;
 	ctf_dtdef_t *dtd;
 
-	if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
-	  {
-	    uint32_t n = LCTF_INFO_VLEN (fp, tp->ctt_info);
-	    ssize_t size, increment;
-	    const void *vmp;
+	if ((dtd = ctf_dynamic_type (ofp, type)) != NULL)
+	  dynamic = 1;
 
-	    (void) ctf_get_ctt_size (fp, tp, &size, &increment);
-	    vmp = (unsigned char *) tp + increment;
+	uint32_t n = LCTF_INFO_VLEN (fp, tp->ctt_info);
+	ssize_t size, increment;
+	const void *vmp;
 
-	    if (kind == CTF_K_STRUCT)
-	      n = MIN (n, 1);	/* Only use first member for structs.  */
+	(void) ctf_get_ctt_size (fp, tp, &size, &increment);
 
-	    if (size < CTF_LSTRUCT_THRESH)
-	      {
-		const ctf_member_t *mp = vmp;
-		for (; n != 0; n--, mp++)
-		  {
-		    ssize_t am = ctf_type_align (ofp, mp->ctm_type);
-		    align = MAX (align, (size_t) am);
-		  }
-	      }
-	    else
+	if (!dynamic)
+	  vmp = (unsigned char *) tp + increment;
+	else
+	  vmp = dtd->dtd_vlen;
+
+	if (kind == CTF_K_STRUCT)
+	  n = MIN (n, 1);	/* Only use first member for structs.  */
+
+	if (size < CTF_LSTRUCT_THRESH && !dynamic)
+	  {
+	    const ctf_member_t *mp = vmp;
+	    for (; n != 0; n--, mp++)
 	      {
-		const ctf_lmember_t *lmp = vmp;
-		for (; n != 0; n--, lmp++)
-		  {
-		    ssize_t am = ctf_type_align (ofp, lmp->ctlm_type);
-		    align = MAX (align, (size_t) am);
-		  }
+		ssize_t am = ctf_type_align (ofp, mp->ctm_type);
+		align = MAX (align, (size_t) am);
 	      }
 	  }
 	else
 	  {
-	      ctf_dmdef_t *dmd;
-
-	      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-		   dmd != NULL; dmd = ctf_list_next (dmd))
-		{
-		  ssize_t am = ctf_type_align (ofp, dmd->dmd_type);
-		  align = MAX (align, (size_t) am);
-		  if (kind == CTF_K_STRUCT)
-		    break;
-		}
+	    const ctf_lmember_t *lmp = vmp;
+	    for (; n != 0; n--, lmp++)
+	      {
+		ssize_t am = ctf_type_align (ofp, lmp->ctlm_type);
+		align = MAX (align, (size_t) am);
+	      }
 	  }
-
 	return align;
       }
 
@@ -1390,8 +1349,10 @@  ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
   ctf_dict_t *ofp = fp;
   const ctf_type_t *tp;
   ctf_dtdef_t *dtd;
+  const void *vmp;
   ssize_t size, increment;
   uint32_t kind, n;
+  int dynamic = 0;
 
   if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
     return -1;			/* errno is set for us.  */
@@ -1405,73 +1366,54 @@  ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
     return (ctf_set_errno (ofp, ECTF_NOTSOU));
 
-  if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
-    {
-      if (size < CTF_LSTRUCT_THRESH)
-	{
-	  const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp +
-							   increment);
+  if ((dtd = ctf_dynamic_type (ofp, type)) != NULL)
+    dynamic = 1;
 
-	  for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
-	    {
-	      const char *membname = ctf_strptr (fp, mp->ctm_name);
+  if (!dynamic)
+    vmp = (unsigned char *) tp + increment;
+  else
+    vmp = dtd->dtd_vlen;
 
-	      if (membname[0] == 0
-		  && (ctf_type_kind (fp, mp->ctm_type) == CTF_K_STRUCT
-		      || ctf_type_kind (fp, mp->ctm_type) == CTF_K_UNION)
-		  && (ctf_member_info (fp, mp->ctm_type, name, mip) == 0))
-		return 0;
+  if (size < CTF_LSTRUCT_THRESH && !dynamic)
+    {
+      const ctf_member_t *mp = vmp;
 
-	      if (strcmp (membname, name) == 0)
-		{
-		  mip->ctm_type = mp->ctm_type;
-		  mip->ctm_offset = mp->ctm_offset;
-		  return 0;
-		}
-	    }
-	}
-      else
+      for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
 	{
-	  const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
-							      increment);
+	  const char *membname = ctf_strptr (fp, mp->ctm_name);
 
-	  for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
-	    {
-	      const char *membname = ctf_strptr (fp, lmp->ctlm_name);
-
-	      if (membname[0] == 0
-		  && (ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_STRUCT
-		      || ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_UNION)
-		  && (ctf_member_info (fp, lmp->ctlm_type, name, mip) == 0))
-		return 0;
+	  if (membname[0] == 0
+	      && (ctf_type_kind (fp, mp->ctm_type) == CTF_K_STRUCT
+		  || ctf_type_kind (fp, mp->ctm_type) == CTF_K_UNION)
+	      && (ctf_member_info (fp, mp->ctm_type, name, mip) == 0))
+	    return 0;
 
-	      if (strcmp (membname, name) == 0)
-		{
-		  mip->ctm_type = lmp->ctlm_type;
-		  mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (lmp);
-		  return 0;
-		}
+	  if (strcmp (membname, name) == 0)
+	    {
+	      mip->ctm_type = mp->ctm_type;
+	      mip->ctm_offset = mp->ctm_offset;
+	      return 0;
 	    }
 	}
     }
   else
     {
-      ctf_dmdef_t *dmd;
+      const ctf_lmember_t *lmp = vmp;
 
-      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-	   dmd != NULL; dmd = ctf_list_next (dmd))
+      for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
 	{
-	  if (dmd->dmd_name == NULL
-	      && (ctf_type_kind (fp, dmd->dmd_type) == CTF_K_STRUCT
-		  || ctf_type_kind (fp, dmd->dmd_type) == CTF_K_UNION)
-	      && (ctf_member_info (fp, dmd->dmd_type, name, mip) == 0))
+	  const char *membname = ctf_strptr (fp, lmp->ctlm_name);
+
+	  if (membname[0] == 0
+	      && (ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_STRUCT
+		  || ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_UNION)
+	      && (ctf_member_info (fp, lmp->ctlm_type, name, mip) == 0))
 	    return 0;
 
-	  if (dmd->dmd_name != NULL
-	      && strcmp (dmd->dmd_name, name) == 0)
+	  if (strcmp (membname, name) == 0)
 	    {
-	      mip->ctm_type = dmd->dmd_type;
-	      mip->ctm_offset = dmd->dmd_offset;
+	      mip->ctm_type = lmp->ctlm_type;
+	      mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (lmp);
 	      return 0;
 	    }
 	}
@@ -1688,8 +1630,10 @@  ctf_type_rvisit (ctf_dict_t *fp, ctf_id_t type, ctf_visit_f *func,
   ctf_id_t otype = type;
   const ctf_type_t *tp;
   const ctf_dtdef_t *dtd;
+  const void *vmp;
   ssize_t size, increment;
   uint32_t kind, n;
+  int dynamic = 0;
   int rc;
 
   if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
@@ -1708,48 +1652,38 @@  ctf_type_rvisit (ctf_dict_t *fp, ctf_id_t type, ctf_visit_f *func,
 
   (void) ctf_get_ctt_size (fp, tp, &size, &increment);
 
-  if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
+  if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
+    dynamic = 1;
+
+  if (!dynamic)
+    vmp = (unsigned char *) tp + increment;
+  else
+    vmp = dtd->dtd_vlen;
+
+  if (size < CTF_LSTRUCT_THRESH && !dynamic)
     {
-      if (size < CTF_LSTRUCT_THRESH)
-	{
-	  const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp +
-							   increment);
+      const ctf_member_t *mp = vmp;
 
-	  for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
-	    {
-	      if ((rc = ctf_type_rvisit (fp, mp->ctm_type,
-					 func, arg, ctf_strptr (fp,
-								mp->ctm_name),
-					 offset + mp->ctm_offset,
-					 depth + 1)) != 0)
-		return rc;
-	    }
-	}
-      else
+      for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
 	{
-	  const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
-							      increment);
-
-	  for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
-	    {
-	      if ((rc = ctf_type_rvisit (fp, lmp->ctlm_type,
-					 func, arg, ctf_strptr (fp,
-								lmp->ctlm_name),
-					 offset + (unsigned long) CTF_LMEM_OFFSET (lmp),
-					 depth + 1)) != 0)
-		return rc;
-	    }
+	  if ((rc = ctf_type_rvisit (fp, mp->ctm_type,
+				     func, arg, ctf_strptr (fp,
+							    mp->ctm_name),
+				     offset + mp->ctm_offset,
+				     depth + 1)) != 0)
+	    return rc;
 	}
     }
   else
     {
-      ctf_dmdef_t *dmd;
+      const ctf_lmember_t *lmp = vmp;
 
-      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-	   dmd != NULL; dmd = ctf_list_next (dmd))
+      for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
 	{
-	  if ((rc = ctf_type_rvisit (fp, dmd->dmd_type, func, arg,
-				     dmd->dmd_name, dmd->dmd_offset,
+	  if ((rc = ctf_type_rvisit (fp, lmp->ctlm_type,
+				     func, arg, ctf_strptr (fp,
+							    lmp->ctlm_name),
+				     offset + (unsigned long) CTF_LMEM_OFFSET (lmp),
 				     depth + 1)) != 0)
 	    return rc;
 	}
diff --git a/libctf/testsuite/libctf-regression/type-add-unnamed-struct-ctf.c b/libctf/testsuite/libctf-regression/type-add-unnamed-struct-ctf.c
index d319aafbac1..a1573a70059 100644
--- a/libctf/testsuite/libctf-regression/type-add-unnamed-struct-ctf.c
+++ b/libctf/testsuite/libctf-regression/type-add-unnamed-struct-ctf.c
@@ -12,6 +12,7 @@  struct foo
     struct
     {
       int baz;
+      struct foo *foo;
     };
   };
 };
diff --git a/libctf/testsuite/libctf-regression/type-add-unnamed-struct.c b/libctf/testsuite/libctf-regression/type-add-unnamed-struct.c
index 43c3934add4..16ff0948b17 100644
--- a/libctf/testsuite/libctf-regression/type-add-unnamed-struct.c
+++ b/libctf/testsuite/libctf-regression/type-add-unnamed-struct.c
@@ -13,7 +13,7 @@  main (int argc, char *argv[])
   ctf_id_t newtype;
   const char *memb;
   ctf_membinfo_t mi;
-  const char *membs[] = { "bar", "baz", NULL };
+  const char *membs[] = { "bar", "baz", "foo", NULL };
   const char **walk;
   int err;
 
diff --git a/libctf/testsuite/libctf-regression/type-add-unnamed-struct.lk b/libctf/testsuite/libctf-regression/type-add-unnamed-struct.lk
index caa8934ed2e..b818a2400a1 100644
--- a/libctf/testsuite/libctf-regression/type-add-unnamed-struct.lk
+++ b/libctf/testsuite/libctf-regression/type-add-unnamed-struct.lk
@@ -1,3 +1,4 @@ 
 # source: type-add-unnamed-struct-ctf.c
 Looked up bar, type [0-9a-f]*, offset [0-9a-f]*
 Looked up baz, type [0-9a-f]*, offset [0-9a-f]*
+Looked up foo, type [0-9a-f]*, offset [0-9a-f]*