1 /* CTF string table management.
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
4 This file is part of libctf.
6 libctf is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not see
18 <http://www.gnu.org/licenses/>. */
23 /* Convert an encoded CTF string name into a pointer to a C string, using an
24 explicit internal strtab rather than the fp-based one. */
26 ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab)
28 ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)];
30 if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL))
33 /* If this name is in the external strtab, and there is a synthetic strtab,
34 use it in preference. */
36 if (CTF_NAME_STID (name) == CTF_STRTAB_1
37 && fp->ctf_syn_ext_strtab != NULL)
38 return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab,
39 (void *) (uintptr_t) name);
41 /* If the name is in the internal strtab, and the offset is beyond the end of
42 the ctsp->cts_len but below the ctf_str_prov_offset, this is a provisional
43 string added by ctf_str_add*() but not yet built into a real strtab: get
44 the value out of the ctf_prov_strtab. */
46 if (CTF_NAME_STID (name) == CTF_STRTAB_0
47 && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset)
48 return ctf_dynhash_lookup (fp->ctf_prov_strtab,
49 (void *) (uintptr_t) name);
51 if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len)
52 return (ctsp->cts_strs + CTF_NAME_OFFSET (name));
54 /* String table not loaded or corrupt offset. */
58 /* Convert an encoded CTF string name into a pointer to a C string by looking
59 up the appropriate string table buffer and then adding the offset. */
61 ctf_strraw (ctf_dict_t *fp, uint32_t name)
63 return ctf_strraw_explicit (fp, name, NULL);
66 /* Return a guaranteed-non-NULL pointer to the string with the given CTF
69 ctf_strptr (ctf_dict_t *fp, uint32_t name)
71 const char *s = ctf_strraw (fp, name);
72 return (s != NULL ? s : "(?)");
75 /* Remove all refs to a given atom. */
77 ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
79 ctf_str_atom_ref_t *ref, *next;
81 for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
83 next = ctf_list_next (ref);
84 ctf_list_delete (&atom->csa_refs, ref);
89 /* Free an atom (only called on ctf_close().) */
91 ctf_str_free_atom (void *a)
93 ctf_str_atom_t *atom = a;
95 ctf_str_purge_atom_refs (atom);
99 /* Create the atoms table. There is always at least one atom in it, the null
102 ctf_str_create_atoms (ctf_dict_t *fp)
104 fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
105 free, ctf_str_free_atom);
106 if (fp->ctf_str_atoms == NULL)
109 if (!fp->ctf_prov_strtab)
110 fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer,
113 if (!fp->ctf_prov_strtab)
114 goto oom_prov_strtab;
117 ctf_str_add (fp, "");
124 ctf_dynhash_destroy (fp->ctf_prov_strtab);
125 fp->ctf_prov_strtab = NULL;
127 ctf_dynhash_destroy (fp->ctf_str_atoms);
128 fp->ctf_str_atoms = NULL;
132 /* Destroy the atoms table. */
134 ctf_str_free_atoms (ctf_dict_t *fp)
136 ctf_dynhash_destroy (fp->ctf_prov_strtab);
137 ctf_dynhash_destroy (fp->ctf_str_atoms);
140 /* Add a string to the atoms table, copying the passed-in string. Return the
141 atom added. Return NULL only when out of memory (and do not touch the
142 passed-in string in that case). Possibly augment the ref list with the
143 passed-in ref. Possibly add a provisional entry for this string to the
144 provisional strtab. */
145 static ctf_str_atom_t *
146 ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
147 int add_ref, int make_provisional, uint32_t *ref)
150 ctf_str_atom_t *atom = NULL;
151 ctf_str_atom_ref_t *aref = NULL;
153 atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
157 if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL)
166 ctf_list_append (&atom->csa_refs, aref);
167 fp->ctf_str_num_refs++;
172 if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
174 memset (atom, 0, sizeof (struct ctf_str_atom));
176 if ((newstr = strdup (str)) == NULL)
179 if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0)
182 atom->csa_str = newstr;
183 atom->csa_snapshot_id = fp->ctf_snapshots;
185 if (make_provisional)
187 atom->csa_offset = fp->ctf_str_prov_offset;
189 if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t)
190 atom->csa_offset, (void *) atom->csa_str) < 0)
193 fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
198 ctf_list_append (&atom->csa_refs, aref);
199 fp->ctf_str_num_refs++;
205 ctf_dynhash_remove (fp->ctf_str_atoms, newstr);
212 /* Add a string to the atoms table, without augmenting the ref list for this
213 string: return a 'provisional offset' which can be used to return this string
214 until ctf_str_write_strtab is called, or 0 on failure. (Everywhere the
215 provisional offset is assigned to should be added as a ref using
216 ctf_str_add_ref() as well.) */
218 ctf_str_add (ctf_dict_t *fp, const char *str)
220 ctf_str_atom_t *atom;
225 atom = ctf_str_add_ref_internal (fp, str, FALSE, TRUE, 0);
229 return atom->csa_offset;
232 /* Like ctf_str_add(), but additionally augment the atom's refs list with the
233 passed-in ref, whether or not the string is already present. There is no
234 attempt to deduplicate the refs list (but duplicates are harmless). */
236 ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
238 ctf_str_atom_t *atom;
243 atom = ctf_str_add_ref_internal (fp, str, TRUE, TRUE, ref);
247 return atom->csa_offset;
250 /* Add an external strtab reference at OFFSET. Returns zero if the addition
251 failed, nonzero otherwise. */
253 ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
255 ctf_str_atom_t *atom;
260 atom = ctf_str_add_ref_internal (fp, str, FALSE, FALSE, 0);
264 atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
266 if (!fp->ctf_syn_ext_strtab)
267 fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
270 if (!fp->ctf_syn_ext_strtab)
272 ctf_set_errno (fp, ENOMEM);
276 if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
278 atom->csa_external_offset,
279 (void *) atom->csa_str) < 0)
281 /* No need to bother freeing the syn_ext_strtab: it will get freed at
282 ctf_str_write_strtab time if unreferenced. */
283 ctf_set_errno (fp, ENOMEM);
290 /* Remove a single ref. */
292 ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
294 ctf_str_atom_ref_t *aref, *anext;
295 ctf_str_atom_t *atom = NULL;
297 atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
301 for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext)
303 anext = ctf_list_next (aref);
304 if (aref->caf_ref == ref)
306 ctf_list_delete (&atom->csa_refs, aref);
312 /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
313 snapshot ID. External atoms are never removed, because they came from the
314 linker string table and are still present even if you roll back type
317 ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
319 ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
320 ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
322 return (atom->csa_snapshot_id > id->snapshot_id)
323 && (atom->csa_external_offset == 0);
326 /* Roll back, deleting all (internal) atoms created after a particular ID. */
328 ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
330 ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
333 /* An adaptor around ctf_purge_atom_refs. */
335 ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
336 void *arg _libctf_unused_)
338 ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
339 ctf_str_purge_atom_refs (atom);
342 /* Remove all the recorded refs from the atoms table. */
344 ctf_str_purge_refs (ctf_dict_t *fp)
346 if (fp->ctf_str_num_refs > 0)
347 ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
348 fp->ctf_str_num_refs = 0;
351 /* Update a list of refs to the specified value. */
353 ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
355 ctf_str_atom_ref_t *ref;
357 for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
358 ref = ctf_list_next (ref))
359 *(ref->caf_ref) = value;
362 /* State shared across the strtab write process. */
363 typedef struct ctf_strtab_write_state
365 /* Strtab we are writing, and the number of strings in it. */
366 ctf_strs_writable_t *strtab;
369 /* Pointers to (existing) atoms in the atoms table, for qsorting. */
370 ctf_str_atom_t **sorttab;
372 /* Loop counter for sorttab population. */
375 /* The null-string atom (skipped during population). */
376 ctf_str_atom_t *nullstr;
377 } ctf_strtab_write_state_t;
379 /* Count the number of entries in the strtab, and its length. */
381 ctf_str_count_strtab (void *key _libctf_unused_, void *value,
384 ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
385 ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
387 /* We only factor in the length of items that have no offset and have refs:
388 other items are in the external strtab, or will simply not be written out
389 at all. They still contribute to the total count, though, because we still
390 have to sort them. We add in the null string's length explicitly, outside
391 this function, since it is explicitly written out even if it has no refs at
394 if (s->nullstr == atom)
400 if (!ctf_list_empty_p (&atom->csa_refs))
402 if (!atom->csa_external_offset)
403 s->strtab->cts_len += strlen (atom->csa_str) + 1;
408 /* Populate the sorttab with pointers to the strtab atoms. */
410 ctf_str_populate_sorttab (void *key _libctf_unused_, void *value,
413 ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
414 ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg;
416 /* Skip the null string. */
417 if (s->nullstr == atom)
420 /* Skip atoms with no refs. */
421 if (!ctf_list_empty_p (&atom->csa_refs))
422 s->sorttab[s->i++] = atom;
425 /* Sort the strtab. */
427 ctf_str_sort_strtab (const void *a, const void *b)
429 ctf_str_atom_t **one = (ctf_str_atom_t **) a;
430 ctf_str_atom_t **two = (ctf_str_atom_t **) b;
432 return (strcmp ((*one)->csa_str, (*two)->csa_str));
435 /* Write out and return a strtab containing all strings with recorded refs,
436 adjusting the refs to refer to the corresponding string. The returned strtab
437 may be NULL on error. Also populate the synthetic strtab with mappings from
438 external strtab offsets to names, so we can look them up with ctf_strptr().
439 Only external strtab offsets with references are added. */
441 ctf_str_write_strtab (ctf_dict_t *fp)
443 ctf_strs_writable_t strtab;
444 ctf_str_atom_t *nullstr;
445 uint32_t cur_stroff = 0;
446 ctf_strtab_write_state_t s;
447 ctf_str_atom_t **sorttab;
449 int any_external = 0;
451 memset (&strtab, 0, sizeof (struct ctf_strs_writable));
452 memset (&s, 0, sizeof (struct ctf_strtab_write_state));
455 nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
458 ctf_err_warn (fp, 0, ECTF_INTERNAL, _("null string not found in strtab"));
459 strtab.cts_strs = NULL;
464 ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_count_strtab, &s);
465 strtab.cts_len++; /* For the null string. */
467 ctf_dprintf ("%lu bytes of strings in strtab.\n",
468 (unsigned long) strtab.cts_len);
470 /* Sort the strtab. Force the null string to be first. */
471 sorttab = calloc (s.strtab_count, sizeof (ctf_str_atom_t *));
475 sorttab[0] = nullstr;
478 ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_populate_sorttab, &s);
480 qsort (&sorttab[1], s.strtab_count - 1, sizeof (ctf_str_atom_t *),
481 ctf_str_sort_strtab);
483 if ((strtab.cts_strs = malloc (strtab.cts_len)) == NULL)
486 /* Update all refs: also update the strtab appropriately. */
487 for (i = 0; i < s.strtab_count; i++)
489 if (sorttab[i]->csa_external_offset)
491 /* External strtab entry. */
494 ctf_str_update_refs (sorttab[i], sorttab[i]->csa_external_offset);
495 sorttab[i]->csa_offset = sorttab[i]->csa_external_offset;
499 /* Internal strtab entry with refs: actually add to the string
502 ctf_str_update_refs (sorttab[i], cur_stroff);
503 sorttab[i]->csa_offset = cur_stroff;
504 strcpy (&strtab.cts_strs[cur_stroff], sorttab[i]->csa_str);
505 cur_stroff += strlen (sorttab[i]->csa_str) + 1;
512 ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
513 fp->ctf_syn_ext_strtab = NULL;
516 /* All the provisional strtab entries are now real strtab entries, and
517 ctf_strptr() will find them there. The provisional offset now starts right
518 beyond the new end of the strtab. */
520 ctf_dynhash_empty (fp->ctf_prov_strtab);
521 fp->ctf_str_prov_offset = strtab.cts_len + 1;