xref: /netbsd-src/external/gpl3/binutils/dist/libctf/ctf-string.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
16f4ced0bSchristos /* CTF string table management.
2*cb63e24eSchristos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
36f4ced0bSchristos 
46f4ced0bSchristos    This file is part of libctf.
56f4ced0bSchristos 
66f4ced0bSchristos    libctf is free software; you can redistribute it and/or modify it under
76f4ced0bSchristos    the terms of the GNU General Public License as published by the Free
86f4ced0bSchristos    Software Foundation; either version 3, or (at your option) any later
96f4ced0bSchristos    version.
106f4ced0bSchristos 
116f4ced0bSchristos    This program is distributed in the hope that it will be useful, but
126f4ced0bSchristos    WITHOUT ANY WARRANTY; without even the implied warranty of
136f4ced0bSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
146f4ced0bSchristos    See the GNU General Public License for more details.
156f4ced0bSchristos 
166f4ced0bSchristos    You should have received a copy of the GNU General Public License
176f4ced0bSchristos    along with this program; see the file COPYING.  If not see
186f4ced0bSchristos    <http://www.gnu.org/licenses/>.  */
196f4ced0bSchristos 
206f4ced0bSchristos #include <ctf-impl.h>
216f4ced0bSchristos #include <string.h>
224f645668Schristos #include <assert.h>
236f4ced0bSchristos 
246f4ced0bSchristos /* Convert an encoded CTF string name into a pointer to a C string, using an
256f4ced0bSchristos   explicit internal strtab rather than the fp-based one.  */
266f4ced0bSchristos const char *
ctf_strraw_explicit(ctf_dict_t * fp,uint32_t name,ctf_strs_t * strtab)274f645668Schristos ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab)
286f4ced0bSchristos {
296f4ced0bSchristos   ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
306f4ced0bSchristos 
316f4ced0bSchristos   if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL))
326f4ced0bSchristos     ctsp = strtab;
336f4ced0bSchristos 
346f4ced0bSchristos   /* If this name is in the external strtab, and there is a synthetic strtab,
356f4ced0bSchristos      use it in preference.  */
366f4ced0bSchristos 
376f4ced0bSchristos   if (CTF_NAME_STID (name) == CTF_STRTAB_1
386f4ced0bSchristos       && fp->ctf_syn_ext_strtab != NULL)
396f4ced0bSchristos     return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab,
406f4ced0bSchristos 			       (void *) (uintptr_t) name);
416f4ced0bSchristos 
426f4ced0bSchristos   /* If the name is in the internal strtab, and the offset is beyond the end of
436f4ced0bSchristos      the ctsp->cts_len but below the ctf_str_prov_offset, this is a provisional
446f4ced0bSchristos      string added by ctf_str_add*() but not yet built into a real strtab: get
456f4ced0bSchristos      the value out of the ctf_prov_strtab.  */
466f4ced0bSchristos 
476f4ced0bSchristos   if (CTF_NAME_STID (name) == CTF_STRTAB_0
486f4ced0bSchristos       && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset)
496f4ced0bSchristos       return ctf_dynhash_lookup (fp->ctf_prov_strtab,
506f4ced0bSchristos 				 (void *) (uintptr_t) name);
516f4ced0bSchristos 
526f4ced0bSchristos   if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
536f4ced0bSchristos     return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
546f4ced0bSchristos 
556f4ced0bSchristos   /* String table not loaded or corrupt offset.  */
566f4ced0bSchristos   return NULL;
576f4ced0bSchristos }
586f4ced0bSchristos 
596f4ced0bSchristos /* Convert an encoded CTF string name into a pointer to a C string by looking
606f4ced0bSchristos   up the appropriate string table buffer and then adding the offset.  */
616f4ced0bSchristos const char *
ctf_strraw(ctf_dict_t * fp,uint32_t name)624f645668Schristos ctf_strraw (ctf_dict_t *fp, uint32_t name)
636f4ced0bSchristos {
646f4ced0bSchristos   return ctf_strraw_explicit (fp, name, NULL);
656f4ced0bSchristos }
666f4ced0bSchristos 
676f4ced0bSchristos /* Return a guaranteed-non-NULL pointer to the string with the given CTF
686f4ced0bSchristos    name.  */
696f4ced0bSchristos const char *
ctf_strptr(ctf_dict_t * fp,uint32_t name)704f645668Schristos ctf_strptr (ctf_dict_t *fp, uint32_t name)
716f4ced0bSchristos {
726f4ced0bSchristos   const char *s = ctf_strraw (fp, name);
736f4ced0bSchristos   return (s != NULL ? s : "(?)");
746f4ced0bSchristos }
756f4ced0bSchristos 
766f4ced0bSchristos /* Remove all refs to a given atom.  */
776f4ced0bSchristos static void
ctf_str_purge_atom_refs(ctf_str_atom_t * atom)786f4ced0bSchristos ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
796f4ced0bSchristos {
806f4ced0bSchristos   ctf_str_atom_ref_t *ref, *next;
816f4ced0bSchristos 
826f4ced0bSchristos   for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
836f4ced0bSchristos     {
846f4ced0bSchristos       next = ctf_list_next (ref);
856f4ced0bSchristos       ctf_list_delete (&atom->csa_refs, ref);
866f4ced0bSchristos       free (ref);
876f4ced0bSchristos     }
886f4ced0bSchristos }
896f4ced0bSchristos 
906f4ced0bSchristos /* Free an atom (only called on ctf_close().)  */
916f4ced0bSchristos static void
ctf_str_free_atom(void * a)926f4ced0bSchristos ctf_str_free_atom (void *a)
936f4ced0bSchristos {
946f4ced0bSchristos   ctf_str_atom_t *atom = a;
956f4ced0bSchristos 
966f4ced0bSchristos   ctf_str_purge_atom_refs (atom);
976f4ced0bSchristos   free (atom);
986f4ced0bSchristos }
996f4ced0bSchristos 
1006f4ced0bSchristos /* Create the atoms table.  There is always at least one atom in it, the null
1016f4ced0bSchristos    string.  */
1026f4ced0bSchristos int
ctf_str_create_atoms(ctf_dict_t * fp)1034f645668Schristos ctf_str_create_atoms (ctf_dict_t *fp)
1046f4ced0bSchristos {
1056f4ced0bSchristos   fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
1066f4ced0bSchristos 					  free, ctf_str_free_atom);
1074f645668Schristos   if (!fp->ctf_str_atoms)
1086f4ced0bSchristos     return -ENOMEM;
1096f4ced0bSchristos 
1106f4ced0bSchristos   if (!fp->ctf_prov_strtab)
1116f4ced0bSchristos     fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer,
1126f4ced0bSchristos 					      ctf_hash_eq_integer,
1136f4ced0bSchristos 					      NULL, NULL);
1146f4ced0bSchristos   if (!fp->ctf_prov_strtab)
1156f4ced0bSchristos     goto oom_prov_strtab;
1166f4ced0bSchristos 
1174f645668Schristos   if (!fp->ctf_str_pending_ref)
1184f645668Schristos     fp->ctf_str_pending_ref = ctf_dynset_create (htab_hash_pointer,
1194f645668Schristos 						 htab_eq_pointer,
1204f645668Schristos 						 NULL);
1214f645668Schristos   if (!fp->ctf_str_pending_ref)
1224f645668Schristos     goto oom_str_pending_ref;
1234f645668Schristos 
1246f4ced0bSchristos   errno = 0;
1256f4ced0bSchristos   ctf_str_add (fp, "");
1266f4ced0bSchristos   if (errno == ENOMEM)
1276f4ced0bSchristos     goto oom_str_add;
1286f4ced0bSchristos 
1296f4ced0bSchristos   return 0;
1306f4ced0bSchristos 
1316f4ced0bSchristos  oom_str_add:
1326f4ced0bSchristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
1336f4ced0bSchristos   fp->ctf_prov_strtab = NULL;
1344f645668Schristos  oom_str_pending_ref:
1354f645668Schristos   ctf_dynset_destroy (fp->ctf_str_pending_ref);
1364f645668Schristos   fp->ctf_str_pending_ref = NULL;
1376f4ced0bSchristos  oom_prov_strtab:
1386f4ced0bSchristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
1396f4ced0bSchristos   fp->ctf_str_atoms = NULL;
1406f4ced0bSchristos   return -ENOMEM;
1416f4ced0bSchristos }
1426f4ced0bSchristos 
1436f4ced0bSchristos /* Destroy the atoms table.  */
1446f4ced0bSchristos void
ctf_str_free_atoms(ctf_dict_t * fp)1454f645668Schristos ctf_str_free_atoms (ctf_dict_t *fp)
1466f4ced0bSchristos {
1476f4ced0bSchristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
1486f4ced0bSchristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
1494f645668Schristos   ctf_dynset_destroy (fp->ctf_str_pending_ref);
1506f4ced0bSchristos }
1516f4ced0bSchristos 
1524f645668Schristos #define CTF_STR_ADD_REF 0x1
1534f645668Schristos #define CTF_STR_MAKE_PROVISIONAL 0x2
1544f645668Schristos #define CTF_STR_PENDING_REF 0x4
1554f645668Schristos 
1566f4ced0bSchristos /* Add a string to the atoms table, copying the passed-in string.  Return the
1576f4ced0bSchristos    atom added. Return NULL only when out of memory (and do not touch the
1586f4ced0bSchristos    passed-in string in that case).  Possibly augment the ref list with the
1596f4ced0bSchristos    passed-in ref.  Possibly add a provisional entry for this string to the
1606f4ced0bSchristos    provisional strtab.   */
1616f4ced0bSchristos static ctf_str_atom_t *
ctf_str_add_ref_internal(ctf_dict_t * fp,const char * str,int flags,uint32_t * ref)1624f645668Schristos ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
1634f645668Schristos 			  int flags, uint32_t *ref)
1646f4ced0bSchristos {
1656f4ced0bSchristos   char *newstr = NULL;
1666f4ced0bSchristos   ctf_str_atom_t *atom = NULL;
1676f4ced0bSchristos   ctf_str_atom_ref_t *aref = NULL;
1686f4ced0bSchristos 
1696f4ced0bSchristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
1706f4ced0bSchristos 
1714f645668Schristos   if (flags & CTF_STR_ADD_REF)
1726f4ced0bSchristos     {
173*cb63e24eSchristos       if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL) {
174*cb63e24eSchristos 	ctf_set_errno (fp, ENOMEM);
1756f4ced0bSchristos 	return NULL;
176*cb63e24eSchristos       }
1776f4ced0bSchristos       aref->caf_ref = ref;
1786f4ced0bSchristos     }
1796f4ced0bSchristos 
1806f4ced0bSchristos   if (atom)
1816f4ced0bSchristos     {
1824f645668Schristos       if (flags & CTF_STR_ADD_REF)
1836f4ced0bSchristos 	{
1844f645668Schristos 	  ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
1856f4ced0bSchristos 	  ctf_list_append (&atom->csa_refs, aref);
1866f4ced0bSchristos 	  fp->ctf_str_num_refs++;
1876f4ced0bSchristos 	}
1886f4ced0bSchristos       return atom;
1896f4ced0bSchristos     }
1906f4ced0bSchristos 
1916f4ced0bSchristos   if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
1926f4ced0bSchristos     goto oom;
1936f4ced0bSchristos   memset (atom, 0, sizeof (struct ctf_str_atom));
1946f4ced0bSchristos 
1956f4ced0bSchristos   if ((newstr = strdup (str)) == NULL)
1966f4ced0bSchristos     goto oom;
1976f4ced0bSchristos 
1986f4ced0bSchristos   if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0)
1996f4ced0bSchristos     goto oom;
2006f4ced0bSchristos 
2016f4ced0bSchristos   atom->csa_str = newstr;
2026f4ced0bSchristos   atom->csa_snapshot_id = fp->ctf_snapshots;
2036f4ced0bSchristos 
2044f645668Schristos   if (flags & CTF_STR_MAKE_PROVISIONAL)
2056f4ced0bSchristos     {
2066f4ced0bSchristos       atom->csa_offset = fp->ctf_str_prov_offset;
2076f4ced0bSchristos 
2086f4ced0bSchristos       if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t)
2096f4ced0bSchristos 			      atom->csa_offset, (void *) atom->csa_str) < 0)
2106f4ced0bSchristos 	goto oom;
2116f4ced0bSchristos 
2126f4ced0bSchristos       fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
2136f4ced0bSchristos     }
2146f4ced0bSchristos 
2154f645668Schristos   if (flags & CTF_STR_PENDING_REF)
2166f4ced0bSchristos     {
2174f645668Schristos       if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) ref) < 0)
2184f645668Schristos 	goto oom;
2194f645668Schristos     }
2204f645668Schristos   else if (flags & CTF_STR_ADD_REF)
2214f645668Schristos     {
2224f645668Schristos       ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
2236f4ced0bSchristos       ctf_list_append (&atom->csa_refs, aref);
2246f4ced0bSchristos       fp->ctf_str_num_refs++;
2256f4ced0bSchristos     }
2266f4ced0bSchristos   return atom;
2276f4ced0bSchristos 
2286f4ced0bSchristos  oom:
2296f4ced0bSchristos   if (newstr)
2306f4ced0bSchristos     ctf_dynhash_remove (fp->ctf_str_atoms, newstr);
2316f4ced0bSchristos   free (atom);
2326f4ced0bSchristos   free (aref);
2336f4ced0bSchristos   free (newstr);
2344f645668Schristos   ctf_set_errno (fp, ENOMEM);
2356f4ced0bSchristos   return NULL;
2366f4ced0bSchristos }
2376f4ced0bSchristos 
2386f4ced0bSchristos /* Add a string to the atoms table, without augmenting the ref list for this
2396f4ced0bSchristos    string: return a 'provisional offset' which can be used to return this string
2406f4ced0bSchristos    until ctf_str_write_strtab is called, or 0 on failure.  (Everywhere the
2416f4ced0bSchristos    provisional offset is assigned to should be added as a ref using
2426f4ced0bSchristos    ctf_str_add_ref() as well.) */
2436f4ced0bSchristos uint32_t
ctf_str_add(ctf_dict_t * fp,const char * str)2444f645668Schristos ctf_str_add (ctf_dict_t *fp, const char *str)
2456f4ced0bSchristos {
2466f4ced0bSchristos   ctf_str_atom_t *atom;
2476f4ced0bSchristos 
2484f645668Schristos   if (!str)
2494f645668Schristos     str = "";
2504f645668Schristos 
2514f645668Schristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_MAKE_PROVISIONAL, 0);
2526f4ced0bSchristos   if (!atom)
2536f4ced0bSchristos     return 0;
2546f4ced0bSchristos 
2556f4ced0bSchristos   return atom->csa_offset;
2566f4ced0bSchristos }
2576f4ced0bSchristos 
2586f4ced0bSchristos /* Like ctf_str_add(), but additionally augment the atom's refs list with the
2596f4ced0bSchristos    passed-in ref, whether or not the string is already present.  There is no
2606f4ced0bSchristos    attempt to deduplicate the refs list (but duplicates are harmless).  */
2616f4ced0bSchristos uint32_t
ctf_str_add_ref(ctf_dict_t * fp,const char * str,uint32_t * ref)2624f645668Schristos ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
2636f4ced0bSchristos {
2646f4ced0bSchristos   ctf_str_atom_t *atom;
2656f4ced0bSchristos 
2664f645668Schristos   if (!str)
2674f645668Schristos     str = "";
2684f645668Schristos 
2694f645668Schristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
2704f645668Schristos 				   | CTF_STR_MAKE_PROVISIONAL, ref);
2716f4ced0bSchristos   if (!atom)
2726f4ced0bSchristos     return 0;
2736f4ced0bSchristos 
2746f4ced0bSchristos   return atom->csa_offset;
2756f4ced0bSchristos }
2766f4ced0bSchristos 
2774f645668Schristos /* Like ctf_str_add_ref(), but notes that this memory location must be added as
2784f645668Schristos    a ref by a later serialization phase, rather than adding it itself.  */
2794f645668Schristos uint32_t
ctf_str_add_pending(ctf_dict_t * fp,const char * str,uint32_t * ref)2804f645668Schristos ctf_str_add_pending (ctf_dict_t *fp, const char *str, uint32_t *ref)
2814f645668Schristos {
2824f645668Schristos   ctf_str_atom_t *atom;
2834f645668Schristos 
2844f645668Schristos   if (!str)
2854f645668Schristos     str = "";
2864f645668Schristos 
2874f645668Schristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PENDING_REF
2884f645668Schristos 				   | CTF_STR_MAKE_PROVISIONAL, ref);
2894f645668Schristos   if (!atom)
2904f645668Schristos     return 0;
2914f645668Schristos 
2924f645668Schristos   return atom->csa_offset;
2934f645668Schristos }
2944f645668Schristos 
2954f645668Schristos /* Note that a pending ref now located at NEW_REF has moved by BYTES bytes.  */
2964f645668Schristos int
ctf_str_move_pending(ctf_dict_t * fp,uint32_t * new_ref,ptrdiff_t bytes)2974f645668Schristos ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
2984f645668Schristos {
2994f645668Schristos   if (bytes == 0)
3004f645668Schristos     return 0;
3014f645668Schristos 
3024f645668Schristos   if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
3034f645668Schristos     return (ctf_set_errno (fp, ENOMEM));
3044f645668Schristos 
3054f645668Schristos   ctf_dynset_remove (fp->ctf_str_pending_ref,
3064f645668Schristos 		     (void *) ((signed char *) new_ref - bytes));
3074f645668Schristos   return 0;
3084f645668Schristos }
3094f645668Schristos 
3106f4ced0bSchristos /* Add an external strtab reference at OFFSET.  Returns zero if the addition
3116f4ced0bSchristos    failed, nonzero otherwise.  */
3126f4ced0bSchristos int
ctf_str_add_external(ctf_dict_t * fp,const char * str,uint32_t offset)3134f645668Schristos ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
3146f4ced0bSchristos {
3156f4ced0bSchristos   ctf_str_atom_t *atom;
3166f4ced0bSchristos 
3174f645668Schristos   if (!str)
3184f645668Schristos     str = "";
3194f645668Schristos 
3204f645668Schristos   atom = ctf_str_add_ref_internal (fp, str, 0, 0);
3216f4ced0bSchristos   if (!atom)
3226f4ced0bSchristos     return 0;
3236f4ced0bSchristos 
3246f4ced0bSchristos   atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
3254f645668Schristos 
3264f645668Schristos   if (!fp->ctf_syn_ext_strtab)
3274f645668Schristos     fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
3284f645668Schristos 						 ctf_hash_eq_integer,
3294f645668Schristos 						 NULL, NULL);
3304f645668Schristos   if (!fp->ctf_syn_ext_strtab)
3314f645668Schristos     {
3324f645668Schristos       ctf_set_errno (fp, ENOMEM);
3334f645668Schristos       return 0;
3344f645668Schristos     }
3354f645668Schristos 
3364f645668Schristos   if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
3374f645668Schristos 			  (void *) (uintptr_t)
3384f645668Schristos 			  atom->csa_external_offset,
3394f645668Schristos 			  (void *) atom->csa_str) < 0)
3404f645668Schristos     {
3414f645668Schristos       /* No need to bother freeing the syn_ext_strtab: it will get freed at
3424f645668Schristos 	 ctf_str_write_strtab time if unreferenced.  */
3434f645668Schristos       ctf_set_errno (fp, ENOMEM);
3444f645668Schristos       return 0;
3454f645668Schristos     }
3464f645668Schristos 
3476f4ced0bSchristos   return 1;
3486f4ced0bSchristos }
3496f4ced0bSchristos 
3506f4ced0bSchristos /* Remove a single ref.  */
3516f4ced0bSchristos void
ctf_str_remove_ref(ctf_dict_t * fp,const char * str,uint32_t * ref)3524f645668Schristos ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
3536f4ced0bSchristos {
3546f4ced0bSchristos   ctf_str_atom_ref_t *aref, *anext;
3556f4ced0bSchristos   ctf_str_atom_t *atom = NULL;
3566f4ced0bSchristos 
3576f4ced0bSchristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
3586f4ced0bSchristos   if (!atom)
3596f4ced0bSchristos     return;
3606f4ced0bSchristos 
3616f4ced0bSchristos   for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
3626f4ced0bSchristos     {
3636f4ced0bSchristos       anext = ctf_list_next (aref);
3646f4ced0bSchristos       if (aref->caf_ref == ref)
3656f4ced0bSchristos 	{
3666f4ced0bSchristos 	  ctf_list_delete (&atom->csa_refs, aref);
3676f4ced0bSchristos 	  free (aref);
3686f4ced0bSchristos 	}
3696f4ced0bSchristos     }
3704f645668Schristos 
3714f645668Schristos   ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
3726f4ced0bSchristos }
3736f4ced0bSchristos 
3746f4ced0bSchristos /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
3754f645668Schristos    snapshot ID.  External atoms are never removed, because they came from the
3764f645668Schristos    linker string table and are still present even if you roll back type
3774f645668Schristos    additions.  */
3786f4ced0bSchristos static int
ctf_str_rollback_atom(void * key _libctf_unused_,void * value,void * arg)3796f4ced0bSchristos ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
3806f4ced0bSchristos {
3816f4ced0bSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
3826f4ced0bSchristos   ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
3836f4ced0bSchristos 
3844f645668Schristos   return (atom->csa_snapshot_id > id->snapshot_id)
3854f645668Schristos     && (atom->csa_external_offset == 0);
3866f4ced0bSchristos }
3876f4ced0bSchristos 
3884f645668Schristos /* Roll back, deleting all (internal) atoms created after a particular ID.  */
3896f4ced0bSchristos void
ctf_str_rollback(ctf_dict_t * fp,ctf_snapshot_id_t id)3904f645668Schristos ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
3916f4ced0bSchristos {
3926f4ced0bSchristos   ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
3936f4ced0bSchristos }
3946f4ced0bSchristos 
3956f4ced0bSchristos /* An adaptor around ctf_purge_atom_refs.  */
3966f4ced0bSchristos static void
ctf_str_purge_one_atom_refs(void * key _libctf_unused_,void * value,void * arg _libctf_unused_)3976f4ced0bSchristos ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
3986f4ced0bSchristos 			     void *arg _libctf_unused_)
3996f4ced0bSchristos {
4006f4ced0bSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
4016f4ced0bSchristos   ctf_str_purge_atom_refs (atom);
4026f4ced0bSchristos }
4036f4ced0bSchristos 
4046f4ced0bSchristos /* Remove all the recorded refs from the atoms table.  */
4056f4ced0bSchristos void
ctf_str_purge_refs(ctf_dict_t * fp)4064f645668Schristos ctf_str_purge_refs (ctf_dict_t *fp)
4076f4ced0bSchristos {
4086f4ced0bSchristos   if (fp->ctf_str_num_refs > 0)
4096f4ced0bSchristos     ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
4106f4ced0bSchristos   fp->ctf_str_num_refs = 0;
4116f4ced0bSchristos }
4126f4ced0bSchristos 
4136f4ced0bSchristos /* Update a list of refs to the specified value. */
4146f4ced0bSchristos static void
ctf_str_update_refs(ctf_str_atom_t * refs,uint32_t value)4156f4ced0bSchristos ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
4166f4ced0bSchristos {
4176f4ced0bSchristos   ctf_str_atom_ref_t *ref;
4186f4ced0bSchristos 
4196f4ced0bSchristos   for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
4206f4ced0bSchristos        ref = ctf_list_next (ref))
4216f4ced0bSchristos       *(ref->caf_ref) = value;
4226f4ced0bSchristos }
4236f4ced0bSchristos 
4246f4ced0bSchristos /* State shared across the strtab write process.  */
4256f4ced0bSchristos typedef struct ctf_strtab_write_state
4266f4ced0bSchristos {
4276f4ced0bSchristos   /* Strtab we are writing, and the number of strings in it.  */
4286f4ced0bSchristos   ctf_strs_writable_t *strtab;
4296f4ced0bSchristos   size_t strtab_count;
4306f4ced0bSchristos 
4316f4ced0bSchristos   /* Pointers to (existing) atoms in the atoms table, for qsorting.  */
4326f4ced0bSchristos   ctf_str_atom_t **sorttab;
4336f4ced0bSchristos 
4346f4ced0bSchristos   /* Loop counter for sorttab population.  */
4356f4ced0bSchristos   size_t i;
4366f4ced0bSchristos 
4376f4ced0bSchristos   /* The null-string atom (skipped during population).  */
4386f4ced0bSchristos   ctf_str_atom_t *nullstr;
4396f4ced0bSchristos } ctf_strtab_write_state_t;
4406f4ced0bSchristos 
4416f4ced0bSchristos /* Count the number of entries in the strtab, and its length.  */
4426f4ced0bSchristos static void
ctf_str_count_strtab(void * key _libctf_unused_,void * value,void * arg)4436f4ced0bSchristos ctf_str_count_strtab (void *key _libctf_unused_, void *value,
4446f4ced0bSchristos 	      void *arg)
4456f4ced0bSchristos {
4466f4ced0bSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
4476f4ced0bSchristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
4486f4ced0bSchristos 
4496f4ced0bSchristos   /* We only factor in the length of items that have no offset and have refs:
4506f4ced0bSchristos      other items are in the external strtab, or will simply not be written out
4516f4ced0bSchristos      at all.  They still contribute to the total count, though, because we still
4526f4ced0bSchristos      have to sort them.  We add in the null string's length explicitly, outside
4536f4ced0bSchristos      this function, since it is explicitly written out even if it has no refs at
4546f4ced0bSchristos      all.  */
4556f4ced0bSchristos 
4566f4ced0bSchristos   if (s->nullstr == atom)
4576f4ced0bSchristos     {
4586f4ced0bSchristos       s->strtab_count++;
4596f4ced0bSchristos       return;
4606f4ced0bSchristos     }
4616f4ced0bSchristos 
4626f4ced0bSchristos   if (!ctf_list_empty_p (&atom->csa_refs))
4636f4ced0bSchristos     {
4646f4ced0bSchristos       if (!atom->csa_external_offset)
4656f4ced0bSchristos 	s->strtab->cts_len += strlen (atom->csa_str) + 1;
4666f4ced0bSchristos       s->strtab_count++;
4676f4ced0bSchristos     }
4686f4ced0bSchristos }
4696f4ced0bSchristos 
4706f4ced0bSchristos /* Populate the sorttab with pointers to the strtab atoms.  */
4716f4ced0bSchristos static void
ctf_str_populate_sorttab(void * key _libctf_unused_,void * value,void * arg)4726f4ced0bSchristos ctf_str_populate_sorttab (void *key _libctf_unused_, void *value,
4736f4ced0bSchristos 		  void *arg)
4746f4ced0bSchristos {
4756f4ced0bSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
4766f4ced0bSchristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
4776f4ced0bSchristos 
4786f4ced0bSchristos   /* Skip the null string.  */
4796f4ced0bSchristos   if (s->nullstr == atom)
4806f4ced0bSchristos     return;
4816f4ced0bSchristos 
4826f4ced0bSchristos   /* Skip atoms with no refs.  */
4836f4ced0bSchristos   if (!ctf_list_empty_p (&atom->csa_refs))
4846f4ced0bSchristos     s->sorttab[s->i++] = atom;
4856f4ced0bSchristos }
4866f4ced0bSchristos 
4876f4ced0bSchristos /* Sort the strtab.  */
4886f4ced0bSchristos static int
ctf_str_sort_strtab(const void * a,const void * b)4896f4ced0bSchristos ctf_str_sort_strtab (const void *a, const void *b)
4906f4ced0bSchristos {
4916f4ced0bSchristos   ctf_str_atom_t **one = (ctf_str_atom_t **) a;
4926f4ced0bSchristos   ctf_str_atom_t **two = (ctf_str_atom_t **) b;
4936f4ced0bSchristos 
4946f4ced0bSchristos   return (strcmp ((*one)->csa_str, (*two)->csa_str));
4956f4ced0bSchristos }
4966f4ced0bSchristos 
4976f4ced0bSchristos /* Write out and return a strtab containing all strings with recorded refs,
4986f4ced0bSchristos    adjusting the refs to refer to the corresponding string.  The returned strtab
4996f4ced0bSchristos    may be NULL on error.  Also populate the synthetic strtab with mappings from
5006f4ced0bSchristos    external strtab offsets to names, so we can look them up with ctf_strptr().
5016f4ced0bSchristos    Only external strtab offsets with references are added.  */
5026f4ced0bSchristos ctf_strs_writable_t
ctf_str_write_strtab(ctf_dict_t * fp)5034f645668Schristos ctf_str_write_strtab (ctf_dict_t *fp)
5046f4ced0bSchristos {
5056f4ced0bSchristos   ctf_strs_writable_t strtab;
5066f4ced0bSchristos   ctf_str_atom_t *nullstr;
5076f4ced0bSchristos   uint32_t cur_stroff = 0;
5086f4ced0bSchristos   ctf_strtab_write_state_t s;
5096f4ced0bSchristos   ctf_str_atom_t **sorttab;
5106f4ced0bSchristos   size_t i;
5116f4ced0bSchristos   int any_external = 0;
5126f4ced0bSchristos 
5136f4ced0bSchristos   memset (&strtab, 0, sizeof (struct ctf_strs_writable));
5146f4ced0bSchristos   memset (&s, 0, sizeof (struct ctf_strtab_write_state));
5156f4ced0bSchristos   s.strtab = &strtab;
5166f4ced0bSchristos 
5176f4ced0bSchristos   nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
5186f4ced0bSchristos   if (!nullstr)
5196f4ced0bSchristos     {
5204f645668Schristos       ctf_err_warn (fp, 0, ECTF_INTERNAL, _("null string not found in strtab"));
5216f4ced0bSchristos       strtab.cts_strs = NULL;
5226f4ced0bSchristos       return strtab;
5236f4ced0bSchristos     }
5246f4ced0bSchristos 
5256f4ced0bSchristos   s.nullstr = nullstr;
5266f4ced0bSchristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_count_strtab, &s);
5276f4ced0bSchristos   strtab.cts_len++;				/* For the null string.  */
5286f4ced0bSchristos 
5296f4ced0bSchristos   ctf_dprintf ("%lu bytes of strings in strtab.\n",
5306f4ced0bSchristos 	       (unsigned long) strtab.cts_len);
5316f4ced0bSchristos 
5326f4ced0bSchristos   /* Sort the strtab.  Force the null string to be first.  */
5336f4ced0bSchristos   sorttab = calloc (s.strtab_count, sizeof (ctf_str_atom_t *));
5346f4ced0bSchristos   if (!sorttab)
5356f4ced0bSchristos     goto oom;
5366f4ced0bSchristos 
5376f4ced0bSchristos   sorttab[0] = nullstr;
5386f4ced0bSchristos   s.i = 1;
5396f4ced0bSchristos   s.sorttab = sorttab;
5406f4ced0bSchristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_populate_sorttab, &s);
5416f4ced0bSchristos 
5426f4ced0bSchristos   qsort (&sorttab[1], s.strtab_count - 1, sizeof (ctf_str_atom_t *),
5436f4ced0bSchristos 	 ctf_str_sort_strtab);
5446f4ced0bSchristos 
5456f4ced0bSchristos   if ((strtab.cts_strs = malloc (strtab.cts_len)) == NULL)
5466f4ced0bSchristos     goto oom_sorttab;
5476f4ced0bSchristos 
5486f4ced0bSchristos   /* Update all refs: also update the strtab appropriately.  */
5496f4ced0bSchristos   for (i = 0; i < s.strtab_count; i++)
5506f4ced0bSchristos     {
5516f4ced0bSchristos       if (sorttab[i]->csa_external_offset)
5526f4ced0bSchristos 	{
5534f645668Schristos 	  /* External strtab entry.  */
5546f4ced0bSchristos 
5556f4ced0bSchristos 	  any_external = 1;
5566f4ced0bSchristos 	  ctf_str_update_refs (sorttab[i], sorttab[i]->csa_external_offset);
5576f4ced0bSchristos 	  sorttab[i]->csa_offset = sorttab[i]->csa_external_offset;
5586f4ced0bSchristos 	}
5596f4ced0bSchristos       else
5606f4ced0bSchristos 	{
5616f4ced0bSchristos 	  /* Internal strtab entry with refs: actually add to the string
5626f4ced0bSchristos 	     table.  */
5636f4ced0bSchristos 
5646f4ced0bSchristos 	  ctf_str_update_refs (sorttab[i], cur_stroff);
5656f4ced0bSchristos 	  sorttab[i]->csa_offset = cur_stroff;
5666f4ced0bSchristos 	  strcpy (&strtab.cts_strs[cur_stroff], sorttab[i]->csa_str);
5676f4ced0bSchristos 	  cur_stroff += strlen (sorttab[i]->csa_str) + 1;
5686f4ced0bSchristos 	}
5696f4ced0bSchristos     }
5706f4ced0bSchristos   free (sorttab);
5716f4ced0bSchristos 
5726f4ced0bSchristos   if (!any_external)
5736f4ced0bSchristos     {
5746f4ced0bSchristos       ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
5756f4ced0bSchristos       fp->ctf_syn_ext_strtab = NULL;
5766f4ced0bSchristos     }
5776f4ced0bSchristos 
5786f4ced0bSchristos   /* All the provisional strtab entries are now real strtab entries, and
5796f4ced0bSchristos      ctf_strptr() will find them there.  The provisional offset now starts right
5806f4ced0bSchristos      beyond the new end of the strtab.  */
5816f4ced0bSchristos 
5826f4ced0bSchristos   ctf_dynhash_empty (fp->ctf_prov_strtab);
5836f4ced0bSchristos   fp->ctf_str_prov_offset = strtab.cts_len + 1;
5846f4ced0bSchristos   return strtab;
5856f4ced0bSchristos 
5866f4ced0bSchristos  oom_sorttab:
5876f4ced0bSchristos   free (sorttab);
5886f4ced0bSchristos  oom:
5896f4ced0bSchristos   return strtab;
5906f4ced0bSchristos }
591