[13/22] libctf: make ctf_serialize() actually serialize

Message ID 20240417202018.34966-14-nick.alcock@oracle.com
State New
Headers
Series more modifiable CTF dicts (and a few bugfixes) |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Nick Alcock April 17, 2024, 8:20 p.m. UTC
  ctf_serialize() evolved from the old ctf_update(), which mutated the
in-memory CTF dict to make all the dynamic in-memory types into static,
unchanging written-to-the-dict types (by deserializing and reserializing
it): back in the days when you could only do type lookups on static types,
this meant you could see all the types you added recently, at the small,
small cost of making it impossible to change those older types ever again
and inducing an amortized O(n^2) cost if you actually wanted to add
references to types you added at arbitrary times to later types.

It also reset things so that ctf_discard() would throw away only types you
added after the most recent ctf_update() call.

Some time ago this was all changed so that you could look up dynamic types
just as easily as static types: ctf_update() changed so that only its
visible side-effect of affecting ctf_discard() remained: the old
ctf_update() was renamed to ctf_serialize(), made internal to libctf, and
called from the various functions that wrote files out.

... but it was still working by serializing and deserializing the entire
dict, swapping out its guts with the newly-serialized copy in an invasive
and horrible fashion that coupled ctf_serialize() to almost every field in
the ctf_dict_t.  This is totally useless, and fixing it is easy: just rip
all that code out and have ctf_serialize return a serialized representation,
and let everything use that directly.  This simplifies most of its callers
significantly.

(It also points up another bug: ctf_gzwrite() failed to call ctf_serialize()
at all, so it would only ever work for a dict you just ctf_write_mem()ed
yourself, just for its invisible side-effect of serializing the dict!)

This lets us simplify away a bunch of internal-only open-side functionality
for overriding the syn_ext_strtab and some just-added functionality for
forcing in an existing atoms table, without loss of functionality, and lets
us lift the restriction on reserializing a dict that was ctf_open()ed rather
than being ctf_create()d: it's now perfectly OK to open a dict, modify it
(except for adding members to existing structs, unions, or enums, which
fails with -ECTF_RDONLY), and write it out again, just as one would expect.

libctf/

	* ctf-serialize.c (ctf_symtypetab_sect_sizes): Fix typos.
	(ctf_type_sect_size): Add static type sizes too.
	(ctf_serialize): Return the new dict rather than updating the
	existing dict.  No longer fail for dicts with static types;
	copy them onto the start of the new types table.
	(ctf_gzwrite): Actually serialize before gzwriting.
	(ctf_write_mem): Improve forced (test-mode) endian-flipping:
	flip dicts even if they are too small to be compressed.
	Improve confusing variable naming.
	* ctf-archive.c (arc_write_one_ctf): Don't bother to call
	ctf_serialize: both the functions we call do so.
	* ctf-string.c (ctf_str_create_atoms): Drop serializing case
	(atoms arg).
	* ctf-open.c (ctf_simple_open): Call ctf_bufopen directly.
	(ctf_simple_open_internal): Delete.
	(ctf_bufopen_internal): Delete/rename to ctf_bufopen: no
	longer bother with syn_ext_strtab or forced atoms table,
	serialization no longer needs them.
	* ctf-create.c (ctf_create): Call ctf_bufopen directly.
	* ctf-impl.h (ctf_str_create_atoms): Drop atoms arg.
	(ctf_simple_open_internal): Delete.
	(ctf_bufopen_internal): Likewise.
	(ctf_serialize): Adjust.
	* testsuite/libctf-lookup/add-to-opened.c: Adjust now that
	this is supposed to work.
---
 libctf/ctf-archive.c                          |   7 +-
 libctf/ctf-create.c                           |   2 +-
 libctf/ctf-impl.h                             |  19 +-
 libctf/ctf-open.c                             |  39 +-
 libctf/ctf-serialize.c                        | 334 ++++++------------
 libctf/ctf-string.c                           |  73 +---
 .../testsuite/libctf-lookup/add-to-opened.c   |  11 +-
 7 files changed, 137 insertions(+), 348 deletions(-)
  

Patch

diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c
index a88c6135e1a..3f12c0da85d 100644
--- a/libctf/ctf-archive.c
+++ b/libctf/ctf-archive.c
@@ -253,12 +253,12 @@  ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
   return err;
 }
 
-/* Write one CTF file out.  Return the file position of the written file (or
+/* Write one CTF dict out.  Return the file position of the written file (or
    rather, of the file-size uint64_t that precedes it): negative return is a
    negative errno or ctf_errno value.  On error, the file position may no longer
    be at the end of the file.  */
 static off_t
-arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
+arc_write_one_ctf (ctf_dict_t *f, int fd, size_t threshold)
 {
   off_t off, end_off;
   uint64_t ctfsz = 0;
@@ -266,9 +266,6 @@  arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
   size_t ctfsz_len;
   int (*writefn) (ctf_dict_t * fp, int fd);
 
-  if (ctf_serialize (f) < 0)
-    return f->ctf_errno * -1;
-
   if ((off = lseek (fd, 0, SEEK_CUR)) < 0)
     return errno * -1;
 
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index 78fb0305c20..ee79e49794d 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -133,7 +133,7 @@  ctf_create (int *errp)
   cts.cts_size = sizeof (hdr);
   cts.cts_entsize = 1;
 
-  if ((fp = ctf_bufopen_internal (&cts, NULL, NULL, NULL, NULL, errp)) == NULL)
+  if ((fp = ctf_bufopen (&cts, NULL, NULL, errp)) == NULL)
     goto err;
 
   /* These hashes will have been initialized with a starting size of zero,
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 3eef232bea0..03e1a66416a 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -364,14 +364,7 @@  typedef struct ctf_dedup
    clients, who see it only as an opaque pointer.  Modifications can therefore
    be made freely to this structure without regard to client versioning.  The
    ctf_dict_t typedef appears in <ctf-api.h> and declares a forward tag.
-   (A ctf_file_t typedef also appears there, for historical reasons.)
-
-   NOTE: ctf_serialize requires that everything inside of ctf_dict either be an
-   immediate value, a pointer to dynamically allocated data *outside* of the
-   ctf_dict itself, a pointer to statically allocated data, or specially handled
-   in ctf_serialize.  If you add a pointer to ctf_dict that points to something
-   within the ctf_dict itself, you must make corresponding changes to
-   ctf_serialize.  */
+   (A ctf_file_t typedef also appears there, for historical reasons.)  */
 
 struct ctf_dict
 {
@@ -735,7 +728,7 @@  extern const char *ctf_strraw (ctf_dict_t *, uint32_t);
 extern const char *ctf_strraw_explicit (ctf_dict_t *, uint32_t,
 					ctf_strs_t *);
 extern const char *ctf_strptr_validate (ctf_dict_t *, uint32_t);
-extern int ctf_str_create_atoms (ctf_dict_t *, ctf_dynhash_t *atoms);
+extern int ctf_str_create_atoms (ctf_dict_t *);
 extern void ctf_str_free_atoms (ctf_dict_t *);
 extern uint32_t ctf_str_add (ctf_dict_t *, const char *);
 extern uint32_t ctf_str_add_ref (ctf_dict_t *, const char *, uint32_t *ref);
@@ -760,15 +753,7 @@  extern void *ctf_set_open_errno (int *, int);
 extern void ctf_flip_header (ctf_header_t *);
 extern int ctf_flip (ctf_dict_t *, ctf_header_t *, unsigned char *, int);
 
-extern ctf_dict_t *ctf_simple_open_internal (const char *, size_t, const char *,
-					     size_t, size_t,
-					     const char *, size_t,
-					     ctf_dynhash_t *, ctf_dynhash_t *, int *);
-extern ctf_dict_t *ctf_bufopen_internal (const ctf_sect_t *, const ctf_sect_t *,
-					 const ctf_sect_t *, ctf_dynhash_t *,
-					 ctf_dynhash_t *, int *);
 extern int ctf_import_unref (ctf_dict_t *fp, ctf_dict_t *pfp);
-extern int ctf_serialize (ctf_dict_t *);
 
 _libctf_malloc_
 extern void *ctf_mmap (size_t length, size_t offset, int fd);
diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
index 6d7a276f2cd..9cbf07626cc 100644
--- a/libctf/ctf-open.c
+++ b/libctf/ctf-open.c
@@ -1233,9 +1233,8 @@  flip_types (ctf_dict_t *fp, void *start, size_t len, int to_foreign)
   return 0;
 }
 
-/* Flip the endianness of BUF, given the offsets in the (already endian-
-   converted) CTH.  If TO_FOREIGN is set, flip to foreign-endianness; if not,
-   flip away.
+/* Flip the endianness of BUF, given the offsets in the (native-endianness) CTH.
+   If TO_FOREIGN is set, flip to foreign-endianness; if not, flip away.
 
    All of this stuff happens before the header is fully initialized, so the
    LCTF_*() macros cannot be used yet.  Since we do not try to endian-convert v1
@@ -1287,21 +1286,6 @@  ctf_dict_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size,
 			     size_t symsect_entsize,
 			     const char *strsect, size_t strsect_size,
 			     int *errp)
-{
-  return ctf_simple_open_internal (ctfsect, ctfsect_size, symsect, symsect_size,
-				   symsect_entsize, strsect, strsect_size, NULL,
-				   NULL, errp);
-}
-
-/* Open a CTF file, mocking up a suitable ctf_sect and overriding the external
-   strtab with a synthetic one.  */
-
-ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size,
-				      const char *symsect, size_t symsect_size,
-				      size_t symsect_entsize,
-				      const char *strsect, size_t strsect_size,
-				      ctf_dynhash_t *syn_strtab,
-				      ctf_dynhash_t *atoms, int *errp)
 {
   ctf_sect_t skeleton;
 
@@ -1338,8 +1322,7 @@  ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size,
       strsectp = &str_sect;
     }
 
-  return ctf_bufopen_internal (ctfsectp, symsectp, strsectp, syn_strtab,
-			       atoms, errp);
+  return ctf_bufopen (ctfsectp, symsectp, strsectp, errp);
 }
 
 /* Decode the specified CTF buffer and optional symbol table, and create a new
@@ -1350,16 +1333,6 @@  ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size,
 ctf_dict_t *
 ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 	     const ctf_sect_t *strsect, int *errp)
-{
-  return ctf_bufopen_internal (ctfsect, symsect, strsect, NULL, NULL, errp);
-}
-
-/* Like ctf_bufopen, but overriding the external strtab with a synthetic one.  */
-
-ctf_dict_t *
-ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
-		      const ctf_sect_t *strsect, ctf_dynhash_t *syn_strtab,
-		      ctf_dynhash_t *atoms, int *errp)
 {
   const ctf_preamble_t *pp;
   size_t hdrsz = sizeof (ctf_header_t);
@@ -1370,8 +1343,7 @@  ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
 
   libctf_init_debug();
 
-  if ((ctfsect == NULL) || ((symsect != NULL) &&
-			    ((strsect == NULL) && syn_strtab == NULL)))
+  if ((ctfsect == NULL) || ((symsect != NULL) && (strsect == NULL)))
     return (ctf_set_open_errno (errp, EINVAL));
 
   if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) &&
@@ -1623,7 +1595,7 @@  ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
   fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf
     + hp->cth_stroff;
   fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen;
-  if (ctf_str_create_atoms (fp, atoms) < 0)
+  if (ctf_str_create_atoms (fp) < 0)
     {
       err = ENOMEM;
       goto bad;
@@ -1669,7 +1641,6 @@  ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
       fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data;
       fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size;
     }
-  fp->ctf_syn_ext_strtab = syn_strtab;
 
   /* Dynamic state, for dynamic addition to this dict after loading.  */
 
diff --git a/libctf/ctf-serialize.c b/libctf/ctf-serialize.c
index 82e5b7d705b..2d1f014db28 100644
--- a/libctf/ctf-serialize.c
+++ b/libctf/ctf-serialize.c
@@ -470,9 +470,9 @@  ctf_symtypetab_sect_sizes (ctf_dict_t *fp, emit_symtypetab_state_t *s,
      filter out reported symbols from the variable section, and filter out all
      other symbols from the symtypetab sections.  (If we are not linking, the
      symbols are sorted; if we are linking, don't bother sorting if we are not
-     filtering out reported symbols: this is almost certaily an ld -r and only
+     filtering out reported symbols: this is almost certainly an ld -r and only
      the linker is likely to consume these symtypetabs again.  The linker
-     doesn't care what order the symtypetab entries is in, since it only
+     doesn't care what order the symtypetab entries are in, since it only
      iterates over symbols and does not use the ctf_lookup_by_symbol* API.)  */
 
   s->sort_syms = 1;
@@ -718,8 +718,8 @@  symerr:
 
 /* Type section.  */
 
-/* Iterate through the dynamic type definition list and compute the
-   size of the CTF type section.  */
+/* Iterate through the static types and the dynamic type definition list and
+   compute the size of the CTF type section.  */
 
 static size_t
 ctf_type_sect_size (ctf_dict_t *fp)
@@ -778,7 +778,7 @@  ctf_type_sect_size (ctf_dict_t *fp)
 	}
     }
 
-  return type_size;
+  return type_size + fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff;
 }
 
 /* Take a final lap through the dynamic type definition list and copy the
@@ -933,30 +933,22 @@  ctf_sort_var (const void *one_, const void *two_, void *arg_)
 
 /* Overall serialization.  */
 
-/* If the specified CTF dict is writable and has been modified, reload this dict
-   with the updated type definitions, ready for serialization.  In order to make
-   this code and the rest of libctf as simple as possible, we perform updates by
-   taking the dynamic type definitions and creating an in-memory CTF dict
-   containing the definitions, and then call ctf_simple_open_internal() on it.
-   We perform one extra trick here for the benefit of callers and to keep our
-   code simple: ctf_simple_open_internal() will return a new ctf_dict_t, but we
-   want to keep the fp constant for the caller, so after
-   ctf_simple_open_internal() returns, we use memcpy to swap the interior of the
-   old and new ctf_dict_t's, and then free the old.
+/* Emit a new CTF dict which is a serialized copy of this one: also reify
+   the string table and update all offsets in the current dict suitably.
+   (This simplifies ctf-string.c a little, at the cost of storing a second
+   copy of the strtab if this dict was originally read in via ctf_open.)
 
-   We do not currently support serializing a dict that has already been
-   serialized in the past: but all the tables support it except for the types
-   table.  */
+   Other aspects of the existing dict are unchanged, although some
+   static entries may be duplicated in the dynamic state (which should
+   have no effect on visible operation).  */
 
-int
-ctf_serialize (ctf_dict_t *fp)
+static unsigned char *
+ctf_serialize (ctf_dict_t *fp, size_t *bufsiz)
 {
-  ctf_dict_t ofp, *nfp;
   ctf_header_t hdr, *hdrp;
   ctf_dvdef_t *dvd;
   ctf_varent_t *dvarents;
   const ctf_strs_writable_t *strtab;
-  int err;
   int sym_functions = 0;
 
   unsigned char *t;
@@ -969,13 +961,6 @@  ctf_serialize (ctf_dict_t *fp)
   emit_symtypetab_state_t symstate;
   memset (&symstate, 0, sizeof (emit_symtypetab_state_t));
 
-  /* This isn't a very nice error code, but it's close enough: it's what you
-     get if you try to modify a type loaded out of a serialized dict, so
-     it makes at least a little sense that it's what you get if you try to
-     reserialize the dict again.  */
-  if (fp->ctf_stypes > 0)
-    return (ctf_set_errno (fp, ECTF_RDONLY));
-
   /* Fill in an initial CTF header.  We will leave the label, object,
      and function sections empty and only output a header, type section,
      and string table.  The type section begins at a 4-byte aligned
@@ -993,7 +978,7 @@  ctf_serialize (ctf_dict_t *fp)
 
   /* Propagate all symbols in the symtypetabs into the dynamic state, so that
      we can put them back in the right order.  Symbols already in the dynamic
-     state are left as they are.  */
+     state, likely due to repeated serialization, are left unchanged.  */
   do
     {
       ctf_next_t *it = NULL;
@@ -1004,17 +989,17 @@  ctf_serialize (ctf_dict_t *fp)
 					    sym_functions)) != CTF_ERR)
 	if ((ctf_add_funcobjt_sym_forced (fp, sym_functions, sym_name, sym)) < 0)
 	  if (ctf_errno (fp) != ECTF_DUPLICATE)
-	    return -1;				/* errno is set for us.  */
+	    return NULL;			/* errno is set for us.  */
 
       if (ctf_errno (fp) != ECTF_NEXT_END)
-	return -1;				/* errno is set for us.  */
+	return NULL;				/* errno is set for us.  */
     } while (sym_functions++ < 1);
 
   /* Figure out how big the symtypetabs are now.  */
 
   if (ctf_symtypetab_sect_sizes (fp, &symstate, &hdr, &objt_size, &func_size,
 				 &objtidx_size, &funcidx_size) < 0)
-    return -1;					/* errno is set for us.  */
+    return NULL;				/* errno is set for us.  */
 
   /* Propagate all vars into the dynamic state, so we can put them back later.
      Variables already in the dynamic state, likely due to repeated
@@ -1026,7 +1011,7 @@  ctf_serialize (ctf_dict_t *fp)
 
       if (name != NULL && !ctf_dvd_lookup (fp, name))
 	if (ctf_add_variable_forced (fp, name, fp->ctf_vars[i].ctv_type) < 0)
-	  return -1;				/* errno is set for us.  */
+	  return NULL;				/* errno is set for us.  */
     }
 
   for (nvars = 0, dvd = ctf_list_next (&fp->ctf_dvdefs);
@@ -1050,7 +1035,10 @@  ctf_serialize (ctf_dict_t *fp)
   buf_size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
 
   if ((buf = malloc (buf_size)) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+    {
+      ctf_set_errno (fp, EAGAIN);
+      return NULL;
+    }
 
   memcpy (buf, &hdr, sizeof (ctf_header_t));
   t = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_objtoff;
@@ -1085,6 +1073,11 @@  ctf_serialize (ctf_dict_t *fp)
 
   assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_typeoff);
 
+  /* Copy in existing static types, then emit new dynamic types.  */
+
+  memcpy (t, fp->ctf_buf + fp->ctf_header->cth_typeoff,
+	  fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff);
+  t += fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff;
   ctf_emit_type_sect (fp, &t);
 
   assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_stroff);
@@ -1111,136 +1104,15 @@  ctf_serialize (ctf_dict_t *fp)
   hdrp = (ctf_header_t *) buf;
   hdrp->cth_strlen = strtab->cts_len;
   buf_size += hdrp->cth_strlen;
+  *bufsiz = buf_size;
 
-  /* Finally, we are ready to ctf_simple_open() the new dict.  If this is
-     successful, we then switch nfp and fp and free the old dict.  */
-
-  if ((nfp = ctf_simple_open_internal ((char *) buf, buf_size, NULL, 0,
-				       0, NULL, 0, fp->ctf_syn_ext_strtab,
-				       fp->ctf_str_atoms, &err)) == NULL)
-    {
-      free (buf);
-      return (ctf_set_errno (fp, err));
-    }
-
-  (void) ctf_setmodel (nfp, ctf_getmodel (fp));
-
-  nfp->ctf_parent = fp->ctf_parent;
-  nfp->ctf_parent_unreffed = fp->ctf_parent_unreffed;
-  nfp->ctf_refcnt = fp->ctf_refcnt;
-  if (nfp->ctf_dynbase == NULL)
-    nfp->ctf_dynbase = buf;		/* Make sure buf is freed on close.  */
-  nfp->ctf_dthash = fp->ctf_dthash;
-  nfp->ctf_dtdefs = fp->ctf_dtdefs;
-  nfp->ctf_dvhash = fp->ctf_dvhash;
-  nfp->ctf_dvdefs = fp->ctf_dvdefs;
-  nfp->ctf_dtoldid = fp->ctf_dtoldid;
-  nfp->ctf_add_processing = fp->ctf_add_processing;
-  nfp->ctf_snapshots = fp->ctf_snapshots + 1;
-  nfp->ctf_specific = fp->ctf_specific;
-  nfp->ctf_nfuncidx = fp->ctf_nfuncidx;
-  nfp->ctf_nobjtidx = fp->ctf_nobjtidx;
-  nfp->ctf_objthash = fp->ctf_objthash;
-  nfp->ctf_funchash = fp->ctf_funchash;
-  nfp->ctf_dynsyms = fp->ctf_dynsyms;
-  nfp->ctf_ptrtab = fp->ctf_ptrtab;
-  nfp->ctf_pptrtab = fp->ctf_pptrtab;
-  nfp->ctf_typemax = fp->ctf_typemax;
-  nfp->ctf_stypes = fp->ctf_stypes;
-  nfp->ctf_dynsymidx = fp->ctf_dynsymidx;
-  nfp->ctf_dynsymmax = fp->ctf_dynsymmax;
-  nfp->ctf_ptrtab_len = fp->ctf_ptrtab_len;
-  nfp->ctf_pptrtab_len = fp->ctf_pptrtab_len;
-  nfp->ctf_link_inputs = fp->ctf_link_inputs;
-  nfp->ctf_link_outputs = fp->ctf_link_outputs;
-  nfp->ctf_errs_warnings = fp->ctf_errs_warnings;
-  nfp->ctf_funcidx_names = fp->ctf_funcidx_names;
-  nfp->ctf_objtidx_names = fp->ctf_objtidx_names;
-  nfp->ctf_funcidx_sxlate = fp->ctf_funcidx_sxlate;
-  nfp->ctf_objtidx_sxlate = fp->ctf_objtidx_sxlate;
-  nfp->ctf_str_prov_offset = fp->ctf_str_prov_offset;
-  nfp->ctf_syn_ext_strtab = fp->ctf_syn_ext_strtab;
-  nfp->ctf_pptrtab_typemax = fp->ctf_pptrtab_typemax;
-  nfp->ctf_in_flight_dynsyms = fp->ctf_in_flight_dynsyms;
-  nfp->ctf_link_in_cu_mapping = fp->ctf_link_in_cu_mapping;
-  nfp->ctf_link_out_cu_mapping = fp->ctf_link_out_cu_mapping;
-  nfp->ctf_link_type_mapping = fp->ctf_link_type_mapping;
-  nfp->ctf_link_memb_name_changer = fp->ctf_link_memb_name_changer;
-  nfp->ctf_link_memb_name_changer_arg = fp->ctf_link_memb_name_changer_arg;
-  nfp->ctf_link_variable_filter = fp->ctf_link_variable_filter;
-  nfp->ctf_link_variable_filter_arg = fp->ctf_link_variable_filter_arg;
-  nfp->ctf_symsect_little_endian = fp->ctf_symsect_little_endian;
-  nfp->ctf_link_flags = fp->ctf_link_flags;
-  nfp->ctf_dedup_atoms = fp->ctf_dedup_atoms;
-  nfp->ctf_dedup_atoms_alloc = fp->ctf_dedup_atoms_alloc;
-  memcpy (&nfp->ctf_dedup, &fp->ctf_dedup, sizeof (fp->ctf_dedup));
-
-  nfp->ctf_snapshot_lu = fp->ctf_snapshots;
-
-  memcpy (&nfp->ctf_lookups, fp->ctf_lookups, sizeof (fp->ctf_lookups));
-  nfp->ctf_structs = fp->ctf_structs;
-  nfp->ctf_unions = fp->ctf_unions;
-  nfp->ctf_enums = fp->ctf_enums;
-  nfp->ctf_names = fp->ctf_names;
-
-  fp->ctf_dthash = NULL;
-  ctf_str_free_atoms (nfp);
-  nfp->ctf_str_atoms = fp->ctf_str_atoms;
-  nfp->ctf_prov_strtab = fp->ctf_prov_strtab;
-  nfp->ctf_dynstrtab = fp->ctf_dynstrtab;
-  nfp->ctf_str_movable_refs = fp->ctf_str_movable_refs;
-  fp->ctf_str_atoms = NULL;
-  fp->ctf_prov_strtab = NULL;
-  fp->ctf_dynstrtab = NULL;
-  fp->ctf_str_movable_refs = NULL;
-  memset (&fp->ctf_dtdefs, 0, sizeof (ctf_list_t));
-  memset (&fp->ctf_errs_warnings, 0, sizeof (ctf_list_t));
-  fp->ctf_add_processing = NULL;
-  fp->ctf_ptrtab = NULL;
-  fp->ctf_pptrtab = NULL;
-  fp->ctf_funcidx_names = NULL;
-  fp->ctf_objtidx_names = NULL;
-  fp->ctf_funcidx_sxlate = NULL;
-  fp->ctf_objtidx_sxlate = NULL;
-  fp->ctf_objthash = NULL;
-  fp->ctf_funchash = NULL;
-  fp->ctf_dynsyms = NULL;
-  fp->ctf_dynsymidx = NULL;
-  fp->ctf_link_inputs = NULL;
-  fp->ctf_link_outputs = NULL;
-  fp->ctf_syn_ext_strtab = NULL;
-  fp->ctf_link_in_cu_mapping = NULL;
-  fp->ctf_link_out_cu_mapping = NULL;
-  fp->ctf_link_type_mapping = NULL;
-  fp->ctf_dedup_atoms = NULL;
-  fp->ctf_dedup_atoms_alloc = NULL;
-  fp->ctf_parent_unreffed = 1;
-
-  fp->ctf_dvhash = NULL;
-  memset (&fp->ctf_dvdefs, 0, sizeof (ctf_list_t));
-  memset (fp->ctf_lookups, 0, sizeof (fp->ctf_lookups));
-  memset (&fp->ctf_in_flight_dynsyms, 0, sizeof (fp->ctf_in_flight_dynsyms));
-  memset (&fp->ctf_dedup, 0, sizeof (fp->ctf_dedup));
-  fp->ctf_structs = NULL;
-  fp->ctf_unions = NULL;
-  fp->ctf_enums = NULL;
-  fp->ctf_names = NULL;
-
-  memcpy (&ofp, fp, sizeof (ctf_dict_t));
-  memcpy (fp, nfp, sizeof (ctf_dict_t));
-  memcpy (nfp, &ofp, sizeof (ctf_dict_t));
-
-  nfp->ctf_refcnt = 1;				/* Force nfp to be freed.  */
-  ctf_dict_close (nfp);
-
-  return 0;
+  return buf;
 
 oom:
-  free (buf);
-  return (ctf_set_errno (fp, EAGAIN));
+  ctf_set_errno (fp, EAGAIN);
 err:
   free (buf);
-  return -1;					/* errno is set for us.  */
+  return NULL;					/* errno is set for us.  */
 }
 
 /* File writing.  */
@@ -1255,30 +1127,27 @@  err:
 int
 ctf_gzwrite (ctf_dict_t *fp, gzFile fd)
 {
-  const unsigned char *buf;
-  ssize_t resid;
-  ssize_t len;
+  unsigned char *buf;
+  unsigned char *p;
+  size_t bufsiz;
+  size_t len, written = 0;
 
-  resid = sizeof (ctf_header_t);
-  buf = (unsigned char *) fp->ctf_header;
-  while (resid != 0)
+  if ((buf = ctf_serialize (fp, &bufsiz)) == NULL)
+    return -1;					/* errno is set for us.  */
+
+  p = buf;
+  while (written < bufsiz)
     {
-      if ((len = gzwrite (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
-      resid -= len;
-      buf += len;
-    }
-
-  resid = fp->ctf_size;
-  buf = fp->ctf_buf;
-  while (resid != 0)
-    {
-      if ((len = gzwrite (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
-      resid -= len;
-      buf += len;
+      if ((len = gzwrite (fd, p, bufsiz - written)) <= 0)
+	{
+	  free (buf);
+	  return (ctf_set_errno (fp, errno));
+	}
+      written += len;
+      p += len;
     }
 
+  free (buf);
   return 0;
 }
 
@@ -1288,88 +1157,95 @@  ctf_gzwrite (ctf_dict_t *fp, gzFile fd)
 unsigned char *
 ctf_write_mem (ctf_dict_t *fp, size_t *size, size_t threshold)
 {
-  unsigned char *buf;
+  unsigned char *rawbuf;
+  unsigned char *buf = NULL;
   unsigned char *bp;
-  ctf_header_t *hp;
-  unsigned char *flipped, *src;
-  ssize_t header_len = sizeof (ctf_header_t);
-  ssize_t compress_len;
+  ctf_header_t *rawhp, *hp;
+  unsigned char *src;
+  size_t rawbufsiz;
+  size_t alloc_len = 0;
+  int uncompressed = 0;
   int flip_endian;
-  int uncompressed;
   int rc;
 
   flip_endian = getenv ("LIBCTF_WRITE_FOREIGN_ENDIAN") != NULL;
-  uncompressed = (fp->ctf_size < threshold);
 
-  if (ctf_serialize (fp) < 0)
+  if ((rawbuf = ctf_serialize (fp, &rawbufsiz)) == NULL)
     return NULL;				/* errno is set for us.  */
 
-  compress_len = compressBound (fp->ctf_size);
-  if (fp->ctf_size < threshold)
-    compress_len = fp->ctf_size;
-  if ((buf = malloc (compress_len
-		     + sizeof (struct ctf_header))) == NULL)
+  if (!ctf_assert (fp, rawbufsiz >= sizeof (ctf_header_t)))
+    goto err;
+
+  if (rawbufsiz >= threshold)
+    alloc_len = compressBound (rawbufsiz - sizeof (ctf_header_t))
+      + sizeof (ctf_header_t);
+
+  /* Trivial operation if the buffer is incompressible or too small to bother
+     compressing, and we're not doing a forced write-time flip.  */
+
+  if (rawbufsiz < threshold || rawbufsiz < alloc_len)
+    {
+      alloc_len = rawbufsiz;
+      uncompressed = 1;
+    }
+
+  if (!flip_endian && uncompressed)
+    {
+      *size = rawbufsiz;
+      return rawbuf;
+    }
+
+  if ((buf = malloc (alloc_len)) == NULL)
     {
       ctf_set_errno (fp, ENOMEM);
       ctf_err_warn (fp, 0, 0, _("ctf_write_mem: cannot allocate %li bytes"),
-		    (unsigned long) (compress_len + sizeof (struct ctf_header)));
-      return NULL;
+		    (unsigned long) (alloc_len));
+      goto err;
     }
 
+  rawhp = (ctf_header_t *) rawbuf;
   hp = (ctf_header_t *) buf;
-  memcpy (hp, fp->ctf_header, header_len);
-  bp = buf + sizeof (struct ctf_header);
-  *size = sizeof (struct ctf_header);
+  memcpy (hp, rawbuf, sizeof (ctf_header_t));
+  bp = buf + sizeof (ctf_header_t);
+  *size = sizeof (ctf_header_t);
 
-  if (uncompressed)
-    hp->cth_flags &= ~CTF_F_COMPRESS;
-  else
+  if (!uncompressed)
     hp->cth_flags |= CTF_F_COMPRESS;
 
-  src = fp->ctf_buf;
-  flipped = NULL;
+  src = rawbuf + sizeof (ctf_header_t);
 
   if (flip_endian)
     {
-      if ((flipped = malloc (fp->ctf_size)) == NULL)
-	{
-	  ctf_set_errno (fp, ENOMEM);
-	  ctf_err_warn (fp, 0, 0, _("ctf_write_mem: cannot allocate %li bytes"),
-			(unsigned long) (fp->ctf_size + sizeof (struct ctf_header)));
-	  return NULL;
-	}
       ctf_flip_header (hp);
-      memcpy (flipped, fp->ctf_buf, fp->ctf_size);
-      if (ctf_flip (fp, fp->ctf_header, flipped, 1) < 0)
-	{
-	  free (buf);
-	  free (flipped);
-	  return NULL;				/* errno is set for us.  */
-	}
-      src = flipped;
+      if (ctf_flip (fp, rawhp, src, 1) < 0)
+	goto err;				/* errno is set for us.  */
     }
 
-  if (uncompressed)
-    {
-      memcpy (bp, src, fp->ctf_size);
-      *size += fp->ctf_size;
-    }
-  else
+  if (!uncompressed)
     {
+      size_t compress_len = alloc_len - sizeof (ctf_header_t);
+
       if ((rc = compress (bp, (uLongf *) &compress_len,
-			  src, fp->ctf_size)) != Z_OK)
+			  src, rawbufsiz - sizeof (ctf_header_t))) != Z_OK)
 	{
 	  ctf_set_errno (fp, ECTF_COMPRESS);
 	  ctf_err_warn (fp, 0, 0, _("zlib deflate err: %s"), zError (rc));
-	  free (buf);
-	  return NULL;
+	  goto err;
 	}
       *size += compress_len;
     }
+  else
+    {
+      memcpy (bp, src, rawbufsiz - sizeof (ctf_header_t));
+      *size += rawbufsiz - sizeof (ctf_header_t);
+    }
 
-  free (flipped);
-
+  free (rawbuf);
   return buf;
+err:
+  free (buf);
+  free (rawbuf);
+  return NULL;
 }
 
 /* Compress the specified CTF data stream and write it to the specified file
diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c
index ccf36498eb9..465b2c88ac8 100644
--- a/libctf/ctf-string.c
+++ b/libctf/ctf-string.c
@@ -143,15 +143,9 @@  ctf_str_free_atom (void *a)
    calls to ctf_str_add_external to populate external strtab entries, since
    these are often not quite the same as what appears in any external
    strtab, and the external strtab is often huge and best not aggressively
-   pulled in.)
-
-   Alternatively, if passed, populate atoms from the passed-in table, but do
-   not propagate their flags or refs: they are all non-freeable and
-   non-movable.  (This is used when serializing a dict: this entire atoms
-   table will be thrown away shortly, so it is important that we not create
-   any new strings.)  */
+   pulled in.)  */
 int
-ctf_str_create_atoms (ctf_dict_t *fp, ctf_dynhash_t *atoms)
+ctf_str_create_atoms (ctf_dict_t *fp)
 {
   size_t i;
 
@@ -178,61 +172,26 @@  ctf_str_create_atoms (ctf_dict_t *fp, ctf_dynhash_t *atoms)
   if (errno == ENOMEM)
     goto oom_str_add;
 
-  /* Serializing.  We have existing strings in an existing atoms table with
-     possibly-live pointers to them which must be used unchanged.  Import
-     them into this atoms table.  */
+  /* Pull in all the strings in the strtab as new atoms.  The provisional
+     strtab must be empty at this point, so there is no need to populate
+     atoms from it as well.  Types in this subset are frozen and readonly,
+     so the refs list and movable refs list need not be populated.  */
 
-  if (atoms)
+  for (i = 0; i < fp->ctf_str[CTF_STRTAB_0].cts_len;
+       i += strlen (&fp->ctf_str[CTF_STRTAB_0].cts_strs[i]) + 1)
     {
-      ctf_next_t *it = NULL;
-      void *k, *v;
-      int err;
+      ctf_str_atom_t *atom;
 
-      while ((err = ctf_dynhash_next (atoms, &it, &k, &v)) == 0)
-	{
-	  ctf_str_atom_t *existing = v;
-	  ctf_str_atom_t *atom;
+      if (fp->ctf_str[CTF_STRTAB_0].cts_strs[i] == 0)
+	continue;
 
-	  if (existing->csa_str[0] == 0)
-	    continue;
+      atom = ctf_str_add_ref_internal (fp, &fp->ctf_str[CTF_STRTAB_0].cts_strs[i],
+				       0, 0);
 
-	  if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
-	    goto oom_str_add;
-	  memcpy (atom, existing, sizeof (struct ctf_str_atom));
-	  memset (&atom->csa_refs, 0, sizeof(ctf_list_t));
-	  atom->csa_flags = 0;
+      if (!atom)
+	goto oom_str_add;
 
-	  if (ctf_dynhash_insert (fp->ctf_str_atoms, atom->csa_str, atom) < 0)
-	    {
-	      free (atom);
-	      goto oom_str_add;
-	    }
-	}
-    }
-  else
-    {
-      /* Not serializing.  Pull in all the strings in the strtab as new
-	 atoms.  The provisional strtab must be empty at this point, so
-	 there is no need to populate atoms from it as well.  Types in this
-	 subset are frozen and readonly, so the refs list and movable refs
-	 list need not be populated.  */
-
-      for (i = 0; i < fp->ctf_str[CTF_STRTAB_0].cts_len;
-	   i += strlen (&fp->ctf_str[CTF_STRTAB_0].cts_strs[i]) + 1)
-	{
-	  ctf_str_atom_t *atom;
-
-	  if (fp->ctf_str[CTF_STRTAB_0].cts_strs[i] == 0)
-	    continue;
-
-	  atom = ctf_str_add_ref_internal (fp, &fp->ctf_str[CTF_STRTAB_0].cts_strs[i],
-					   0, 0);
-
-	  if (!atom)
-	    goto oom_str_add;
-
-	  atom->csa_offset = i;
-	}
+      atom->csa_offset = i;
     }
 
   return 0;
diff --git a/libctf/testsuite/libctf-lookup/add-to-opened.c b/libctf/testsuite/libctf-lookup/add-to-opened.c
index dc2e1f55b99..96629afe1aa 100644
--- a/libctf/testsuite/libctf-lookup/add-to-opened.c
+++ b/libctf/testsuite/libctf-lookup/add-to-opened.c
@@ -118,11 +118,9 @@  main (int argc, char *argv[])
   if (ctf_errno (fp) != ECTF_RDONLY)
     fprintf (stderr, "unexpected error %s attempting to set array in readonly portion\n", ctf_errmsg (ctf_errno (fp)));
 
-  if ((ctf_written = ctf_write_mem (fp, &size, 4096)) != NULL)
-    fprintf (stderr, "Writeout unexpectedly succeeded: %s\n", ctf_errmsg (ctf_errno (fp)));
-
-  if (ctf_errno (fp) != ECTF_RDONLY)
-    fprintf (stderr, "unexpected error %s trying to write out previously serialized dict\n", ctf_errmsg (ctf_errno (fp)));
+  if ((ctf_written = ctf_write_mem (fp, &size, 4096)) == NULL)
+    fprintf (stderr, "Re-writeout unexpectedly failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+  free (ctf_written);
 
   /* Finally, make sure we can add new types, and look them up again.  */
 
@@ -138,6 +136,9 @@  main (int argc, char *argv[])
   if (ctf_type_reference (fp, ptrtype) != type)
     fprintf (stderr, "Look up of newly-added type in serialized dict yields ID %lx, expected %lx\n", ctf_type_reference (fp, ptrtype), type);
 
+  ctf_dict_close (fp);
+  ctf_close (ctf);
+
   printf ("All done.\n");
   return 0;