Coverage Report

Created: 2023-08-28 06:23

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