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