dt-relr.exp --no-as-needed
[binutils-gdb.git] / libctf / ctf-create.c
index bc46cfa6ca80ea4ec2b62b79b190e6965c417563..e0fce13afc1bc1586ebb8149d01fa89337b11cf1 100644 (file)
@@ -1,5 +1,5 @@
 /* CTF dict creation.
-   Copyright (C) 2019-2021 Free Software Foundation, Inc.
+   Copyright (C) 2019-2022 Free Software Foundation, Inc.
 
    This file is part of libctf.
 
 #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%
@@ -64,6 +70,30 @@ ctf_grow_ptrtab (ctf_dict_t *fp)
   return 0;
 }
 
+/* Make sure a vlen has enough space: expand it otherwise.  Unlike the ptrtab,
+   which grows quite slowly, the vlen grows in big jumps because it is quite
+   expensive to expand: the caller has to scan the old vlen for string refs
+   first and remove them, then re-add them afterwards.  The initial size is
+   more or less arbitrary.  */
+static int
+ctf_grow_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vlen)
+{
+  unsigned char *old = dtd->dtd_vlen;
+
+  if (dtd->dtd_vlen_alloc > vlen)
+    return 0;
+
+  if ((dtd->dtd_vlen = realloc (dtd->dtd_vlen,
+                               dtd->dtd_vlen_alloc * 2)) == NULL)
+    {
+      dtd->dtd_vlen = old;
+      return (ctf_set_errno (fp, ENOMEM));
+    }
+  memset (dtd->dtd_vlen + dtd->dtd_vlen_alloc, 0, dtd->dtd_vlen_alloc);
+  dtd->dtd_vlen_alloc *= 2;
+  return 0;
+}
+
 /* To create an empty CTF dict, we just declare a zeroed header and call
    ctf_bufopen() on it.  If ctf_bufopen succeeds, we mark the new dict r/w and
    initialize the dynamic members.  We start assigning type IDs at 1 because
@@ -220,35 +250,42 @@ 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;
   const char *name;
 
   ctf_dynhash_remove (fp->ctf_dthash, (void *) (uintptr_t) dtd->dtd_type);
-  free (dtd->dtd_vlen);
 
   switch (kind)
     {
     case CTF_K_STRUCT:
     case CTF_K_UNION:
-    case CTF_K_ENUM:
-      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_FUNCTION:
-      free (dtd->dtd_u.dtu_argv);
+    case CTF_K_ENUM:
+      {
+       ctf_enum_t *en = (ctf_enum_t *) dtd->dtd_vlen;
+       size_t i;
+
+       for (i = 0; i < vlen; i++)
+         ctf_str_remove_ref (fp, ctf_strraw (fp, en[i].cte_name),
+                             &en[i].cte_name);
+      }
       break;
     case CTF_K_FORWARD:
       name_kind = dtd->dtd_data.ctt_type;
       break;
     }
+  free (dtd->dtd_vlen);
+  dtd->dtd_vlen_alloc = 0;
 
   if (dtd->dtd_data.ctt_name
       && (name = ctf_strraw (fp, dtd->dtd_data.ctt_name)) != NULL
@@ -405,6 +442,9 @@ ctf_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
   return 0;
 }
 
+/* Note: vlen is the amount of space *allocated* for the vlen.  It may well not
+   be the amount of space used (yet): the space used is declared in per-kind
+   fashion in the dtd_data's info word.  */
 static ctf_id_t
 ctf_add_generic (ctf_dict_t *fp, uint32_t flag, const char *name, int kind,
                 size_t vlen, ctf_dtdef_t **rp)
@@ -431,6 +471,7 @@ ctf_add_generic (ctf_dict_t *fp, uint32_t flag, const char *name, int kind,
   if ((dtd = calloc (1, sizeof (ctf_dtdef_t))) == NULL)
     return (ctf_set_errno (fp, EAGAIN));
 
+  dtd->dtd_vlen_alloc = vlen;
   if (vlen > 0)
     {
       if ((dtd->dtd_vlen = calloc (1, vlen)) == NULL)
@@ -442,7 +483,8 @@ ctf_add_generic (ctf_dict_t *fp, uint32_t flag, const char *name, int kind,
   type = ++fp->ctf_typemax;
   type = LCTF_INDEX_TO_TYPE (fp, type, (fp->ctf_flags & LCTF_CHILD));
 
-  dtd->dtd_data.ctt_name = ctf_str_add_ref (fp, name, &dtd->dtd_data.ctt_name);
+  dtd->dtd_data.ctt_name = ctf_str_add_pending (fp, name,
+                                               &dtd->dtd_data.ctt_name);
   dtd->dtd_type = type;
 
   if (dtd->dtd_data.ctt_name == 0 && name != NULL && name[0] != '\0')
@@ -703,8 +745,9 @@ ctf_add_function (ctf_dict_t *fp, uint32_t flag,
   ctf_dtdef_t *dtd;
   ctf_id_t type;
   uint32_t vlen;
-  uint32_t *vdat = NULL;
+  uint32_t *vdat;
   ctf_dict_t *tmp = fp;
+  size_t initial_vlen;
   size_t i;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
@@ -720,38 +763,35 @@ ctf_add_function (ctf_dict_t *fp, uint32_t flag,
 
   if (ctc->ctc_return != 0
       && ctf_lookup_by_id (&tmp, ctc->ctc_return) == NULL)
-    return CTF_ERR;            /* errno is set for us.  */
+    return CTF_ERR;                            /* errno is set for us.  */
 
   if (vlen > CTF_MAX_VLEN)
     return (ctf_set_errno (fp, EOVERFLOW));
 
-  if (vlen != 0 && (vdat = malloc (sizeof (ctf_id_t) * vlen)) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+  /* One word extra allocated for padding for 4-byte alignment if need be.
+     Not reflected in vlen: we don't want to copy anything into it, and
+     it's in addition to (e.g.) the trailing 0 indicating varargs.  */
+
+  initial_vlen = (sizeof (uint32_t) * (vlen + (vlen & 1)));
+  if ((type = ctf_add_generic (fp, flag, NULL, CTF_K_FUNCTION,
+                              initial_vlen, &dtd)) == CTF_ERR)
+    return CTF_ERR;                            /* errno is set for us.  */
+
+  vdat = (uint32_t *) dtd->dtd_vlen;
 
   for (i = 0; i < ctc->ctc_argc; i++)
     {
       tmp = fp;
       if (argv[i] != 0 && ctf_lookup_by_id (&tmp, argv[i]) == NULL)
-       {
-         free (vdat);
-         return CTF_ERR;          /* errno is set for us.  */
-       }
+       return CTF_ERR;                         /* errno is set for us.  */
       vdat[i] = (uint32_t) argv[i];
     }
 
-  if ((type = ctf_add_generic (fp, flag, NULL, CTF_K_FUNCTION,
-                              0, &dtd)) == CTF_ERR)
-    {
-      free (vdat);
-      return CTF_ERR;             /* errno is set for us.  */
-    }
-
   dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen);
   dtd->dtd_data.ctt_type = (uint32_t) ctc->ctc_return;
 
   if (ctc->ctc_flags & CTF_FUNC_VARARG)
     vdat[vlen - 1] = 0;                   /* Add trailing zero to indicate varargs.  */
-  dtd->dtd_u.dtu_argv = vdat;
 
   return type;
 }
@@ -762,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)
@@ -770,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;
 }
@@ -799,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)
@@ -807,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;
 }
@@ -835,6 +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) * INITIAL_VLEN;
 
   /* Promote root-visible forwards to enums.  */
   if (name != NULL)
@@ -843,9 +890,17 @@ ctf_add_enum (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_ENUM,
-                                   0, &dtd)) == CTF_ERR)
+                                   initial_vlen, &dtd)) == CTF_ERR)
     return CTF_ERR;            /* errno is set for us.  */
 
+  /* Forwards won't have any vlen yet.  */
+  if (dtd->dtd_vlen_alloc == 0)
+    {
+      if ((dtd->dtd_vlen = calloc (1, initial_vlen)) == NULL)
+       return (ctf_set_errno (fp, ENOMEM));
+      dtd->dtd_vlen_alloc = initial_vlen;
+    }
+
   dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_ENUM, flag, 0);
   dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int;
 
@@ -910,6 +965,39 @@ ctf_add_forward (ctf_dict_t *fp, uint32_t flag, const char *name,
   return type;
 }
 
+ctf_id_t
+ctf_add_unknown (ctf_dict_t *fp, uint32_t flag, const char *name)
+{
+  ctf_dtdef_t *dtd;
+  ctf_id_t type = 0;
+
+  /* If a type is already defined with this name, error (if not CTF_K_UNKNOWN)
+     or just return it.  */
+
+  if (name != NULL && name[0] != '\0' && flag == CTF_ADD_ROOT
+      && (type = ctf_lookup_by_rawname (fp, CTF_K_UNKNOWN, name)))
+    {
+      if (ctf_type_kind (fp, type) == CTF_K_UNKNOWN)
+       return type;
+      else
+       {
+         ctf_err_warn (fp, 1, ECTF_CONFLICT,
+                       _("ctf_add_unknown: cannot add unknown type "
+                         "named %s: type of this name already defined"),
+                       name ? name : _("(unnamed type)"));
+         return (ctf_set_errno (fp, ECTF_CONFLICT));
+       }
+    }
+
+  if ((type = ctf_add_generic (fp, flag, name, CTF_K_UNKNOWN, 0, &dtd)) == CTF_ERR)
+    return CTF_ERR;            /* errno is set for us.  */
+
+  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_UNKNOWN, flag, 0);
+  dtd->dtd_data.ctt_type = 0;
+
+  return type;
+}
+
 ctf_id_t
 ctf_add_typedef (ctf_dict_t *fp, uint32_t flag, const char *name,
                 ctf_id_t ref)
@@ -960,10 +1048,11 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
                    int value)
 {
   ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, enid);
-  ctf_dmdef_t *dmd;
+  unsigned char *old_vlen;
+  ctf_enum_t *en;
+  size_t i;
 
   uint32_t kind, vlen, root;
-  char *s;
 
   if (name == NULL)
     return (ctf_set_errno (fp, EINVAL));
@@ -984,29 +1073,32 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
   if (vlen == CTF_MAX_VLEN)
     return (ctf_set_errno (fp, ECTF_DTFULL));
 
-  for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-       dmd != NULL; dmd = ctf_list_next (dmd))
+  old_vlen = dtd->dtd_vlen;
+  if (ctf_grow_vlen (fp, dtd, sizeof (ctf_enum_t) * (vlen + 1)) < 0)
+    return -1;                                 /* errno is set for us.  */
+  en = (ctf_enum_t *) dtd->dtd_vlen;
+
+  if (dtd->dtd_vlen != old_vlen)
     {
-      if (strcmp (dmd->dmd_name, name) == 0)
-       return (ctf_set_errno (fp, ECTF_DUPLICATE));
-    }
+      ptrdiff_t move = (signed char *) dtd->dtd_vlen - (signed char *) old_vlen;
 
-  if ((dmd = malloc (sizeof (ctf_dmdef_t))) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+      /* Remove pending refs in the old vlen region and reapply them.  */
 
-  if ((s = strdup (name)) == NULL)
-    {
-      free (dmd);
-      return (ctf_set_errno (fp, EAGAIN));
+      for (i = 0; i < vlen; i++)
+       ctf_str_move_pending (fp, &en[i].cte_name, move);
     }
 
-  dmd->dmd_name = s;
-  dmd->dmd_type = CTF_ERR;
-  dmd->dmd_offset = 0;
-  dmd->dmd_value = value;
+  for (i = 0; i < vlen; i++)
+    if (strcmp (ctf_strptr (fp, en[i].cte_name), name) == 0)
+      return (ctf_set_errno (fp, ECTF_DUPLICATE));
+
+  en[i].cte_name = ctf_str_add_pending (fp, name, &en[i].cte_name);
+  en[i].cte_value = value;
+
+  if (en[i].cte_name == 0 && name != NULL && name[0] != '\0')
+    return -1;                                 /* errno is set for us. */
 
   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;
 
@@ -1018,12 +1110,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));
@@ -1044,14 +1137,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 ||
@@ -1076,18 +1181,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)
     {
@@ -1095,9 +1192,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;
@@ -1107,10 +1203,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)
            {
@@ -1128,14 +1221,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.  */
            }
 
@@ -1150,36 +1244,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;
@@ -1363,41 +1453,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
@@ -1780,11 +1835,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
@@ -1819,67 +1873,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;
       }