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