xref: /netbsd-src/external/gpl3/gdb/dist/libctf/ctf-string.c (revision 12989c96ee862c63521a9ead8c44629b7a2ba9b1)
1 /* CTF string table management.
2    Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 
4    This file is part of libctf.
5 
6    libctf is free software; you can redistribute it and/or modify it under
7    the terms of the GNU General Public License as published by the Free
8    Software Foundation; either version 3, or (at your option) any later
9    version.
10 
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14    See the GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; see the file COPYING.  If not see
18    <http://www.gnu.org/licenses/>.  */
19 
20 #include <assert.h>
21 #include <ctf-impl.h>
22 #include <string.h>
23 
24 static ctf_str_atom_t *
25 ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
26 			  int flags, uint32_t *ref);
27 
28 /* Convert an encoded CTF string name into a pointer to a C string, possibly
29   using an explicit internal provisional strtab rather than the fp-based
30   one.  */
31 const char *
32 ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab)
33 {
34   ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
35 
36   if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL))
37     ctsp = strtab;
38 
39   /* If this name is in the external strtab, and there is a synthetic
40      strtab, use it in preference.  (This is used to add the set of strings
41      -- symbol names, etc -- the linker knows about before the strtab is
42      written out.)  */
43 
44   if (CTF_NAME_STID (name) == CTF_STRTAB_1
45       && fp->ctf_syn_ext_strtab != NULL)
46     return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab,
47 			       (void *) (uintptr_t) name);
48 
49   /* If the name is in the internal strtab, and the name offset is beyond
50      the end of the ctsp->cts_len but below the ctf_str_prov_offset, this is
51      a provisional string added by ctf_str_add*() but not yet built into a
52      real strtab: get the value out of the ctf_prov_strtab.  */
53 
54   if (CTF_NAME_STID (name) == CTF_STRTAB_0
55       && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset)
56       return ctf_dynhash_lookup (fp->ctf_prov_strtab,
57 				 (void *) (uintptr_t) name);
58 
59   if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
60     return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
61 
62   /* String table not loaded or corrupt offset.  */
63   return NULL;
64 }
65 
66 /* Convert an encoded CTF string name into a pointer to a C string by looking
67   up the appropriate string table buffer and then adding the offset.  */
68 const char *
69 ctf_strraw (ctf_dict_t *fp, uint32_t name)
70 {
71   return ctf_strraw_explicit (fp, name, NULL);
72 }
73 
74 /* Return a guaranteed-non-NULL pointer to the string with the given CTF
75    name.  */
76 const char *
77 ctf_strptr (ctf_dict_t *fp, uint32_t name)
78 {
79   const char *s = ctf_strraw (fp, name);
80   return (s != NULL ? s : "(?)");
81 }
82 
83 /* As above, but return info on what is wrong in more detail.
84    (Used for type lookups.) */
85 
86 const char *
87 ctf_strptr_validate (ctf_dict_t *fp, uint32_t name)
88 {
89   const char *str = ctf_strraw (fp, name);
90 
91   if (str == NULL)
92     {
93       if (CTF_NAME_STID (name) == CTF_STRTAB_1
94 	  && fp->ctf_syn_ext_strtab == NULL
95 	  && fp->ctf_str[CTF_NAME_STID (name)].cts_strs == NULL)
96 	{
97 	  ctf_set_errno (fp, ECTF_STRTAB);
98 	  return NULL;
99 	}
100 
101       ctf_set_errno (fp, ECTF_BADNAME);
102       return NULL;
103     }
104   return str;
105 }
106 
107 /* Remove all refs to a given atom.  */
108 static void
109 ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
110 {
111   ctf_str_atom_ref_t *ref, *next;
112 
113   for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
114     {
115       next = ctf_list_next (ref);
116       ctf_list_delete (&atom->csa_refs, ref);
117       if (atom->csa_flags & CTF_STR_ATOM_MOVABLE)
118 	{
119 	  ctf_str_atom_ref_movable_t *movref;
120 	  movref = (ctf_str_atom_ref_movable_t *) ref;
121 	  ctf_dynhash_remove (movref->caf_movable_refs, ref);
122 	}
123 
124       free (ref);
125     }
126 }
127 
128 /* Free an atom.  */
129 static void
130 ctf_str_free_atom (void *a)
131 {
132   ctf_str_atom_t *atom = a;
133 
134   ctf_str_purge_atom_refs (atom);
135 
136   if (atom->csa_flags & CTF_STR_ATOM_FREEABLE)
137     free (atom->csa_str);
138 
139   free (atom);
140 }
141 
142 /* Create the atoms table.  There is always at least one atom in it, the null
143    string: but also pull in atoms from the internal strtab.  (We rely on
144    calls to ctf_str_add_external to populate external strtab entries, since
145    these are often not quite the same as what appears in any external
146    strtab, and the external strtab is often huge and best not aggressively
147    pulled in.)  */
148 int
149 ctf_str_create_atoms (ctf_dict_t *fp)
150 {
151   size_t i;
152 
153   fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
154 					  NULL, ctf_str_free_atom);
155   if (!fp->ctf_str_atoms)
156     return -ENOMEM;
157 
158   if (!fp->ctf_prov_strtab)
159     fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer,
160 					      ctf_hash_eq_integer,
161 					      NULL, NULL);
162   if (!fp->ctf_prov_strtab)
163     goto oom_prov_strtab;
164 
165   fp->ctf_str_movable_refs = ctf_dynhash_create (ctf_hash_integer,
166 						 ctf_hash_eq_integer,
167 						 NULL, NULL);
168   if (!fp->ctf_str_movable_refs)
169     goto oom_movable_refs;
170 
171   errno = 0;
172   ctf_str_add (fp, "");
173   if (errno == ENOMEM)
174     goto oom_str_add;
175 
176   /* Pull in all the strings in the strtab as new atoms.  The provisional
177      strtab must be empty at this point, so there is no need to populate
178      atoms from it as well.  Types in this subset are frozen and readonly,
179      so the refs list and movable refs list need not be populated.  */
180 
181   for (i = 0; i < fp->ctf_str[CTF_STRTAB_0].cts_len;
182        i += strlen (&fp->ctf_str[CTF_STRTAB_0].cts_strs[i]) + 1)
183     {
184       ctf_str_atom_t *atom;
185 
186       if (fp->ctf_str[CTF_STRTAB_0].cts_strs[i] == 0)
187 	continue;
188 
189       atom = ctf_str_add_ref_internal (fp, &fp->ctf_str[CTF_STRTAB_0].cts_strs[i],
190 				       0, 0);
191 
192       if (!atom)
193 	goto oom_str_add;
194 
195       atom->csa_offset = i;
196     }
197 
198   return 0;
199 
200  oom_str_add:
201   ctf_dynhash_destroy (fp->ctf_str_movable_refs);
202   fp->ctf_str_movable_refs = NULL;
203  oom_movable_refs:
204   ctf_dynhash_destroy (fp->ctf_prov_strtab);
205   fp->ctf_prov_strtab = NULL;
206  oom_prov_strtab:
207   ctf_dynhash_destroy (fp->ctf_str_atoms);
208   fp->ctf_str_atoms = NULL;
209   return -ENOMEM;
210 }
211 
212 /* Destroy the atoms table and associated refs.  */
213 void
214 ctf_str_free_atoms (ctf_dict_t *fp)
215 {
216   ctf_dynhash_destroy (fp->ctf_prov_strtab);
217   ctf_dynhash_destroy (fp->ctf_str_atoms);
218   ctf_dynhash_destroy (fp->ctf_str_movable_refs);
219   if (fp->ctf_dynstrtab)
220     {
221       free (fp->ctf_dynstrtab->cts_strs);
222       free (fp->ctf_dynstrtab);
223     }
224 }
225 
226 #define CTF_STR_ADD_REF 0x1
227 #define CTF_STR_PROVISIONAL 0x2
228 #define CTF_STR_MOVABLE 0x4
229 
230 /* Allocate a ref and bind it into a ref list.  */
231 
232 static ctf_str_atom_ref_t *
233 aref_create (ctf_dict_t *fp, ctf_str_atom_t *atom, uint32_t *ref, int flags)
234 {
235   ctf_str_atom_ref_t *aref;
236   size_t s = sizeof (struct ctf_str_atom_ref);
237 
238   if (flags & CTF_STR_MOVABLE)
239     s = sizeof (struct ctf_str_atom_ref_movable);
240 
241   aref = malloc (s);
242 
243   if (!aref)
244     return NULL;
245 
246   aref->caf_ref = ref;
247 
248   /* Movable refs get a backpointer to them in ctf_str_movable_refs, and a
249      pointer to ctf_str_movable_refs itself in the ref, for use when freeing
250      refs: they can be moved later in batches via a call to
251      ctf_str_move_refs.  */
252 
253   if (flags & CTF_STR_MOVABLE)
254     {
255       ctf_str_atom_ref_movable_t *movref = (ctf_str_atom_ref_movable_t *) aref;
256 
257       movref->caf_movable_refs = fp->ctf_str_movable_refs;
258 
259       if (ctf_dynhash_insert (fp->ctf_str_movable_refs, ref, aref) < 0)
260 	{
261 	  free (aref);
262 	  return NULL;
263 	}
264     }
265 
266   ctf_list_append (&atom->csa_refs, aref);
267 
268   return aref;
269 }
270 
271 /* Add a string to the atoms table, copying the passed-in string if
272    necessary.  Return the atom added. Return NULL only when out of memory
273    (and do not touch the passed-in string in that case).
274 
275    Possibly add a provisional entry for this string to the provisional
276    strtab.  If the string is in the provisional strtab, update its ref list
277    with the passed-in ref, causing the ref to be updated when the strtab is
278    written out.  */
279 
280 static ctf_str_atom_t *
281 ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
282 			  int flags, uint32_t *ref)
283 {
284   char *newstr = NULL;
285   ctf_str_atom_t *atom = NULL;
286   int added = 0;
287 
288   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
289 
290   /* Existing atoms get refs added only if they are provisional:
291      non-provisional strings already have a fixed strtab offset, and just
292      get their ref updated immediately, since its value cannot change.  */
293 
294   if (atom)
295     {
296       if (!ctf_dynhash_lookup (fp->ctf_prov_strtab, (void *) (uintptr_t)
297 			       atom->csa_offset))
298 	{
299 	  if (flags & CTF_STR_ADD_REF)
300 	    {
301 	      if (atom->csa_external_offset)
302 		*ref = atom->csa_external_offset;
303 	      else
304 		*ref = atom->csa_offset;
305 	    }
306 	  return atom;
307 	}
308 
309       if (flags & CTF_STR_ADD_REF)
310 	{
311 	  if (!aref_create (fp, atom, ref, flags))
312 	    {
313 	      ctf_set_errno (fp, ENOMEM);
314 	      return NULL;
315 	    }
316 	}
317 
318       return atom;
319     }
320 
321   /* New atom.  */
322 
323   if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
324     goto oom;
325   memset (atom, 0, sizeof (struct ctf_str_atom));
326 
327   /* Don't allocate new strings if this string is within an mmapped
328      strtab.  */
329 
330   if ((unsigned char *) str < (unsigned char *) fp->ctf_data_mmapped
331       || (unsigned char *) str > (unsigned char *) fp->ctf_data_mmapped + fp->ctf_data_mmapped_len)
332     {
333       if ((newstr = strdup (str)) == NULL)
334 	goto oom;
335       atom->csa_flags |= CTF_STR_ATOM_FREEABLE;
336       atom->csa_str = newstr;
337     }
338   else
339     atom->csa_str = (char *) str;
340 
341   if (ctf_dynhash_insert (fp->ctf_str_atoms, atom->csa_str, atom) < 0)
342     goto oom;
343   added = 1;
344 
345   atom->csa_snapshot_id = fp->ctf_snapshots;
346 
347   /* New atoms marked provisional go into the provisional strtab, and get a
348      ref added.  */
349 
350   if (flags & CTF_STR_PROVISIONAL)
351     {
352       atom->csa_offset = fp->ctf_str_prov_offset;
353 
354       if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t)
355 			      atom->csa_offset, (void *) atom->csa_str) < 0)
356 	goto oom;
357 
358       fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
359 
360       if (flags & CTF_STR_ADD_REF)
361       {
362 	if (!aref_create (fp, atom, ref, flags))
363 	  goto oom;
364       }
365     }
366 
367   return atom;
368 
369  oom:
370   if (added)
371     ctf_dynhash_remove (fp->ctf_str_atoms, atom->csa_str);
372   free (atom);
373   free (newstr);
374   ctf_set_errno (fp, ENOMEM);
375   return NULL;
376 }
377 
378 /* Add a string to the atoms table, without augmenting the ref list for this
379    string: return a 'provisional offset' which can be used to return this string
380    until ctf_str_write_strtab is called, or 0 on failure.  (Everywhere the
381    provisional offset is assigned to should be added as a ref using
382    ctf_str_add_ref() as well.) */
383 uint32_t
384 ctf_str_add (ctf_dict_t *fp, const char *str)
385 {
386   ctf_str_atom_t *atom;
387 
388   if (!str)
389     str = "";
390 
391   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PROVISIONAL, 0);
392   if (!atom)
393     return 0;
394 
395   return atom->csa_offset;
396 }
397 
398 /* Like ctf_str_add(), but additionally augment the atom's refs list with the
399    passed-in ref, whether or not the string is already present.  There is no
400    attempt to deduplicate the refs list (but duplicates are harmless).  */
401 uint32_t
402 ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
403 {
404   ctf_str_atom_t *atom;
405 
406   if (!str)
407     str = "";
408 
409   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
410 				   | CTF_STR_PROVISIONAL, ref);
411   if (!atom)
412     return 0;
413 
414   return atom->csa_offset;
415 }
416 
417 /* Like ctf_str_add_ref(), but note that the ref may be moved later on.  */
418 uint32_t
419 ctf_str_add_movable_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
420 {
421   ctf_str_atom_t *atom;
422 
423   if (!str)
424     str = "";
425 
426   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
427 				   | CTF_STR_PROVISIONAL
428 				   | CTF_STR_MOVABLE, ref);
429   if (!atom)
430     return 0;
431 
432   return atom->csa_offset;
433 }
434 
435 /* Add an external strtab reference at OFFSET.  Returns zero if the addition
436    failed, nonzero otherwise.  */
437 int
438 ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
439 {
440   ctf_str_atom_t *atom;
441 
442   if (!str)
443     str = "";
444 
445   atom = ctf_str_add_ref_internal (fp, str, 0, 0);
446   if (!atom)
447     return 0;
448 
449   atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
450 
451   if (!fp->ctf_syn_ext_strtab)
452     fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
453 						 ctf_hash_eq_integer,
454 						 NULL, NULL);
455   if (!fp->ctf_syn_ext_strtab)
456     {
457       ctf_set_errno (fp, ENOMEM);
458       return 0;
459     }
460 
461   if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
462 			  (void *) (uintptr_t)
463 			  atom->csa_external_offset,
464 			  (void *) atom->csa_str) < 0)
465     {
466       /* No need to bother freeing the syn_ext_strtab: it will get freed at
467 	 ctf_str_write_strtab time if unreferenced.  */
468       ctf_set_errno (fp, ENOMEM);
469       return 0;
470     }
471 
472   return 1;
473 }
474 
475 /* Note that refs have moved from (SRC, LEN) to DEST.  We use the movable
476    refs backpointer for this, because it is done an amortized-constant
477    number of times during structure member and enumerand addition, and if we
478    did a linear search this would turn such addition into an O(n^2)
479    operation.  Even this is not linear, but it's better than that.  */
480 int
481 ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest)
482 {
483   uintptr_t p;
484 
485   if (src == dest)
486     return 0;
487 
488   for (p = (uintptr_t) src; p - (uintptr_t) src < len; p++)
489     {
490       ctf_str_atom_ref_t *ref;
491 
492       if ((ref = ctf_dynhash_lookup (fp->ctf_str_movable_refs,
493 				     (ctf_str_atom_ref_t *) p)) != NULL)
494 	{
495 	  int out_of_memory;
496 
497 	  ref->caf_ref = (uint32_t *) (((uintptr_t) ref->caf_ref +
498 					(uintptr_t) dest - (uintptr_t) src));
499 	  ctf_dynhash_remove (fp->ctf_str_movable_refs,
500 			      (ctf_str_atom_ref_t *) p);
501 	  out_of_memory = ctf_dynhash_insert (fp->ctf_str_movable_refs,
502 					      ref->caf_ref, ref);
503 	  assert (out_of_memory == 0);
504 	}
505     }
506 
507   return 0;
508 }
509 
510 /* Remove a single ref.  */
511 void
512 ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
513 {
514   ctf_str_atom_ref_t *aref, *anext;
515   ctf_str_atom_t *atom = NULL;
516 
517   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
518   if (!atom)
519     return;
520 
521   for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
522     {
523       anext = ctf_list_next (aref);
524       if (aref->caf_ref == ref)
525 	{
526 	  ctf_list_delete (&atom->csa_refs, aref);
527 	  free (aref);
528 	}
529     }
530 }
531 
532 /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
533    snapshot ID.  External atoms are never removed, because they came from the
534    linker string table and are still present even if you roll back type
535    additions.  */
536 static int
537 ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
538 {
539   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
540   ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
541 
542   return (atom->csa_snapshot_id > id->snapshot_id)
543     && (atom->csa_external_offset == 0);
544 }
545 
546 /* Roll back, deleting all (internal) atoms created after a particular ID.  */
547 void
548 ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
549 {
550   ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
551 }
552 
553 /* An adaptor around ctf_purge_atom_refs.  */
554 static void
555 ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
556 			     void *arg _libctf_unused_)
557 {
558   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
559   ctf_str_purge_atom_refs (atom);
560 }
561 
562 /* Remove all the recorded refs from the atoms table.  */
563 void
564 ctf_str_purge_refs (ctf_dict_t *fp)
565 {
566   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
567 }
568 
569 /* Update a list of refs to the specified value. */
570 static void
571 ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
572 {
573   ctf_str_atom_ref_t *ref;
574 
575   for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
576        ref = ctf_list_next (ref))
577     *(ref->caf_ref) = value;
578 }
579 
580 /* Sort the strtab.  */
581 static int
582 ctf_str_sort_strtab (const void *a, const void *b)
583 {
584   ctf_str_atom_t **one = (ctf_str_atom_t **) a;
585   ctf_str_atom_t **two = (ctf_str_atom_t **) b;
586 
587   return (strcmp ((*one)->csa_str, (*two)->csa_str));
588 }
589 
590 /* Write out and return a strtab containing all strings with recorded refs,
591    adjusting the refs to refer to the corresponding string.  The returned
592    strtab is already assigned to strtab 0 in this dict, is owned by this
593    dict, and may be NULL on error.  Also populate the synthetic strtab with
594    mappings from external strtab offsets to names, so we can look them up
595    with ctf_strptr().  Only external strtab offsets with references are
596    added.
597 
598    As a side effect, replaces the strtab of the current dict with the newly-
599    generated strtab.  This is an exception to the general rule that
600    serialization does not change the dict passed in, because the alternative
601    is to copy the entire atoms table on every reserialization just to avoid
602    modifying the original, which is excessively costly for minimal gain.
603 
604    We use the lazy man's approach and double memory costs by always storing
605    atoms as individually allocated entities whenever they come from anywhere
606    but a freshly-opened, mmapped dict, even though after serialization there
607    is another copy in the strtab; this ensures that ctf_strptr()-returned
608    pointers to them remain valid for the lifetime of the dict.
609 
610    This is all rendered more complex because if a dict is ctf_open()ed it
611    will have a bunch of strings in its strtab already, and their strtab
612    offsets can never change (without piles of complexity to rescan the
613    entire dict just to get all the offsets to all of them into the atoms
614    table).  Entries below the existing strtab limit are just copied into the
615    new dict: entries above it are new, and are are sorted first, then
616    appended to it.  The sorting is purely a compression-efficiency
617    improvement, and we get nearly as good an improvement from sorting big
618    chunks like this as we would from sorting the whole thing.  */
619 
620 const ctf_strs_writable_t *
621 ctf_str_write_strtab (ctf_dict_t *fp)
622 {
623   ctf_strs_writable_t *strtab;
624   size_t strtab_count = 0;
625   uint32_t cur_stroff = 0;
626   ctf_str_atom_t **sorttab;
627   ctf_next_t *it = NULL;
628   size_t i;
629   void *v;
630   int err;
631   int new_strtab = 0;
632   int any_external = 0;
633 
634   strtab = calloc (1, sizeof (ctf_strs_writable_t));
635   if (!strtab)
636     return NULL;
637 
638   /* The strtab contains the existing string table at its start: figure out
639      how many new strings we need to add.  We only need to add new strings
640      that have no external offset, that have refs, and that are found in the
641      provisional strtab.  If the existing strtab is empty we also need to
642      add the null string at its start.  */
643 
644   strtab->cts_len = fp->ctf_str[CTF_STRTAB_0].cts_len;
645 
646   if (strtab->cts_len == 0)
647     {
648       new_strtab = 1;
649       strtab->cts_len++; 			/* For the \0.  */
650     }
651 
652   /* Count new entries in the strtab: i.e. entries in the provisional
653      strtab.  Ignore any entry for \0, entries which ended up in the
654      external strtab, and unreferenced entries.  */
655 
656   while ((err = ctf_dynhash_next (fp->ctf_prov_strtab, &it, NULL, &v)) == 0)
657     {
658       const char *str = (const char *) v;
659       ctf_str_atom_t *atom;
660 
661       atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
662       if (!ctf_assert (fp, atom))
663 	goto err_strtab;
664 
665       if (atom->csa_str[0] == 0 || ctf_list_empty_p (&atom->csa_refs) ||
666 	  atom->csa_external_offset)
667 	continue;
668 
669       strtab->cts_len += strlen (atom->csa_str) + 1;
670       strtab_count++;
671     }
672   if (err != ECTF_NEXT_END)
673     {
674       ctf_dprintf ("ctf_str_write_strtab: error counting strtab entries: %s\n",
675 		   ctf_errmsg (err));
676       goto err_strtab;
677     }
678 
679   ctf_dprintf ("%lu bytes of strings in strtab: %lu pre-existing.\n",
680 	       (unsigned long) strtab->cts_len,
681 	       (unsigned long) fp->ctf_str[CTF_STRTAB_0].cts_len);
682 
683   /* Sort the new part of the strtab.  */
684 
685   sorttab = calloc (strtab_count, sizeof (ctf_str_atom_t *));
686   if (!sorttab)
687     {
688       ctf_set_errno (fp, ENOMEM);
689       goto err_strtab;
690     }
691 
692   i = 0;
693   while ((err = ctf_dynhash_next (fp->ctf_prov_strtab, &it, NULL, &v)) == 0)
694     {
695       ctf_str_atom_t *atom;
696 
697       atom = ctf_dynhash_lookup (fp->ctf_str_atoms, v);
698       if (!ctf_assert (fp, atom))
699 	goto err_sorttab;
700 
701       if (atom->csa_str[0] == 0 || ctf_list_empty_p (&atom->csa_refs) ||
702 	  atom->csa_external_offset)
703 	continue;
704 
705       sorttab[i++] = atom;
706     }
707 
708   qsort (sorttab, strtab_count, sizeof (ctf_str_atom_t *),
709 	 ctf_str_sort_strtab);
710 
711   if ((strtab->cts_strs = malloc (strtab->cts_len)) == NULL)
712     goto err_sorttab;
713 
714   cur_stroff = fp->ctf_str[CTF_STRTAB_0].cts_len;
715 
716   if (new_strtab)
717     {
718       strtab->cts_strs[0] = 0;
719       cur_stroff++;
720     }
721   else
722     memcpy (strtab->cts_strs, fp->ctf_str[CTF_STRTAB_0].cts_strs,
723 	    fp->ctf_str[CTF_STRTAB_0].cts_len);
724 
725   /* Work over the sorttab, add its strings to the strtab, and remember
726      where they are in the csa_offset for the appropriate atom.  No ref
727      updating is done at this point, because refs might well relate to
728      already-existing strings, or external strings, which do not need adding
729      to the strtab and may not be in the sorttab.  */
730 
731   for (i = 0; i < strtab_count; i++)
732     {
733       sorttab[i]->csa_offset = cur_stroff;
734       strcpy (&strtab->cts_strs[cur_stroff], sorttab[i]->csa_str);
735       cur_stroff += strlen (sorttab[i]->csa_str) + 1;
736     }
737   free (sorttab);
738   sorttab = NULL;
739 
740   /* Update all refs, then purge them as no longer necessary: also update
741      the strtab appropriately.  */
742 
743   while ((err = ctf_dynhash_next (fp->ctf_str_atoms, &it, NULL, &v)) == 0)
744     {
745       ctf_str_atom_t *atom = (ctf_str_atom_t *) v;
746       uint32_t offset;
747 
748       if (ctf_list_empty_p (&atom->csa_refs))
749 	continue;
750 
751       if (atom->csa_external_offset)
752 	{
753 	  any_external = 1;
754 	  offset = atom->csa_external_offset;
755 	}
756       else
757 	offset = atom->csa_offset;
758       ctf_str_update_refs (atom, offset);
759     }
760   if (err != ECTF_NEXT_END)
761     {
762       ctf_dprintf ("ctf_str_write_strtab: error iterating over atoms while updating refs: %s\n",
763 		   ctf_errmsg (err));
764       goto err_strtab;
765     }
766   ctf_str_purge_refs (fp);
767 
768   if (!any_external)
769     {
770       ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
771       fp->ctf_syn_ext_strtab = NULL;
772     }
773 
774   /* Replace the old strtab with the new one in this dict.  */
775 
776   if (fp->ctf_dynstrtab)
777     {
778       free (fp->ctf_dynstrtab->cts_strs);
779       free (fp->ctf_dynstrtab);
780     }
781 
782   fp->ctf_dynstrtab = strtab;
783   fp->ctf_str[CTF_STRTAB_0].cts_strs = strtab->cts_strs;
784   fp->ctf_str[CTF_STRTAB_0].cts_len = strtab->cts_len;
785 
786   /* All the provisional strtab entries are now real strtab entries, and
787      ctf_strptr() will find them there.  The provisional offset now starts right
788      beyond the new end of the strtab.  */
789 
790   ctf_dynhash_empty (fp->ctf_prov_strtab);
791   fp->ctf_str_prov_offset = strtab->cts_len + 1;
792   return strtab;
793 
794  err_sorttab:
795   free (sorttab);
796  err_strtab:
797   free (strtab);
798   return NULL;
799 }
800