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