xref: /netbsd-src/external/gpl3/binutils.old/dist/libctf/ctf-string.c (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
1867d70fcSchristos /* CTF string table management.
2*c42dbd0eSchristos    Copyright (C) 2019-2022 Free Software Foundation, Inc.
3867d70fcSchristos 
4867d70fcSchristos    This file is part of libctf.
5867d70fcSchristos 
6867d70fcSchristos    libctf is free software; you can redistribute it and/or modify it under
7867d70fcSchristos    the terms of the GNU General Public License as published by the Free
8867d70fcSchristos    Software Foundation; either version 3, or (at your option) any later
9867d70fcSchristos    version.
10867d70fcSchristos 
11867d70fcSchristos    This program is distributed in the hope that it will be useful, but
12867d70fcSchristos    WITHOUT ANY WARRANTY; without even the implied warranty of
13867d70fcSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14867d70fcSchristos    See the GNU General Public License for more details.
15867d70fcSchristos 
16867d70fcSchristos    You should have received a copy of the GNU General Public License
17867d70fcSchristos    along with this program; see the file COPYING.  If not see
18867d70fcSchristos    <http://www.gnu.org/licenses/>.  */
19867d70fcSchristos 
20867d70fcSchristos #include <ctf-impl.h>
21867d70fcSchristos #include <string.h>
22*c42dbd0eSchristos #include <assert.h>
23867d70fcSchristos 
24867d70fcSchristos /* Convert an encoded CTF string name into a pointer to a C string, using an
25867d70fcSchristos   explicit internal strtab rather than the fp-based one.  */
26867d70fcSchristos const char *
ctf_strraw_explicit(ctf_dict_t * fp,uint32_t name,ctf_strs_t * strtab)27*c42dbd0eSchristos ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab)
28867d70fcSchristos {
29867d70fcSchristos   ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
30867d70fcSchristos 
31867d70fcSchristos   if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL))
32867d70fcSchristos     ctsp = strtab;
33867d70fcSchristos 
34867d70fcSchristos   /* If this name is in the external strtab, and there is a synthetic strtab,
35867d70fcSchristos      use it in preference.  */
36867d70fcSchristos 
37867d70fcSchristos   if (CTF_NAME_STID (name) == CTF_STRTAB_1
38867d70fcSchristos       && fp->ctf_syn_ext_strtab != NULL)
39867d70fcSchristos     return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab,
40867d70fcSchristos 			       (void *) (uintptr_t) name);
41867d70fcSchristos 
42867d70fcSchristos   /* If the name is in the internal strtab, and the offset is beyond the end of
43867d70fcSchristos      the ctsp->cts_len but below the ctf_str_prov_offset, this is a provisional
44867d70fcSchristos      string added by ctf_str_add*() but not yet built into a real strtab: get
45867d70fcSchristos      the value out of the ctf_prov_strtab.  */
46867d70fcSchristos 
47867d70fcSchristos   if (CTF_NAME_STID (name) == CTF_STRTAB_0
48867d70fcSchristos       && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset)
49867d70fcSchristos       return ctf_dynhash_lookup (fp->ctf_prov_strtab,
50867d70fcSchristos 				 (void *) (uintptr_t) name);
51867d70fcSchristos 
52867d70fcSchristos   if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
53867d70fcSchristos     return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
54867d70fcSchristos 
55867d70fcSchristos   /* String table not loaded or corrupt offset.  */
56867d70fcSchristos   return NULL;
57867d70fcSchristos }
58867d70fcSchristos 
59867d70fcSchristos /* Convert an encoded CTF string name into a pointer to a C string by looking
60867d70fcSchristos   up the appropriate string table buffer and then adding the offset.  */
61867d70fcSchristos const char *
ctf_strraw(ctf_dict_t * fp,uint32_t name)62*c42dbd0eSchristos ctf_strraw (ctf_dict_t *fp, uint32_t name)
63867d70fcSchristos {
64867d70fcSchristos   return ctf_strraw_explicit (fp, name, NULL);
65867d70fcSchristos }
66867d70fcSchristos 
67867d70fcSchristos /* Return a guaranteed-non-NULL pointer to the string with the given CTF
68867d70fcSchristos    name.  */
69867d70fcSchristos const char *
ctf_strptr(ctf_dict_t * fp,uint32_t name)70*c42dbd0eSchristos ctf_strptr (ctf_dict_t *fp, uint32_t name)
71867d70fcSchristos {
72867d70fcSchristos   const char *s = ctf_strraw (fp, name);
73867d70fcSchristos   return (s != NULL ? s : "(?)");
74867d70fcSchristos }
75867d70fcSchristos 
76867d70fcSchristos /* Remove all refs to a given atom.  */
77867d70fcSchristos static void
ctf_str_purge_atom_refs(ctf_str_atom_t * atom)78867d70fcSchristos ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
79867d70fcSchristos {
80867d70fcSchristos   ctf_str_atom_ref_t *ref, *next;
81867d70fcSchristos 
82867d70fcSchristos   for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
83867d70fcSchristos     {
84867d70fcSchristos       next = ctf_list_next (ref);
85867d70fcSchristos       ctf_list_delete (&atom->csa_refs, ref);
86867d70fcSchristos       free (ref);
87867d70fcSchristos     }
88867d70fcSchristos }
89867d70fcSchristos 
90867d70fcSchristos /* Free an atom (only called on ctf_close().)  */
91867d70fcSchristos static void
ctf_str_free_atom(void * a)92867d70fcSchristos ctf_str_free_atom (void *a)
93867d70fcSchristos {
94867d70fcSchristos   ctf_str_atom_t *atom = a;
95867d70fcSchristos 
96867d70fcSchristos   ctf_str_purge_atom_refs (atom);
97867d70fcSchristos   free (atom);
98867d70fcSchristos }
99867d70fcSchristos 
100867d70fcSchristos /* Create the atoms table.  There is always at least one atom in it, the null
101867d70fcSchristos    string.  */
102867d70fcSchristos int
ctf_str_create_atoms(ctf_dict_t * fp)103*c42dbd0eSchristos ctf_str_create_atoms (ctf_dict_t *fp)
104867d70fcSchristos {
105867d70fcSchristos   fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
106867d70fcSchristos 					  free, ctf_str_free_atom);
107*c42dbd0eSchristos   if (!fp->ctf_str_atoms)
108867d70fcSchristos     return -ENOMEM;
109867d70fcSchristos 
110867d70fcSchristos   if (!fp->ctf_prov_strtab)
111867d70fcSchristos     fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer,
112867d70fcSchristos 					      ctf_hash_eq_integer,
113867d70fcSchristos 					      NULL, NULL);
114867d70fcSchristos   if (!fp->ctf_prov_strtab)
115867d70fcSchristos     goto oom_prov_strtab;
116867d70fcSchristos 
117*c42dbd0eSchristos   if (!fp->ctf_str_pending_ref)
118*c42dbd0eSchristos     fp->ctf_str_pending_ref = ctf_dynset_create (htab_hash_pointer,
119*c42dbd0eSchristos 						 htab_eq_pointer,
120*c42dbd0eSchristos 						 NULL);
121*c42dbd0eSchristos   if (!fp->ctf_str_pending_ref)
122*c42dbd0eSchristos     goto oom_str_pending_ref;
123*c42dbd0eSchristos 
124867d70fcSchristos   errno = 0;
125867d70fcSchristos   ctf_str_add (fp, "");
126867d70fcSchristos   if (errno == ENOMEM)
127867d70fcSchristos     goto oom_str_add;
128867d70fcSchristos 
129867d70fcSchristos   return 0;
130867d70fcSchristos 
131867d70fcSchristos  oom_str_add:
132867d70fcSchristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
133867d70fcSchristos   fp->ctf_prov_strtab = NULL;
134*c42dbd0eSchristos  oom_str_pending_ref:
135*c42dbd0eSchristos   ctf_dynset_destroy (fp->ctf_str_pending_ref);
136*c42dbd0eSchristos   fp->ctf_str_pending_ref = NULL;
137867d70fcSchristos  oom_prov_strtab:
138867d70fcSchristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
139867d70fcSchristos   fp->ctf_str_atoms = NULL;
140867d70fcSchristos   return -ENOMEM;
141867d70fcSchristos }
142867d70fcSchristos 
143867d70fcSchristos /* Destroy the atoms table.  */
144867d70fcSchristos void
ctf_str_free_atoms(ctf_dict_t * fp)145*c42dbd0eSchristos ctf_str_free_atoms (ctf_dict_t *fp)
146867d70fcSchristos {
147867d70fcSchristos   ctf_dynhash_destroy (fp->ctf_prov_strtab);
148867d70fcSchristos   ctf_dynhash_destroy (fp->ctf_str_atoms);
149*c42dbd0eSchristos   ctf_dynset_destroy (fp->ctf_str_pending_ref);
150867d70fcSchristos }
151867d70fcSchristos 
152*c42dbd0eSchristos #define CTF_STR_ADD_REF 0x1
153*c42dbd0eSchristos #define CTF_STR_MAKE_PROVISIONAL 0x2
154*c42dbd0eSchristos #define CTF_STR_PENDING_REF 0x4
155*c42dbd0eSchristos 
156867d70fcSchristos /* Add a string to the atoms table, copying the passed-in string.  Return the
157867d70fcSchristos    atom added. Return NULL only when out of memory (and do not touch the
158867d70fcSchristos    passed-in string in that case).  Possibly augment the ref list with the
159867d70fcSchristos    passed-in ref.  Possibly add a provisional entry for this string to the
160867d70fcSchristos    provisional strtab.   */
161867d70fcSchristos static ctf_str_atom_t *
ctf_str_add_ref_internal(ctf_dict_t * fp,const char * str,int flags,uint32_t * ref)162*c42dbd0eSchristos ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
163*c42dbd0eSchristos 			  int flags, uint32_t *ref)
164867d70fcSchristos {
165867d70fcSchristos   char *newstr = NULL;
166867d70fcSchristos   ctf_str_atom_t *atom = NULL;
167867d70fcSchristos   ctf_str_atom_ref_t *aref = NULL;
168867d70fcSchristos 
169867d70fcSchristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
170867d70fcSchristos 
171*c42dbd0eSchristos   if (flags & CTF_STR_ADD_REF)
172867d70fcSchristos     {
173867d70fcSchristos       if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL)
174867d70fcSchristos 	return NULL;
175867d70fcSchristos       aref->caf_ref = ref;
176867d70fcSchristos     }
177867d70fcSchristos 
178867d70fcSchristos   if (atom)
179867d70fcSchristos     {
180*c42dbd0eSchristos       if (flags & CTF_STR_ADD_REF)
181867d70fcSchristos 	{
182*c42dbd0eSchristos 	  ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
183867d70fcSchristos 	  ctf_list_append (&atom->csa_refs, aref);
184867d70fcSchristos 	  fp->ctf_str_num_refs++;
185867d70fcSchristos 	}
186867d70fcSchristos       return atom;
187867d70fcSchristos     }
188867d70fcSchristos 
189867d70fcSchristos   if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
190867d70fcSchristos     goto oom;
191867d70fcSchristos   memset (atom, 0, sizeof (struct ctf_str_atom));
192867d70fcSchristos 
193867d70fcSchristos   if ((newstr = strdup (str)) == NULL)
194867d70fcSchristos     goto oom;
195867d70fcSchristos 
196867d70fcSchristos   if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0)
197867d70fcSchristos     goto oom;
198867d70fcSchristos 
199867d70fcSchristos   atom->csa_str = newstr;
200867d70fcSchristos   atom->csa_snapshot_id = fp->ctf_snapshots;
201867d70fcSchristos 
202*c42dbd0eSchristos   if (flags & CTF_STR_MAKE_PROVISIONAL)
203867d70fcSchristos     {
204867d70fcSchristos       atom->csa_offset = fp->ctf_str_prov_offset;
205867d70fcSchristos 
206867d70fcSchristos       if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t)
207867d70fcSchristos 			      atom->csa_offset, (void *) atom->csa_str) < 0)
208867d70fcSchristos 	goto oom;
209867d70fcSchristos 
210867d70fcSchristos       fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
211867d70fcSchristos     }
212867d70fcSchristos 
213*c42dbd0eSchristos   if (flags & CTF_STR_PENDING_REF)
214867d70fcSchristos     {
215*c42dbd0eSchristos       if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) ref) < 0)
216*c42dbd0eSchristos 	goto oom;
217*c42dbd0eSchristos     }
218*c42dbd0eSchristos   else if (flags & CTF_STR_ADD_REF)
219*c42dbd0eSchristos     {
220*c42dbd0eSchristos       ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
221867d70fcSchristos       ctf_list_append (&atom->csa_refs, aref);
222867d70fcSchristos       fp->ctf_str_num_refs++;
223867d70fcSchristos     }
224867d70fcSchristos   return atom;
225867d70fcSchristos 
226867d70fcSchristos  oom:
227867d70fcSchristos   if (newstr)
228867d70fcSchristos     ctf_dynhash_remove (fp->ctf_str_atoms, newstr);
229867d70fcSchristos   free (atom);
230867d70fcSchristos   free (aref);
231867d70fcSchristos   free (newstr);
232*c42dbd0eSchristos   ctf_set_errno (fp, ENOMEM);
233867d70fcSchristos   return NULL;
234867d70fcSchristos }
235867d70fcSchristos 
236867d70fcSchristos /* Add a string to the atoms table, without augmenting the ref list for this
237867d70fcSchristos    string: return a 'provisional offset' which can be used to return this string
238867d70fcSchristos    until ctf_str_write_strtab is called, or 0 on failure.  (Everywhere the
239867d70fcSchristos    provisional offset is assigned to should be added as a ref using
240867d70fcSchristos    ctf_str_add_ref() as well.) */
241867d70fcSchristos uint32_t
ctf_str_add(ctf_dict_t * fp,const char * str)242*c42dbd0eSchristos ctf_str_add (ctf_dict_t *fp, const char *str)
243867d70fcSchristos {
244867d70fcSchristos   ctf_str_atom_t *atom;
245867d70fcSchristos 
246*c42dbd0eSchristos   if (!str)
247*c42dbd0eSchristos     str = "";
248*c42dbd0eSchristos 
249*c42dbd0eSchristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_MAKE_PROVISIONAL, 0);
250867d70fcSchristos   if (!atom)
251867d70fcSchristos     return 0;
252867d70fcSchristos 
253867d70fcSchristos   return atom->csa_offset;
254867d70fcSchristos }
255867d70fcSchristos 
256867d70fcSchristos /* Like ctf_str_add(), but additionally augment the atom's refs list with the
257867d70fcSchristos    passed-in ref, whether or not the string is already present.  There is no
258867d70fcSchristos    attempt to deduplicate the refs list (but duplicates are harmless).  */
259867d70fcSchristos uint32_t
ctf_str_add_ref(ctf_dict_t * fp,const char * str,uint32_t * ref)260*c42dbd0eSchristos ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
261867d70fcSchristos {
262867d70fcSchristos   ctf_str_atom_t *atom;
263867d70fcSchristos 
264*c42dbd0eSchristos   if (!str)
265*c42dbd0eSchristos     str = "";
266*c42dbd0eSchristos 
267*c42dbd0eSchristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
268*c42dbd0eSchristos 				   | CTF_STR_MAKE_PROVISIONAL, ref);
269867d70fcSchristos   if (!atom)
270867d70fcSchristos     return 0;
271867d70fcSchristos 
272867d70fcSchristos   return atom->csa_offset;
273867d70fcSchristos }
274867d70fcSchristos 
275*c42dbd0eSchristos /* Like ctf_str_add_ref(), but notes that this memory location must be added as
276*c42dbd0eSchristos    a ref by a later serialization phase, rather than adding it itself.  */
277*c42dbd0eSchristos uint32_t
ctf_str_add_pending(ctf_dict_t * fp,const char * str,uint32_t * ref)278*c42dbd0eSchristos ctf_str_add_pending (ctf_dict_t *fp, const char *str, uint32_t *ref)
279*c42dbd0eSchristos {
280*c42dbd0eSchristos   ctf_str_atom_t *atom;
281*c42dbd0eSchristos 
282*c42dbd0eSchristos   if (!str)
283*c42dbd0eSchristos     str = "";
284*c42dbd0eSchristos 
285*c42dbd0eSchristos   atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PENDING_REF
286*c42dbd0eSchristos 				   | CTF_STR_MAKE_PROVISIONAL, ref);
287*c42dbd0eSchristos   if (!atom)
288*c42dbd0eSchristos     return 0;
289*c42dbd0eSchristos 
290*c42dbd0eSchristos   return atom->csa_offset;
291*c42dbd0eSchristos }
292*c42dbd0eSchristos 
293*c42dbd0eSchristos /* Note that a pending ref now located at NEW_REF has moved by BYTES bytes.  */
294*c42dbd0eSchristos int
ctf_str_move_pending(ctf_dict_t * fp,uint32_t * new_ref,ptrdiff_t bytes)295*c42dbd0eSchristos ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
296*c42dbd0eSchristos {
297*c42dbd0eSchristos   if (bytes == 0)
298*c42dbd0eSchristos     return 0;
299*c42dbd0eSchristos 
300*c42dbd0eSchristos   if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
301*c42dbd0eSchristos     return (ctf_set_errno (fp, ENOMEM));
302*c42dbd0eSchristos 
303*c42dbd0eSchristos   ctf_dynset_remove (fp->ctf_str_pending_ref,
304*c42dbd0eSchristos 		     (void *) ((signed char *) new_ref - bytes));
305*c42dbd0eSchristos   return 0;
306*c42dbd0eSchristos }
307*c42dbd0eSchristos 
308867d70fcSchristos /* Add an external strtab reference at OFFSET.  Returns zero if the addition
309867d70fcSchristos    failed, nonzero otherwise.  */
310867d70fcSchristos int
ctf_str_add_external(ctf_dict_t * fp,const char * str,uint32_t offset)311*c42dbd0eSchristos ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
312867d70fcSchristos {
313867d70fcSchristos   ctf_str_atom_t *atom;
314867d70fcSchristos 
315*c42dbd0eSchristos   if (!str)
316*c42dbd0eSchristos     str = "";
317*c42dbd0eSchristos 
318*c42dbd0eSchristos   atom = ctf_str_add_ref_internal (fp, str, 0, 0);
319867d70fcSchristos   if (!atom)
320867d70fcSchristos     return 0;
321867d70fcSchristos 
322867d70fcSchristos   atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
323*c42dbd0eSchristos 
324*c42dbd0eSchristos   if (!fp->ctf_syn_ext_strtab)
325*c42dbd0eSchristos     fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
326*c42dbd0eSchristos 						 ctf_hash_eq_integer,
327*c42dbd0eSchristos 						 NULL, NULL);
328*c42dbd0eSchristos   if (!fp->ctf_syn_ext_strtab)
329*c42dbd0eSchristos     {
330*c42dbd0eSchristos       ctf_set_errno (fp, ENOMEM);
331*c42dbd0eSchristos       return 0;
332*c42dbd0eSchristos     }
333*c42dbd0eSchristos 
334*c42dbd0eSchristos   if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
335*c42dbd0eSchristos 			  (void *) (uintptr_t)
336*c42dbd0eSchristos 			  atom->csa_external_offset,
337*c42dbd0eSchristos 			  (void *) atom->csa_str) < 0)
338*c42dbd0eSchristos     {
339*c42dbd0eSchristos       /* No need to bother freeing the syn_ext_strtab: it will get freed at
340*c42dbd0eSchristos 	 ctf_str_write_strtab time if unreferenced.  */
341*c42dbd0eSchristos       ctf_set_errno (fp, ENOMEM);
342*c42dbd0eSchristos       return 0;
343*c42dbd0eSchristos     }
344*c42dbd0eSchristos 
345867d70fcSchristos   return 1;
346867d70fcSchristos }
347867d70fcSchristos 
348867d70fcSchristos /* Remove a single ref.  */
349867d70fcSchristos void
ctf_str_remove_ref(ctf_dict_t * fp,const char * str,uint32_t * ref)350*c42dbd0eSchristos ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
351867d70fcSchristos {
352867d70fcSchristos   ctf_str_atom_ref_t *aref, *anext;
353867d70fcSchristos   ctf_str_atom_t *atom = NULL;
354867d70fcSchristos 
355867d70fcSchristos   atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
356867d70fcSchristos   if (!atom)
357867d70fcSchristos     return;
358867d70fcSchristos 
359867d70fcSchristos   for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
360867d70fcSchristos     {
361867d70fcSchristos       anext = ctf_list_next (aref);
362867d70fcSchristos       if (aref->caf_ref == ref)
363867d70fcSchristos 	{
364867d70fcSchristos 	  ctf_list_delete (&atom->csa_refs, aref);
365867d70fcSchristos 	  free (aref);
366867d70fcSchristos 	}
367867d70fcSchristos     }
368*c42dbd0eSchristos 
369*c42dbd0eSchristos   ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
370867d70fcSchristos }
371867d70fcSchristos 
372867d70fcSchristos /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
373*c42dbd0eSchristos    snapshot ID.  External atoms are never removed, because they came from the
374*c42dbd0eSchristos    linker string table and are still present even if you roll back type
375*c42dbd0eSchristos    additions.  */
376867d70fcSchristos static int
ctf_str_rollback_atom(void * key _libctf_unused_,void * value,void * arg)377867d70fcSchristos ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
378867d70fcSchristos {
379867d70fcSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
380867d70fcSchristos   ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
381867d70fcSchristos 
382*c42dbd0eSchristos   return (atom->csa_snapshot_id > id->snapshot_id)
383*c42dbd0eSchristos     && (atom->csa_external_offset == 0);
384867d70fcSchristos }
385867d70fcSchristos 
386*c42dbd0eSchristos /* Roll back, deleting all (internal) atoms created after a particular ID.  */
387867d70fcSchristos void
ctf_str_rollback(ctf_dict_t * fp,ctf_snapshot_id_t id)388*c42dbd0eSchristos ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
389867d70fcSchristos {
390867d70fcSchristos   ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
391867d70fcSchristos }
392867d70fcSchristos 
393867d70fcSchristos /* An adaptor around ctf_purge_atom_refs.  */
394867d70fcSchristos static void
ctf_str_purge_one_atom_refs(void * key _libctf_unused_,void * value,void * arg _libctf_unused_)395867d70fcSchristos ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
396867d70fcSchristos 			     void *arg _libctf_unused_)
397867d70fcSchristos {
398867d70fcSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
399867d70fcSchristos   ctf_str_purge_atom_refs (atom);
400867d70fcSchristos }
401867d70fcSchristos 
402867d70fcSchristos /* Remove all the recorded refs from the atoms table.  */
403867d70fcSchristos void
ctf_str_purge_refs(ctf_dict_t * fp)404*c42dbd0eSchristos ctf_str_purge_refs (ctf_dict_t *fp)
405867d70fcSchristos {
406867d70fcSchristos   if (fp->ctf_str_num_refs > 0)
407867d70fcSchristos     ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
408867d70fcSchristos   fp->ctf_str_num_refs = 0;
409867d70fcSchristos }
410867d70fcSchristos 
411867d70fcSchristos /* Update a list of refs to the specified value. */
412867d70fcSchristos static void
ctf_str_update_refs(ctf_str_atom_t * refs,uint32_t value)413867d70fcSchristos ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
414867d70fcSchristos {
415867d70fcSchristos   ctf_str_atom_ref_t *ref;
416867d70fcSchristos 
417867d70fcSchristos   for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
418867d70fcSchristos        ref = ctf_list_next (ref))
419867d70fcSchristos       *(ref->caf_ref) = value;
420867d70fcSchristos }
421867d70fcSchristos 
422867d70fcSchristos /* State shared across the strtab write process.  */
423867d70fcSchristos typedef struct ctf_strtab_write_state
424867d70fcSchristos {
425867d70fcSchristos   /* Strtab we are writing, and the number of strings in it.  */
426867d70fcSchristos   ctf_strs_writable_t *strtab;
427867d70fcSchristos   size_t strtab_count;
428867d70fcSchristos 
429867d70fcSchristos   /* Pointers to (existing) atoms in the atoms table, for qsorting.  */
430867d70fcSchristos   ctf_str_atom_t **sorttab;
431867d70fcSchristos 
432867d70fcSchristos   /* Loop counter for sorttab population.  */
433867d70fcSchristos   size_t i;
434867d70fcSchristos 
435867d70fcSchristos   /* The null-string atom (skipped during population).  */
436867d70fcSchristos   ctf_str_atom_t *nullstr;
437867d70fcSchristos } ctf_strtab_write_state_t;
438867d70fcSchristos 
439867d70fcSchristos /* Count the number of entries in the strtab, and its length.  */
440867d70fcSchristos static void
ctf_str_count_strtab(void * key _libctf_unused_,void * value,void * arg)441867d70fcSchristos ctf_str_count_strtab (void *key _libctf_unused_, void *value,
442867d70fcSchristos 	      void *arg)
443867d70fcSchristos {
444867d70fcSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
445867d70fcSchristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
446867d70fcSchristos 
447867d70fcSchristos   /* We only factor in the length of items that have no offset and have refs:
448867d70fcSchristos      other items are in the external strtab, or will simply not be written out
449867d70fcSchristos      at all.  They still contribute to the total count, though, because we still
450867d70fcSchristos      have to sort them.  We add in the null string's length explicitly, outside
451867d70fcSchristos      this function, since it is explicitly written out even if it has no refs at
452867d70fcSchristos      all.  */
453867d70fcSchristos 
454867d70fcSchristos   if (s->nullstr == atom)
455867d70fcSchristos     {
456867d70fcSchristos       s->strtab_count++;
457867d70fcSchristos       return;
458867d70fcSchristos     }
459867d70fcSchristos 
460867d70fcSchristos   if (!ctf_list_empty_p (&atom->csa_refs))
461867d70fcSchristos     {
462867d70fcSchristos       if (!atom->csa_external_offset)
463867d70fcSchristos 	s->strtab->cts_len += strlen (atom->csa_str) + 1;
464867d70fcSchristos       s->strtab_count++;
465867d70fcSchristos     }
466867d70fcSchristos }
467867d70fcSchristos 
468867d70fcSchristos /* Populate the sorttab with pointers to the strtab atoms.  */
469867d70fcSchristos static void
ctf_str_populate_sorttab(void * key _libctf_unused_,void * value,void * arg)470867d70fcSchristos ctf_str_populate_sorttab (void *key _libctf_unused_, void *value,
471867d70fcSchristos 		  void *arg)
472867d70fcSchristos {
473867d70fcSchristos   ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
474867d70fcSchristos   ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
475867d70fcSchristos 
476867d70fcSchristos   /* Skip the null string.  */
477867d70fcSchristos   if (s->nullstr == atom)
478867d70fcSchristos     return;
479867d70fcSchristos 
480867d70fcSchristos   /* Skip atoms with no refs.  */
481867d70fcSchristos   if (!ctf_list_empty_p (&atom->csa_refs))
482867d70fcSchristos     s->sorttab[s->i++] = atom;
483867d70fcSchristos }
484867d70fcSchristos 
485867d70fcSchristos /* Sort the strtab.  */
486867d70fcSchristos static int
ctf_str_sort_strtab(const void * a,const void * b)487867d70fcSchristos ctf_str_sort_strtab (const void *a, const void *b)
488867d70fcSchristos {
489867d70fcSchristos   ctf_str_atom_t **one = (ctf_str_atom_t **) a;
490867d70fcSchristos   ctf_str_atom_t **two = (ctf_str_atom_t **) b;
491867d70fcSchristos 
492867d70fcSchristos   return (strcmp ((*one)->csa_str, (*two)->csa_str));
493867d70fcSchristos }
494867d70fcSchristos 
495867d70fcSchristos /* Write out and return a strtab containing all strings with recorded refs,
496867d70fcSchristos    adjusting the refs to refer to the corresponding string.  The returned strtab
497867d70fcSchristos    may be NULL on error.  Also populate the synthetic strtab with mappings from
498867d70fcSchristos    external strtab offsets to names, so we can look them up with ctf_strptr().
499867d70fcSchristos    Only external strtab offsets with references are added.  */
500867d70fcSchristos ctf_strs_writable_t
ctf_str_write_strtab(ctf_dict_t * fp)501*c42dbd0eSchristos ctf_str_write_strtab (ctf_dict_t *fp)
502867d70fcSchristos {
503867d70fcSchristos   ctf_strs_writable_t strtab;
504867d70fcSchristos   ctf_str_atom_t *nullstr;
505867d70fcSchristos   uint32_t cur_stroff = 0;
506867d70fcSchristos   ctf_strtab_write_state_t s;
507867d70fcSchristos   ctf_str_atom_t **sorttab;
508867d70fcSchristos   size_t i;
509867d70fcSchristos   int any_external = 0;
510867d70fcSchristos 
511867d70fcSchristos   memset (&strtab, 0, sizeof (struct ctf_strs_writable));
512867d70fcSchristos   memset (&s, 0, sizeof (struct ctf_strtab_write_state));
513867d70fcSchristos   s.strtab = &strtab;
514867d70fcSchristos 
515867d70fcSchristos   nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
516867d70fcSchristos   if (!nullstr)
517867d70fcSchristos     {
518*c42dbd0eSchristos       ctf_err_warn (fp, 0, ECTF_INTERNAL, _("null string not found in strtab"));
519867d70fcSchristos       strtab.cts_strs = NULL;
520867d70fcSchristos       return strtab;
521867d70fcSchristos     }
522867d70fcSchristos 
523867d70fcSchristos   s.nullstr = nullstr;
524867d70fcSchristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_count_strtab, &s);
525867d70fcSchristos   strtab.cts_len++;				/* For the null string.  */
526867d70fcSchristos 
527867d70fcSchristos   ctf_dprintf ("%lu bytes of strings in strtab.\n",
528867d70fcSchristos 	       (unsigned long) strtab.cts_len);
529867d70fcSchristos 
530867d70fcSchristos   /* Sort the strtab.  Force the null string to be first.  */
531867d70fcSchristos   sorttab = calloc (s.strtab_count, sizeof (ctf_str_atom_t *));
532867d70fcSchristos   if (!sorttab)
533867d70fcSchristos     goto oom;
534867d70fcSchristos 
535867d70fcSchristos   sorttab[0] = nullstr;
536867d70fcSchristos   s.i = 1;
537867d70fcSchristos   s.sorttab = sorttab;
538867d70fcSchristos   ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_populate_sorttab, &s);
539867d70fcSchristos 
540867d70fcSchristos   qsort (&sorttab[1], s.strtab_count - 1, sizeof (ctf_str_atom_t *),
541867d70fcSchristos 	 ctf_str_sort_strtab);
542867d70fcSchristos 
543867d70fcSchristos   if ((strtab.cts_strs = malloc (strtab.cts_len)) == NULL)
544867d70fcSchristos     goto oom_sorttab;
545867d70fcSchristos 
546867d70fcSchristos   /* Update all refs: also update the strtab appropriately.  */
547867d70fcSchristos   for (i = 0; i < s.strtab_count; i++)
548867d70fcSchristos     {
549867d70fcSchristos       if (sorttab[i]->csa_external_offset)
550867d70fcSchristos 	{
551*c42dbd0eSchristos 	  /* External strtab entry.  */
552867d70fcSchristos 
553867d70fcSchristos 	  any_external = 1;
554867d70fcSchristos 	  ctf_str_update_refs (sorttab[i], sorttab[i]->csa_external_offset);
555867d70fcSchristos 	  sorttab[i]->csa_offset = sorttab[i]->csa_external_offset;
556867d70fcSchristos 	}
557867d70fcSchristos       else
558867d70fcSchristos 	{
559867d70fcSchristos 	  /* Internal strtab entry with refs: actually add to the string
560867d70fcSchristos 	     table.  */
561867d70fcSchristos 
562867d70fcSchristos 	  ctf_str_update_refs (sorttab[i], cur_stroff);
563867d70fcSchristos 	  sorttab[i]->csa_offset = cur_stroff;
564867d70fcSchristos 	  strcpy (&strtab.cts_strs[cur_stroff], sorttab[i]->csa_str);
565867d70fcSchristos 	  cur_stroff += strlen (sorttab[i]->csa_str) + 1;
566867d70fcSchristos 	}
567867d70fcSchristos     }
568867d70fcSchristos   free (sorttab);
569867d70fcSchristos 
570867d70fcSchristos   if (!any_external)
571867d70fcSchristos     {
572867d70fcSchristos       ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
573867d70fcSchristos       fp->ctf_syn_ext_strtab = NULL;
574867d70fcSchristos     }
575867d70fcSchristos 
576867d70fcSchristos   /* All the provisional strtab entries are now real strtab entries, and
577867d70fcSchristos      ctf_strptr() will find them there.  The provisional offset now starts right
578867d70fcSchristos      beyond the new end of the strtab.  */
579867d70fcSchristos 
580867d70fcSchristos   ctf_dynhash_empty (fp->ctf_prov_strtab);
581867d70fcSchristos   fp->ctf_str_prov_offset = strtab.cts_len + 1;
582867d70fcSchristos   return strtab;
583867d70fcSchristos 
584867d70fcSchristos  oom_sorttab:
585867d70fcSchristos   free (sorttab);
586867d70fcSchristos  oom:
587867d70fcSchristos   return strtab;
588867d70fcSchristos }
589