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