1 /* CTF string table management.
2 Copyright (C) 2019 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_file_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 (ctsp
->cts_strs
!= NULL
&& CTF_NAME_OFFSET (name
) < ctsp
->cts_len
)
42 return (ctsp
->cts_strs
+ CTF_NAME_OFFSET (name
));
44 /* String table not loaded or corrupt offset. */
48 /* Convert an encoded CTF string name into a pointer to a C string by looking
49 up the appropriate string table buffer and then adding the offset. */
51 ctf_strraw (ctf_file_t
*fp
, uint32_t name
)
53 return ctf_strraw_explicit (fp
, name
, NULL
);
56 /* Return a guaranteed-non-NULL pointer to the string with the given CTF
59 ctf_strptr (ctf_file_t
*fp
, uint32_t name
)
61 const char *s
= ctf_strraw (fp
, name
);
62 return (s
!= NULL
? s
: "(?)");
65 /* Remove all refs to a given atom. */
67 ctf_str_purge_atom_refs (ctf_str_atom_t
*atom
)
69 ctf_str_atom_ref_t
*ref
, *next
;
71 for (ref
= ctf_list_next (&atom
->csa_refs
); ref
!= NULL
; ref
= next
)
73 next
= ctf_list_next (ref
);
74 ctf_list_delete (&atom
->csa_refs
, ref
);
79 /* Free an atom (only called on ctf_close().) */
81 ctf_str_free_atom (void *a
)
83 ctf_str_atom_t
*atom
= a
;
85 ctf_str_purge_atom_refs (atom
);
89 /* Create the atoms table. There is always at least one atom in it, the null
92 ctf_str_create_atoms (ctf_file_t
*fp
)
94 fp
->ctf_str_atoms
= ctf_dynhash_create (ctf_hash_string
, ctf_hash_eq_string
,
95 ctf_free
, ctf_str_free_atom
);
96 if (fp
->ctf_str_atoms
== NULL
)
103 /* Destroy the atoms table. */
105 ctf_str_free_atoms (ctf_file_t
*fp
)
107 ctf_dynhash_destroy (fp
->ctf_str_atoms
);
110 /* Add a string to the atoms table, copying the passed-in string. Return the
111 atom added. Return NULL only when out of memory (and do not touch the
112 passed-in string in that case). Possibly augment the ref list with the
114 static ctf_str_atom_t
*
115 ctf_str_add_ref_internal (ctf_file_t
*fp
, const char *str
,
116 int add_ref
, uint32_t *ref
)
119 ctf_str_atom_t
*atom
= NULL
;
120 ctf_str_atom_ref_t
*aref
= NULL
;
122 atom
= ctf_dynhash_lookup (fp
->ctf_str_atoms
, str
);
126 if ((aref
= ctf_alloc (sizeof (struct ctf_str_atom_ref
))) == NULL
)
135 ctf_list_append (&atom
->csa_refs
, aref
);
136 fp
->ctf_str_num_refs
++;
141 if ((atom
= ctf_alloc (sizeof (struct ctf_str_atom
))) == NULL
)
143 memset (atom
, 0, sizeof (struct ctf_str_atom
));
145 if ((newstr
= ctf_strdup (str
)) == NULL
)
148 if (ctf_dynhash_insert (fp
->ctf_str_atoms
, newstr
, atom
) < 0)
151 atom
->csa_str
= newstr
;
152 atom
->csa_snapshot_id
= fp
->ctf_snapshots
;
155 ctf_list_append (&atom
->csa_refs
, aref
);
156 fp
->ctf_str_num_refs
++;
167 /* Add a string to the atoms table and return it, without augmenting the ref
168 list for this string. */
170 ctf_str_add (ctf_file_t
*fp
, const char *str
)
172 ctf_str_atom_t
*atom
;
176 atom
= ctf_str_add_ref_internal (fp
, str
, FALSE
, 0);
180 return atom
->csa_str
;
183 /* Like ctf_str_add(), but additionally augment the atom's refs list with the
184 passed-in ref, whether or not the string is already present. There is no
185 attempt to deduplicate the refs list (but duplicates are harmless). */
187 ctf_str_add_ref (ctf_file_t
*fp
, const char *str
, uint32_t *ref
)
189 ctf_str_atom_t
*atom
;
193 atom
= ctf_str_add_ref_internal (fp
, str
, TRUE
, ref
);
197 return atom
->csa_str
;
200 /* Add an external strtab reference at OFFSET. */
202 ctf_str_add_external (ctf_file_t
*fp
, const char *str
, uint32_t offset
)
204 ctf_str_atom_t
*atom
;
208 atom
= ctf_str_add_ref_internal (fp
, str
, FALSE
, 0);
212 atom
->csa_offset
= CTF_SET_STID (offset
, CTF_STRTAB_1
);
213 return atom
->csa_str
;
216 /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
219 ctf_str_rollback_atom (void *key _libctf_unused_
, void *value
, void *arg
)
221 ctf_str_atom_t
*atom
= (ctf_str_atom_t
*) value
;
222 ctf_snapshot_id_t
*id
= (ctf_snapshot_id_t
*) arg
;
224 return (atom
->csa_snapshot_id
> id
->snapshot_id
);
227 /* Roll back, deleting all atoms created after a particular ID. */
229 ctf_str_rollback (ctf_file_t
*fp
, ctf_snapshot_id_t id
)
231 ctf_dynhash_iter_remove (fp
->ctf_str_atoms
, ctf_str_rollback_atom
, &id
);
234 /* An adaptor around ctf_purge_atom_refs. */
236 ctf_str_purge_one_atom_refs (void *key _libctf_unused_
, void *value
,
237 void *arg _libctf_unused_
)
239 ctf_str_atom_t
*atom
= (ctf_str_atom_t
*) value
;
240 ctf_str_purge_atom_refs (atom
);
243 /* Remove all the recorded refs from the atoms table. */
245 ctf_str_purge_refs (ctf_file_t
*fp
)
247 if (fp
->ctf_str_num_refs
> 0)
248 ctf_dynhash_iter (fp
->ctf_str_atoms
, ctf_str_purge_one_atom_refs
, NULL
);
249 fp
->ctf_str_num_refs
= 0;
252 /* Update a list of refs to the specified value. */
254 ctf_str_update_refs (ctf_str_atom_t
*refs
, uint32_t value
)
256 ctf_str_atom_ref_t
*ref
;
258 for (ref
= ctf_list_next (&refs
->csa_refs
); ref
!= NULL
;
259 ref
= ctf_list_next (ref
))
260 *(ref
->caf_ref
) = value
;
263 /* State shared across the strtab write process. */
264 typedef struct ctf_strtab_write_state
266 /* Strtab we are writing, and the number of strings in it. */
267 ctf_strs_writable_t
*strtab
;
270 /* Pointers to (existing) atoms in the atoms table, for qsorting. */
271 ctf_str_atom_t
**sorttab
;
273 /* Loop counter for sorttab population. */
276 /* The null-string atom (skipped during population). */
277 ctf_str_atom_t
*nullstr
;
278 } ctf_strtab_write_state_t
;
280 /* Count the number of entries in the strtab, and its length. */
282 ctf_str_count_strtab (void *key _libctf_unused_
, void *value
,
285 ctf_str_atom_t
*atom
= (ctf_str_atom_t
*) value
;
286 ctf_strtab_write_state_t
*s
= (ctf_strtab_write_state_t
*) arg
;
288 /* We only factor in the length of items that have no offset:
289 other items are in the external strtab. They still contribute to the
290 total count, though, because we still have to sort them. */
291 if (!atom
->csa_offset
)
292 s
->strtab
->cts_len
+= strlen (atom
->csa_str
) + 1;
296 /* Populate the sorttab with pointers to the strtab atoms. */
298 ctf_str_populate_sorttab (void *key _libctf_unused_
, void *value
,
301 ctf_str_atom_t
*atom
= (ctf_str_atom_t
*) value
;
302 ctf_strtab_write_state_t
*s
= (ctf_strtab_write_state_t
*) arg
;
304 /* Skip the null string. */
305 if (s
->nullstr
== atom
)
308 s
->sorttab
[s
->i
++] = atom
;
311 /* Sort the strtab. */
313 ctf_str_sort_strtab (const void *a
, const void *b
)
315 ctf_str_atom_t
**one
= (ctf_str_atom_t
**) a
;
316 ctf_str_atom_t
**two
= (ctf_str_atom_t
**) b
;
318 return (strcmp ((*one
)->csa_str
, (*two
)->csa_str
));
321 /* Write out and return a strtab containing all strings with recorded refs,
322 adjusting the refs to refer to the corresponding string. The returned strtab
323 may be NULL on error. Also populate the synthetic strtab with mappings from
324 external strtab offsets to names, so we can look them up with ctf_strptr().
325 Only external strtab offsets with references are added. */
327 ctf_str_write_strtab (ctf_file_t
*fp
)
329 ctf_strs_writable_t strtab
;
330 ctf_str_atom_t
*nullstr
;
331 uint32_t cur_stroff
= 0;
332 ctf_strtab_write_state_t s
;
333 ctf_str_atom_t
**sorttab
;
335 int any_external
= 0;
337 memset (&strtab
, 0, sizeof (struct ctf_strs_writable
));
338 memset (&s
, 0, sizeof (struct ctf_strtab_write_state
));
341 nullstr
= ctf_dynhash_lookup (fp
->ctf_str_atoms
, "");
344 ctf_dprintf ("Internal error: null string not found in strtab.\n");
345 strtab
.cts_strs
= NULL
;
349 ctf_dynhash_iter (fp
->ctf_str_atoms
, ctf_str_count_strtab
, &s
);
351 ctf_dprintf ("%lu bytes of strings in strtab.\n",
352 (unsigned long) strtab
.cts_len
);
354 /* Sort the strtab. Force the null string to be first. */
355 sorttab
= calloc (s
.strtab_count
, sizeof (ctf_str_atom_t
*));
359 sorttab
[0] = nullstr
;
363 ctf_dynhash_iter (fp
->ctf_str_atoms
, ctf_str_populate_sorttab
, &s
);
365 qsort (&sorttab
[1], s
.strtab_count
- 1, sizeof (ctf_str_atom_t
*),
366 ctf_str_sort_strtab
);
368 if ((strtab
.cts_strs
= ctf_alloc (strtab
.cts_len
)) == NULL
)
371 if (!fp
->ctf_syn_ext_strtab
)
372 fp
->ctf_syn_ext_strtab
= ctf_dynhash_create (ctf_hash_integer
,
375 if (!fp
->ctf_syn_ext_strtab
)
378 /* Update all refs: also update the strtab appropriately. */
379 for (i
= 0; i
< s
.strtab_count
; i
++)
381 if (sorttab
[i
]->csa_offset
)
383 /* External strtab entry: populate the synthetic external strtab.
385 This is safe because you cannot ctf_rollback to before the point
386 when a ctf_update is done, and the strtab is written at ctf_update
387 time. So any atoms we reference here are sure to stick around
388 until ctf_file_close. */
391 ctf_str_update_refs (sorttab
[i
], sorttab
[i
]->csa_offset
);
392 if (ctf_dynhash_insert (fp
->ctf_syn_ext_strtab
,
393 (void *) (uintptr_t) sorttab
[i
]->csa_offset
,
394 (void *) sorttab
[i
]->csa_str
) < 0)
399 /* Internal strtab entry: actually add to the string table. */
401 ctf_str_update_refs (sorttab
[i
], cur_stroff
);
402 strcpy (&strtab
.cts_strs
[cur_stroff
], sorttab
[i
]->csa_str
);
403 cur_stroff
+= strlen (sorttab
[i
]->csa_str
) + 1;
410 ctf_dynhash_destroy (fp
->ctf_syn_ext_strtab
);
411 fp
->ctf_syn_ext_strtab
= NULL
;
417 free (strtab
.cts_strs
);
418 strtab
.cts_strs
= NULL
;