+ ctf_str_atom_t *atom;
+
+ if (!str)
+ str = "";
+
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_MAKE_PROVISIONAL, 0);
+ if (!atom)
+ return 0;
+
+ return atom->csa_offset;
+}
+
+/* Like ctf_str_add(), but additionally augment the atom's refs list with the
+ passed-in ref, whether or not the string is already present. There is no
+ attempt to deduplicate the refs list (but duplicates are harmless). */
+uint32_t
+ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
+{
+ ctf_str_atom_t *atom;
+
+ if (!str)
+ str = "";
+
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
+ | CTF_STR_MAKE_PROVISIONAL, ref);
+ if (!atom)
+ return 0;
+
+ return atom->csa_offset;
+}
+
+/* Like ctf_str_add_ref(), but notes that this memory location must be added as
+ a ref by a later serialization phase, rather than adding it itself. */
+uint32_t
+ctf_str_add_pending (ctf_dict_t *fp, const char *str, uint32_t *ref)
+{
+ ctf_str_atom_t *atom;
+
+ if (!str)
+ str = "";
+
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PENDING_REF
+ | CTF_STR_MAKE_PROVISIONAL, ref);
+ if (!atom)
+ return 0;
+
+ return atom->csa_offset;
+}
+
+/* Note that a pending ref now located at NEW_REF has moved by BYTES bytes. */
+int
+ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
+{
+ if (bytes == 0)
+ return 0;
+
+ if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
+ return (ctf_set_errno (fp, ENOMEM));
+
+ ctf_dynset_remove (fp->ctf_str_pending_ref,
+ (void *) ((signed char *) new_ref - bytes));
+ return 0;
+}
+
+/* Add an external strtab reference at OFFSET. Returns zero if the addition
+ failed, nonzero otherwise. */
+int
+ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
+{
+ ctf_str_atom_t *atom;
+
+ if (!str)
+ str = "";
+
+ atom = ctf_str_add_ref_internal (fp, str, 0, 0);
+ if (!atom)
+ return 0;
+
+ atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
+
+ if (!fp->ctf_syn_ext_strtab)
+ fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
+ ctf_hash_eq_integer,
+ NULL, NULL);
+ if (!fp->ctf_syn_ext_strtab)
+ {
+ ctf_set_errno (fp, ENOMEM);
+ return 0;
+ }
+
+ if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
+ (void *) (uintptr_t)
+ atom->csa_external_offset,
+ (void *) atom->csa_str) < 0)
+ {
+ /* No need to bother freeing the syn_ext_strtab: it will get freed at
+ ctf_str_write_strtab time if unreferenced. */
+ ctf_set_errno (fp, ENOMEM);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Remove a single ref. */
+void
+ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
+{
+ ctf_str_atom_ref_t *aref, *anext;
+ ctf_str_atom_t *atom = NULL;
+
+ atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
+ if (!atom)
+ return;
+
+ for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
+ {
+ anext = ctf_list_next (aref);
+ if (aref->caf_ref == ref)
+ {
+ ctf_list_delete (&atom->csa_refs, aref);
+ free (aref);
+ }
+ }
+
+ ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);