1*869ffda3Schristos /* CTF string table management.
2*869ffda3Schristos    Copyright (C) 2019-2020 Free Software Foundation, Inc.
3*869ffda3Schristos 
4*869ffda3Schristos    This file is part of libctf.
5*869ffda3Schristos 
6*869ffda3Schristos    libctf is free software; you can redistribute it and/or modify it under
7*869ffda3Schristos    the terms of the GNU General Public License as published by the Free
8*869ffda3Schristos    Software Foundation; either version 3, or (at your option) any later
9*869ffda3Schristos    version.
10*869ffda3Schristos 
11*869ffda3Schristos    This program is distributed in the hope that it will be useful, but
12*869ffda3Schristos    WITHOUT ANY WARRANTY; without even the implied warranty of
13*869ffda3Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14*869ffda3Schristos    See the GNU General Public License for more details.
15*869ffda3Schristos 
16*869ffda3Schristos    You should have received a copy of the GNU General Public License
17*869ffda3Schristos    along with this program; see the file COPYING.  If not see
18*869ffda3Schristos    <http://www.gnu.org/licenses/>.  */
19*869ffda3Schristos 
20*869ffda3Schristos #include <ctf-impl.h>
21*869ffda3Schristos #include <string.h>
22*869ffda3Schristos 
23*869ffda3Schristos /* Convert an encoded CTF string name into a pointer to a C string, using an
24*869ffda3Schristos   explicit internal strtab rather than the fp-based one.  */
25*869ffda3Schristos const char *
ctf_strraw_explicit(ctf_file_t * fp,uint32_t name,ctf_strs_t * strtab)26*869ffda3Schristos ctf_strraw_explicit (ctf_file_t *fp, uint32_t name, ctf_strs_t *strtab)
27*869ffda3Schristos {
28*869ffda3Schristos   ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
29*869ffda3Schristos 
30*869ffda3Schristos   if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL))
31*869ffda3Schristos     ctsp = strtab;
32*869ffda3Schristos 
33*869ffda3Schristos   /* If this name is in the external strtab, and there is a synthetic strtab,
34*869ffda3Schristos      use it in preference.  */
35*869ffda3Schristos 
36*869ffda3Schristos   if (CTF_NAME_STID (name) == CTF_STRTAB_1
37*869ffda3Schristos       && fp->ctf_syn_ext_strtab != NULL)
38*869ffda3Schristos     return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab,
39*869ffda3Schristos 			       (void *) (uintptr_t) name);
40*869ffda3Schristos 
41*869ffda3Schristos   /* If the name is in the internal strtab, and the offset is beyond the end of
42*869ffda3Schristos      the ctsp->cts_len but below the ctf_str_prov_offset, this is a provisional
43*869ffda3Schristos      string added by ctf_str_add*() but not yet built into a real strtab: get
44*869ffda3Schristos      the value out of the ctf_prov_strtab.  */
45*869ffda3Schristos 
46*869ffda3Schristos   if (CTF_NAME_STID (name) == CTF_STRTAB_0
47*869ffda3Schristos       && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset)
48*869ffda3Schristos       return ctf_dynhash_lookup (fp->ctf_prov_strtab,
49*869ffda3Schristos 				 (void *) (uintptr_t) name);
50*869ffda3Schristos 
51*869ffda3Schristos   if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
52*869ffda3Schristos     return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
53*869ffda3Schristos 
54*869ffda3Schristos   /* String table not loaded or corrupt offset.  */
55*869ffda3Schristos   return NULL;
56*869ffda3Schristos }
57*869ffda3Schristos 
58*869ffda3Schristos /* Convert an encoded CTF string name into a pointer to a C string by looking
59*869ffda3Schristos   up the appropriate string table buffer and then adding the offset.  */
60*869ffda3Schristos const char *
ctf_strraw(ctf_file_t * fp,uint32_t name)61*869ffda3Schristos ctf_strraw (ctf_file_t *fp, uint32_t name)
62*869ffda3Schristos {
63*869ffda3Schristos   return ctf_strraw_explicit (fp, name, NULL);
64*869ffda3Schristos }
65*869ffda3Schristos 
66*869ffda3Schristos /* Return a guaranteed-non-NULL pointer to the string with the given CTF
67*869ffda3Schristos    name.  */
68*869ffda3Schristos const char *
ctf_strptr(ctf_file_t * fp,uint32_t name)69*869ffda3Schristos ctf_strptr (ctf_file_t *fp, uint32_t name)
70*869ffda3Schristos {
71*869ffda3Schristos   const char *s = ctf_strraw (fp, name);
72*869ffda3Schristos   return (s != NULL ? s : "(?)");
73*869ffda3Schristos }
74*869ffda3Schristos 
75*869ffda3Schristos /* Remove all refs to a given atom.  */
76*869ffda3Schristos static void
ctf_str_purge_atom_refs(ctf_str_atom_t * atom)77*869ffda3Schristos ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
78*869ffda3Schristos {
79*869ffda3Schristos   ctf_str_atom_ref_t *ref, *next;
80*869ffda3Schristos 
81*869ffda3Schristos   for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
82*869ffda3Schristos     {
83*869ffda3Schristos       next = ctf_list_next (ref);
84*869ffda3Schristos       ctf_list_delete (&atom->csa_refs, ref);
85*869ffda3Schristos       free (ref);
86*869ffda3Schristos     }
87*869ffda3Schristos }
88*869ffda3Schristos 
89*869ffda3Schristos /* Free an atom (only called on ctf_close().)  */
90*869ffda3Schristos static void
ctf_str_free_atom(void * a)91*869ffda3Schristos ctf_str_free_atom (void *a)
92*869ffda3Schristos {
93*869ffda3Schristos   ctf_str_atom_t *atom = a;
94*869ffda3Schristos 
95*869ffda3Schristos   ctf_str_purge_atom_refs (atom);
96*869ffda3Schristos   free (atom);
97*869ffda3Schristos }
98*869ffda3Schristos 
99*869ffda3Schristos /* Create the atoms table.  There is always at least one atom in it, the null
100*869ffda3Schristos    string.  */
101*869ffda3Schristos int
ctf_str_create_atoms(ctf_file_t * fp)102*869ffda3Schristos ctf_str_create_atoms (ctf_file_t *fp)
103*869ffda3Schristos {
104*869ffda3Schristos   fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
105*869ffda3Schristos 					  free, ctf_str_free_atom);
106*869ffda3Schristos   if (fp->ctf_str_atoms == NULL)
107*869ffda3Schristos     return -ENOMEM;
108*869ffda3Schristos 
109*869ffda3Schristos   if (!fp->ctf_prov_strtab)
110*869ffda3Schristos     fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer,
111*869ffda3Schristos 					      ctf_hash_eq_integer,
112*869ffda3Schristos 					      NULL, NULL);
113*869ffda3Schristos   if (!fp->ctf_prov_strtab)
114*869ffda3Schristos     goto oom_prov_strtab;
115*869ffda3Schristos 
116*869ffda3Schristos   errno = 0;
117*869ffda3Schristos   ctf_str_add (fp, "");
118*869ffda3Schristos   if (errno == ENOMEM)
119*869ffda3Schristos     goto oom_str_add;
120*869ffda3Schristos 
121*869ffda3Schristos   return 0;
122*869ffda3Schristos 
123*869ffda3Schristos  oom_str_add:
124*869ffda3Schristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
125*869ffda3Schristos   fp->ctf_prov_strtab = NULL;
126*869ffda3Schristos  oom_prov_strtab:
127*869ffda3Schristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
128*869ffda3Schristos   fp->ctf_str_atoms = NULL;
129*869ffda3Schristos   return -ENOMEM;
130*869ffda3Schristos }
131*869ffda3Schristos 
132*869ffda3Schristos /* Destroy the atoms table.  */
133*869ffda3Schristos void
ctf_str_free_atoms(ctf_file_t * fp)134*869ffda3Schristos ctf_str_free_atoms (ctf_file_t *fp)
135*869ffda3Schristos {
136*869ffda3Schristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
137*869ffda3Schristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
138*869ffda3Schristos }
139*869ffda3Schristos 
140*869ffda3Schristos /* Add a string to the atoms table, copying the passed-in string.  Return the
141*869ffda3Schristos    atom added. Return NULL only when out of memory (and do not touch the
142*869ffda3Schristos    passed-in string in that case).  Possibly augment the ref list with the
143*869ffda3Schristos    passed-in ref.  Possibly add a provisional entry for this string to the
144*869ffda3Schristos    provisional strtab.   */
145*869ffda3Schristos static ctf_str_atom_t *
ctf_str_add_ref_internal(ctf_file_t * fp,const char * str,int add_ref,int make_provisional,uint32_t * ref)146*869ffda3Schristos ctf_str_add_ref_internal (ctf_file_t *fp, const char *str,
147*869ffda3Schristos 			  int add_ref, int make_provisional, uint32_t *ref)
148*869ffda3Schristos {
149*869ffda3Schristos   char *newstr = NULL;
150*869ffda3Schristos   ctf_str_atom_t *atom = NULL;
151*869ffda3Schristos   ctf_str_atom_ref_t *aref = NULL;
152*869ffda3Schristos 
153*869ffda3Schristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
154*869ffda3Schristos 
155*869ffda3Schristos   if (add_ref)
156*869ffda3Schristos     {
157*869ffda3Schristos       if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL)
158*869ffda3Schristos 	return NULL;
159*869ffda3Schristos       aref->caf_ref = ref;
160*869ffda3Schristos     }
161*869ffda3Schristos 
162*869ffda3Schristos   if (atom)
163*869ffda3Schristos     {
164*869ffda3Schristos       if (add_ref)
165*869ffda3Schristos 	{
166*869ffda3Schristos 	  ctf_list_append (&atom->csa_refs, aref);
167*869ffda3Schristos 	  fp->ctf_str_num_refs++;
168*869ffda3Schristos 	}
169*869ffda3Schristos       return atom;
170*869ffda3Schristos     }
171*869ffda3Schristos 
172*869ffda3Schristos   if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
173*869ffda3Schristos     goto oom;
174*869ffda3Schristos   memset (atom, 0, sizeof (struct ctf_str_atom));
175*869ffda3Schristos 
176*869ffda3Schristos   if ((newstr = strdup (str)) == NULL)
177*869ffda3Schristos     goto oom;
178*869ffda3Schristos 
179*869ffda3Schristos   if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0)
180*869ffda3Schristos     goto oom;
181*869ffda3Schristos 
182*869ffda3Schristos   atom->csa_str = newstr;
183*869ffda3Schristos   atom->csa_snapshot_id = fp->ctf_snapshots;
184*869ffda3Schristos 
185*869ffda3Schristos   if (make_provisional)
186*869ffda3Schristos     {
187*869ffda3Schristos       atom->csa_offset = fp->ctf_str_prov_offset;
188*869ffda3Schristos 
189*869ffda3Schristos       if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t)
190*869ffda3Schristos 			      atom->csa_offset, (void *) atom->csa_str) < 0)
191*869ffda3Schristos 	goto oom;
192*869ffda3Schristos 
193*869ffda3Schristos       fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
194*869ffda3Schristos     }
195*869ffda3Schristos 
196*869ffda3Schristos   if (add_ref)
197*869ffda3Schristos     {
198*869ffda3Schristos       ctf_list_append (&atom->csa_refs, aref);
199*869ffda3Schristos       fp->ctf_str_num_refs++;
200*869ffda3Schristos     }
201*869ffda3Schristos   return atom;
202*869ffda3Schristos 
203*869ffda3Schristos  oom:
204*869ffda3Schristos   if (newstr)
205*869ffda3Schristos     ctf_dynhash_remove (fp->ctf_str_atoms, newstr);
206*869ffda3Schristos   free (atom);
207*869ffda3Schristos   free (aref);
208*869ffda3Schristos   free (newstr);
209*869ffda3Schristos   return NULL;
210*869ffda3Schristos }
211*869ffda3Schristos 
212*869ffda3Schristos /* Add a string to the atoms table, without augmenting the ref list for this
213*869ffda3Schristos    string: return a 'provisional offset' which can be used to return this string
214*869ffda3Schristos    until ctf_str_write_strtab is called, or 0 on failure.  (Everywhere the
215*869ffda3Schristos    provisional offset is assigned to should be added as a ref using
216*869ffda3Schristos    ctf_str_add_ref() as well.) */
217*869ffda3Schristos uint32_t
ctf_str_add(ctf_file_t * fp,const char * str)218*869ffda3Schristos ctf_str_add (ctf_file_t *fp, const char *str)
219*869ffda3Schristos {
220*869ffda3Schristos   ctf_str_atom_t *atom;
221*869ffda3Schristos   if (!str)
222*869ffda3Schristos     return 0;
223*869ffda3Schristos 
224*869ffda3Schristos   atom = ctf_str_add_ref_internal (fp, str, FALSE, TRUE, 0);
225*869ffda3Schristos   if (!atom)
226*869ffda3Schristos     return 0;
227*869ffda3Schristos 
228*869ffda3Schristos   return atom->csa_offset;
229*869ffda3Schristos }
230*869ffda3Schristos 
231*869ffda3Schristos /* Like ctf_str_add(), but additionally augment the atom's refs list with the
232*869ffda3Schristos    passed-in ref, whether or not the string is already present.  There is no
233*869ffda3Schristos    attempt to deduplicate the refs list (but duplicates are harmless).  */
234*869ffda3Schristos uint32_t
ctf_str_add_ref(ctf_file_t * fp,const char * str,uint32_t * ref)235*869ffda3Schristos ctf_str_add_ref (ctf_file_t *fp, const char *str, uint32_t *ref)
236*869ffda3Schristos {
237*869ffda3Schristos   ctf_str_atom_t *atom;
238*869ffda3Schristos   if (!str)
239*869ffda3Schristos     return 0;
240*869ffda3Schristos 
241*869ffda3Schristos   atom = ctf_str_add_ref_internal (fp, str, TRUE, TRUE, ref);
242*869ffda3Schristos   if (!atom)
243*869ffda3Schristos     return 0;
244*869ffda3Schristos 
245*869ffda3Schristos   return atom->csa_offset;
246*869ffda3Schristos }
247*869ffda3Schristos 
248*869ffda3Schristos /* Add an external strtab reference at OFFSET.  Returns zero if the addition
249*869ffda3Schristos    failed, nonzero otherwise.  */
250*869ffda3Schristos int
ctf_str_add_external(ctf_file_t * fp,const char * str,uint32_t offset)251*869ffda3Schristos ctf_str_add_external (ctf_file_t *fp, const char *str, uint32_t offset)
252*869ffda3Schristos {
253*869ffda3Schristos   ctf_str_atom_t *atom;
254*869ffda3Schristos   if (!str)
255*869ffda3Schristos     return 0;
256*869ffda3Schristos 
257*869ffda3Schristos   atom = ctf_str_add_ref_internal (fp, str, FALSE, FALSE, 0);
258*869ffda3Schristos   if (!atom)
259*869ffda3Schristos     return 0;
260*869ffda3Schristos 
261*869ffda3Schristos   atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
262*869ffda3Schristos   return 1;
263*869ffda3Schristos }
264*869ffda3Schristos 
265*869ffda3Schristos /* Remove a single ref.  */
266*869ffda3Schristos void
ctf_str_remove_ref(ctf_file_t * fp,const char * str,uint32_t * ref)267*869ffda3Schristos ctf_str_remove_ref (ctf_file_t *fp, const char *str, uint32_t *ref)
268*869ffda3Schristos {
269*869ffda3Schristos   ctf_str_atom_ref_t *aref, *anext;
270*869ffda3Schristos   ctf_str_atom_t *atom = NULL;
271*869ffda3Schristos 
272*869ffda3Schristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
273*869ffda3Schristos   if (!atom)
274*869ffda3Schristos     return;
275*869ffda3Schristos 
276*869ffda3Schristos   for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
277*869ffda3Schristos     {
278*869ffda3Schristos       anext = ctf_list_next (aref);
279*869ffda3Schristos       if (aref->caf_ref == ref)
280*869ffda3Schristos 	{
281*869ffda3Schristos 	  ctf_list_delete (&atom->csa_refs, aref);
282*869ffda3Schristos 	  free (aref);
283*869ffda3Schristos 	}
284*869ffda3Schristos     }
285*869ffda3Schristos }
286*869ffda3Schristos 
287*869ffda3Schristos /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
288*869ffda3Schristos    snapshot ID.  */
289*869ffda3Schristos static int
ctf_str_rollback_atom(void * key _libctf_unused_,void * value,void * arg)290*869ffda3Schristos ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
291*869ffda3Schristos {
292*869ffda3Schristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
293*869ffda3Schristos   ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
294*869ffda3Schristos 
295*869ffda3Schristos   return (atom->csa_snapshot_id > id->snapshot_id);
296*869ffda3Schristos }
297*869ffda3Schristos 
298*869ffda3Schristos /* Roll back, deleting all atoms created after a particular ID.  */
299*869ffda3Schristos void
ctf_str_rollback(ctf_file_t * fp,ctf_snapshot_id_t id)300*869ffda3Schristos ctf_str_rollback (ctf_file_t *fp, ctf_snapshot_id_t id)
301*869ffda3Schristos {
302*869ffda3Schristos   ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
303*869ffda3Schristos }
304*869ffda3Schristos 
305*869ffda3Schristos /* An adaptor around ctf_purge_atom_refs.  */
306*869ffda3Schristos static void
ctf_str_purge_one_atom_refs(void * key _libctf_unused_,void * value,void * arg _libctf_unused_)307*869ffda3Schristos ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
308*869ffda3Schristos 			     void *arg _libctf_unused_)
309*869ffda3Schristos {
310*869ffda3Schristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
311*869ffda3Schristos   ctf_str_purge_atom_refs (atom);
312*869ffda3Schristos }
313*869ffda3Schristos 
314*869ffda3Schristos /* Remove all the recorded refs from the atoms table.  */
315*869ffda3Schristos void
ctf_str_purge_refs(ctf_file_t * fp)316*869ffda3Schristos ctf_str_purge_refs (ctf_file_t *fp)
317*869ffda3Schristos {
318*869ffda3Schristos   if (fp->ctf_str_num_refs > 0)
319*869ffda3Schristos     ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
320*869ffda3Schristos   fp->ctf_str_num_refs = 0;
321*869ffda3Schristos }
322*869ffda3Schristos 
323*869ffda3Schristos /* Update a list of refs to the specified value. */
324*869ffda3Schristos static void
ctf_str_update_refs(ctf_str_atom_t * refs,uint32_t value)325*869ffda3Schristos ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
326*869ffda3Schristos {
327*869ffda3Schristos   ctf_str_atom_ref_t *ref;
328*869ffda3Schristos 
329*869ffda3Schristos   for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
330*869ffda3Schristos        ref = ctf_list_next (ref))
331*869ffda3Schristos       *(ref->caf_ref) = value;
332*869ffda3Schristos }
333*869ffda3Schristos 
334*869ffda3Schristos /* State shared across the strtab write process.  */
335*869ffda3Schristos typedef struct ctf_strtab_write_state
336*869ffda3Schristos {
337*869ffda3Schristos   /* Strtab we are writing, and the number of strings in it.  */
338*869ffda3Schristos   ctf_strs_writable_t *strtab;
339*869ffda3Schristos   size_t strtab_count;
340*869ffda3Schristos 
341*869ffda3Schristos   /* Pointers to (existing) atoms in the atoms table, for qsorting.  */
342*869ffda3Schristos   ctf_str_atom_t **sorttab;
343*869ffda3Schristos 
344*869ffda3Schristos   /* Loop counter for sorttab population.  */
345*869ffda3Schristos   size_t i;
346*869ffda3Schristos 
347*869ffda3Schristos   /* The null-string atom (skipped during population).  */
348*869ffda3Schristos   ctf_str_atom_t *nullstr;
349*869ffda3Schristos } ctf_strtab_write_state_t;
350*869ffda3Schristos 
351*869ffda3Schristos /* Count the number of entries in the strtab, and its length.  */
352*869ffda3Schristos static void
ctf_str_count_strtab(void * key _libctf_unused_,void * value,void * arg)353*869ffda3Schristos ctf_str_count_strtab (void *key _libctf_unused_, void *value,
354*869ffda3Schristos 	      void *arg)
355*869ffda3Schristos {
356*869ffda3Schristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
357*869ffda3Schristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
358*869ffda3Schristos 
359*869ffda3Schristos   /* We only factor in the length of items that have no offset and have refs:
360*869ffda3Schristos      other items are in the external strtab, or will simply not be written out
361*869ffda3Schristos      at all.  They still contribute to the total count, though, because we still
362*869ffda3Schristos      have to sort them.  We add in the null string's length explicitly, outside
363*869ffda3Schristos      this function, since it is explicitly written out even if it has no refs at
364*869ffda3Schristos      all.  */
365*869ffda3Schristos 
366*869ffda3Schristos   if (s->nullstr == atom)
367*869ffda3Schristos     {
368*869ffda3Schristos       s->strtab_count++;
369*869ffda3Schristos       return;
370*869ffda3Schristos     }
371*869ffda3Schristos 
372*869ffda3Schristos   if (!ctf_list_empty_p (&atom->csa_refs))
373*869ffda3Schristos     {
374*869ffda3Schristos       if (!atom->csa_external_offset)
375*869ffda3Schristos 	s->strtab->cts_len += strlen (atom->csa_str) + 1;
376*869ffda3Schristos       s->strtab_count++;
377*869ffda3Schristos     }
378*869ffda3Schristos }
379*869ffda3Schristos 
380*869ffda3Schristos /* Populate the sorttab with pointers to the strtab atoms.  */
381*869ffda3Schristos static void
ctf_str_populate_sorttab(void * key _libctf_unused_,void * value,void * arg)382*869ffda3Schristos ctf_str_populate_sorttab (void *key _libctf_unused_, void *value,
383*869ffda3Schristos 		  void *arg)
384*869ffda3Schristos {
385*869ffda3Schristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
386*869ffda3Schristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
387*869ffda3Schristos 
388*869ffda3Schristos   /* Skip the null string.  */
389*869ffda3Schristos   if (s->nullstr == atom)
390*869ffda3Schristos     return;
391*869ffda3Schristos 
392*869ffda3Schristos   /* Skip atoms with no refs.  */
393*869ffda3Schristos   if (!ctf_list_empty_p (&atom->csa_refs))
394*869ffda3Schristos     s->sorttab[s->i++] = atom;
395*869ffda3Schristos }
396*869ffda3Schristos 
397*869ffda3Schristos /* Sort the strtab.  */
398*869ffda3Schristos static int
ctf_str_sort_strtab(const void * a,const void * b)399*869ffda3Schristos ctf_str_sort_strtab (const void *a, const void *b)
400*869ffda3Schristos {
401*869ffda3Schristos   ctf_str_atom_t **one = (ctf_str_atom_t **) a;
402*869ffda3Schristos   ctf_str_atom_t **two = (ctf_str_atom_t **) b;
403*869ffda3Schristos 
404*869ffda3Schristos   return (strcmp ((*one)->csa_str, (*two)->csa_str));
405*869ffda3Schristos }
406*869ffda3Schristos 
407*869ffda3Schristos /* Write out and return a strtab containing all strings with recorded refs,
408*869ffda3Schristos    adjusting the refs to refer to the corresponding string.  The returned strtab
409*869ffda3Schristos    may be NULL on error.  Also populate the synthetic strtab with mappings from
410*869ffda3Schristos    external strtab offsets to names, so we can look them up with ctf_strptr().
411*869ffda3Schristos    Only external strtab offsets with references are added.  */
412*869ffda3Schristos ctf_strs_writable_t
ctf_str_write_strtab(ctf_file_t * fp)413*869ffda3Schristos ctf_str_write_strtab (ctf_file_t *fp)
414*869ffda3Schristos {
415*869ffda3Schristos   ctf_strs_writable_t strtab;
416*869ffda3Schristos   ctf_str_atom_t *nullstr;
417*869ffda3Schristos   uint32_t cur_stroff = 0;
418*869ffda3Schristos   ctf_strtab_write_state_t s;
419*869ffda3Schristos   ctf_str_atom_t **sorttab;
420*869ffda3Schristos   size_t i;
421*869ffda3Schristos   int any_external = 0;
422*869ffda3Schristos 
423*869ffda3Schristos   memset (&strtab, 0, sizeof (struct ctf_strs_writable));
424*869ffda3Schristos   memset (&s, 0, sizeof (struct ctf_strtab_write_state));
425*869ffda3Schristos   s.strtab = &strtab;
426*869ffda3Schristos 
427*869ffda3Schristos   nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
428*869ffda3Schristos   if (!nullstr)
429*869ffda3Schristos     {
430*869ffda3Schristos       ctf_dprintf ("Internal error: null string not found in strtab.\n");
431*869ffda3Schristos       strtab.cts_strs = NULL;
432*869ffda3Schristos       return strtab;
433*869ffda3Schristos     }
434*869ffda3Schristos 
435*869ffda3Schristos   s.nullstr = nullstr;
436*869ffda3Schristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_count_strtab, &s);
437*869ffda3Schristos   strtab.cts_len++;				/* For the null string.  */
438*869ffda3Schristos 
439*869ffda3Schristos   ctf_dprintf ("%lu bytes of strings in strtab.\n",
440*869ffda3Schristos 	       (unsigned long) strtab.cts_len);
441*869ffda3Schristos 
442*869ffda3Schristos   /* Sort the strtab.  Force the null string to be first.  */
443*869ffda3Schristos   sorttab = calloc (s.strtab_count, sizeof (ctf_str_atom_t *));
444*869ffda3Schristos   if (!sorttab)
445*869ffda3Schristos     goto oom;
446*869ffda3Schristos 
447*869ffda3Schristos   sorttab[0] = nullstr;
448*869ffda3Schristos   s.i = 1;
449*869ffda3Schristos   s.sorttab = sorttab;
450*869ffda3Schristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_populate_sorttab, &s);
451*869ffda3Schristos 
452*869ffda3Schristos   qsort (&sorttab[1], s.strtab_count - 1, sizeof (ctf_str_atom_t *),
453*869ffda3Schristos 	 ctf_str_sort_strtab);
454*869ffda3Schristos 
455*869ffda3Schristos   if ((strtab.cts_strs = malloc (strtab.cts_len)) == NULL)
456*869ffda3Schristos     goto oom_sorttab;
457*869ffda3Schristos 
458*869ffda3Schristos   if (!fp->ctf_syn_ext_strtab)
459*869ffda3Schristos     fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
460*869ffda3Schristos 						 ctf_hash_eq_integer,
461*869ffda3Schristos 						 NULL, NULL);
462*869ffda3Schristos   if (!fp->ctf_syn_ext_strtab)
463*869ffda3Schristos     goto oom_strtab;
464*869ffda3Schristos 
465*869ffda3Schristos   /* Update all refs: also update the strtab appropriately.  */
466*869ffda3Schristos   for (i = 0; i < s.strtab_count; i++)
467*869ffda3Schristos     {
468*869ffda3Schristos       if (sorttab[i]->csa_external_offset)
469*869ffda3Schristos 	{
470*869ffda3Schristos 	  /* External strtab entry: populate the synthetic external strtab.
471*869ffda3Schristos 
472*869ffda3Schristos 	     This is safe because you cannot ctf_rollback to before the point
473*869ffda3Schristos 	     when a ctf_update is done, and the strtab is written at ctf_update
474*869ffda3Schristos 	     time.  So any atoms we reference here are sure to stick around
475*869ffda3Schristos 	     until ctf_file_close.  */
476*869ffda3Schristos 
477*869ffda3Schristos 	  any_external = 1;
478*869ffda3Schristos 	  ctf_str_update_refs (sorttab[i], sorttab[i]->csa_external_offset);
479*869ffda3Schristos 	  if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
480*869ffda3Schristos 				  (void *) (uintptr_t)
481*869ffda3Schristos 				  sorttab[i]->csa_external_offset,
482*869ffda3Schristos 				  (void *) sorttab[i]->csa_str) < 0)
483*869ffda3Schristos 	    goto oom_strtab;
484*869ffda3Schristos 	  sorttab[i]->csa_offset = sorttab[i]->csa_external_offset;
485*869ffda3Schristos 	}
486*869ffda3Schristos       else
487*869ffda3Schristos 	{
488*869ffda3Schristos 	  /* Internal strtab entry with refs: actually add to the string
489*869ffda3Schristos 	     table.  */
490*869ffda3Schristos 
491*869ffda3Schristos 	  ctf_str_update_refs (sorttab[i], cur_stroff);
492*869ffda3Schristos 	  sorttab[i]->csa_offset = cur_stroff;
493*869ffda3Schristos 	  strcpy (&strtab.cts_strs[cur_stroff], sorttab[i]->csa_str);
494*869ffda3Schristos 	  cur_stroff += strlen (sorttab[i]->csa_str) + 1;
495*869ffda3Schristos 	}
496*869ffda3Schristos     }
497*869ffda3Schristos   free (sorttab);
498*869ffda3Schristos 
499*869ffda3Schristos   if (!any_external)
500*869ffda3Schristos     {
501*869ffda3Schristos       ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
502*869ffda3Schristos       fp->ctf_syn_ext_strtab = NULL;
503*869ffda3Schristos     }
504*869ffda3Schristos 
505*869ffda3Schristos   /* All the provisional strtab entries are now real strtab entries, and
506*869ffda3Schristos      ctf_strptr() will find them there.  The provisional offset now starts right
507*869ffda3Schristos      beyond the new end of the strtab.  */
508*869ffda3Schristos 
509*869ffda3Schristos   ctf_dynhash_empty (fp->ctf_prov_strtab);
510*869ffda3Schristos   fp->ctf_str_prov_offset = strtab.cts_len + 1;
511*869ffda3Schristos   return strtab;
512*869ffda3Schristos 
513*869ffda3Schristos  oom_strtab:
514*869ffda3Schristos   free (strtab.cts_strs);
515*869ffda3Schristos   strtab.cts_strs = NULL;
516*869ffda3Schristos  oom_sorttab:
517*869ffda3Schristos   free (sorttab);
518*869ffda3Schristos  oom:
519*869ffda3Schristos   return strtab;
520*869ffda3Schristos }
521