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