Coverage Report

Created: 2025-06-24 06:45

/src/binutils-gdb/bfd/elf-strtab.c
Line
Count
Source (jump to first uncovered line)
1
/* ELF strtab with GC and suffix merging support.
2
   Copyright (C) 2001-2025 Free Software Foundation, Inc.
3
   Written by Jakub Jelinek <jakub@redhat.com>.
4
5
   This file is part of BFD, the Binary File Descriptor library.
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program; if not, write to the Free Software
19
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
#include "sysdep.h"
23
#include "bfd.h"
24
#include "libbfd.h"
25
#include "elf-bfd.h"
26
#include "hashtab.h"
27
#include "libiberty.h"
28
29
/* An entry in the strtab hash table.  */
30
31
struct elf_strtab_hash_entry
32
{
33
  struct bfd_hash_entry root;
34
  /* Length of this entry.  This includes the zero terminator.  */
35
  int len;
36
  unsigned int refcount;
37
  union {
38
    /* Index within the merged section.  */
39
    bfd_size_type index;
40
    /* Entry this is a suffix of (if len < 0).  */
41
    struct elf_strtab_hash_entry *suffix;
42
  } u;
43
};
44
45
/* The strtab hash table.  */
46
47
struct elf_strtab_hash
48
{
49
  struct bfd_hash_table table;
50
  /* Next available index.  */
51
  size_t size;
52
  /* Number of array entries alloced.  */
53
  size_t alloced;
54
  /* Final strtab size.  */
55
  bfd_size_type sec_size;
56
  /* Array of pointers to strtab entries.  */
57
  struct elf_strtab_hash_entry **array;
58
};
59
60
/* Routine to create an entry in a section merge hashtab.  */
61
62
static struct bfd_hash_entry *
63
elf_strtab_hash_newfunc (struct bfd_hash_entry *entry,
64
       struct bfd_hash_table *table,
65
       const char *string)
66
12.9k
{
67
  /* Allocate the structure if it has not already been allocated by a
68
     subclass.  */
69
12.9k
  if (entry == NULL)
70
12.9k
    entry = (struct bfd_hash_entry *)
71
12.9k
  bfd_hash_allocate (table, sizeof (struct elf_strtab_hash_entry));
72
12.9k
  if (entry == NULL)
73
0
    return NULL;
74
75
  /* Call the allocation method of the superclass.  */
76
12.9k
  entry = bfd_hash_newfunc (entry, table, string);
77
78
12.9k
  if (entry)
79
12.9k
    {
80
      /* Initialize the local fields.  */
81
12.9k
      struct elf_strtab_hash_entry *ret;
82
83
12.9k
      ret = (struct elf_strtab_hash_entry *) entry;
84
12.9k
      ret->u.index = -1;
85
12.9k
      ret->refcount = 0;
86
12.9k
      ret->len = 0;
87
12.9k
    }
88
89
12.9k
  return entry;
90
12.9k
}
91
92
/* Create a new hash table.  */
93
94
struct elf_strtab_hash *
95
_bfd_elf_strtab_init (void)
96
136
{
97
136
  struct elf_strtab_hash *table;
98
136
  size_t amt = sizeof (struct elf_strtab_hash);
99
100
136
  table = (struct elf_strtab_hash *) bfd_malloc (amt);
101
136
  if (table == NULL)
102
0
    return NULL;
103
104
136
  if (!bfd_hash_table_init (&table->table, elf_strtab_hash_newfunc,
105
136
          sizeof (struct elf_strtab_hash_entry)))
106
0
    {
107
0
      free (table);
108
0
      return NULL;
109
0
    }
110
111
136
  table->sec_size = 0;
112
136
  table->size = 1;
113
136
  table->alloced = 64;
114
136
  amt = sizeof (struct elf_strtab_hasn_entry *);
115
136
  table->array = ((struct elf_strtab_hash_entry **)
116
136
      bfd_malloc (table->alloced * amt));
117
136
  if (table->array == NULL)
118
0
    {
119
0
      bfd_hash_table_free (&table->table);
120
0
      free (table);
121
0
      return NULL;
122
0
    }
123
124
136
  table->array[0] = NULL;
125
126
136
  return table;
127
136
}
128
129
/* Free a strtab.  */
130
131
void
132
_bfd_elf_strtab_free (struct elf_strtab_hash *tab)
133
136
{
134
136
  bfd_hash_table_free (&tab->table);
135
136
  free (tab->array);
136
136
  free (tab);
137
136
}
138
139
/* Get the index of an entity in a hash table, adding it if it is not
140
   already present.  */
141
142
size_t
143
_bfd_elf_strtab_add (struct elf_strtab_hash *tab,
144
         const char *str,
145
         bool copy)
146
17.4k
{
147
17.4k
  register struct elf_strtab_hash_entry *entry;
148
149
  /* We handle this specially, since we don't want to do refcounting
150
     on it.  */
151
17.4k
  if (*str == '\0')
152
2.43k
    return 0;
153
154
15.0k
  BFD_ASSERT (tab->sec_size == 0);
155
15.0k
  entry = (struct elf_strtab_hash_entry *)
156
15.0k
    bfd_hash_lookup (&tab->table, str, true, copy);
157
158
15.0k
  if (entry == NULL)
159
0
    return (size_t) -1;
160
161
15.0k
  entry->refcount++;
162
15.0k
  if (entry->len == 0)
163
12.9k
    {
164
12.9k
      entry->len = strlen (str) + 1;
165
      /* 2G strings lose.  */
166
12.9k
      BFD_ASSERT (entry->len > 0);
167
12.9k
      if (tab->size == tab->alloced)
168
81
  {
169
81
    bfd_size_type amt = sizeof (struct elf_strtab_hash_entry *);
170
81
    tab->alloced *= 2;
171
81
    tab->array = (struct elf_strtab_hash_entry **)
172
81
        bfd_realloc_or_free (tab->array, tab->alloced * amt);
173
81
    if (tab->array == NULL)
174
0
      return (size_t) -1;
175
81
  }
176
177
12.9k
      entry->u.index = tab->size++;
178
12.9k
      tab->array[entry->u.index] = entry;
179
12.9k
    }
180
15.0k
  return entry->u.index;
181
15.0k
}
182
183
void
184
_bfd_elf_strtab_addref (struct elf_strtab_hash *tab, size_t idx)
185
4.37k
{
186
4.37k
  if (idx == 0 || idx == (size_t) -1)
187
1
    return;
188
4.37k
  BFD_ASSERT (tab->sec_size == 0);
189
4.37k
  BFD_ASSERT (idx < tab->size);
190
4.37k
  ++tab->array[idx]->refcount;
191
4.37k
}
192
193
void
194
_bfd_elf_strtab_delref (struct elf_strtab_hash *tab, size_t idx)
195
0
{
196
0
  if (idx == 0 || idx == (size_t) -1)
197
0
    return;
198
0
  BFD_ASSERT (tab->sec_size == 0);
199
0
  BFD_ASSERT (idx < tab->size);
200
0
  BFD_ASSERT (tab->array[idx]->refcount > 0);
201
0
  --tab->array[idx]->refcount;
202
0
}
203
204
unsigned int
205
_bfd_elf_strtab_refcount (struct elf_strtab_hash *tab, size_t idx)
206
0
{
207
0
  return tab->array[idx]->refcount;
208
0
}
209
210
void
211
_bfd_elf_strtab_clear_all_refs (struct elf_strtab_hash *tab)
212
96
{
213
96
  size_t idx;
214
215
2.79k
  for (idx = 1; idx < tab->size; idx++)
216
2.69k
    tab->array[idx]->refcount = 0;
217
96
}
218
219
/* Save strtab refcounts prior to adding --as-needed library.  */
220
221
struct strtab_save
222
{
223
  size_t size;
224
  unsigned int refcount[1];
225
};
226
227
void *
228
_bfd_elf_strtab_save (struct elf_strtab_hash *tab)
229
0
{
230
0
  struct strtab_save *save;
231
0
  size_t idx, size;
232
233
0
  size = sizeof (*save) + (tab->size - 1) * sizeof (save->refcount[0]);
234
0
  save = bfd_malloc (size);
235
0
  if (save == NULL)
236
0
    return save;
237
238
0
  save->size = tab->size;
239
0
  for (idx = 1; idx < tab->size; idx++)
240
0
    save->refcount[idx] = tab->array[idx]->refcount;
241
0
  return save;
242
0
}
243
244
/* Restore strtab refcounts on finding --as-needed library not needed.  */
245
246
void
247
_bfd_elf_strtab_restore (struct elf_strtab_hash *tab, void *buf)
248
0
{
249
0
  size_t idx, curr_size = tab->size, save_size;
250
0
  struct strtab_save *save = (struct strtab_save *) buf;
251
252
0
  BFD_ASSERT (tab->sec_size == 0);
253
0
  save_size = 1;
254
0
  if (save != NULL)
255
0
    save_size = save->size;
256
0
  BFD_ASSERT (save_size <= curr_size);
257
0
  tab->size = save_size;
258
0
  for (idx = 1; idx < save_size; ++idx)
259
0
    tab->array[idx]->refcount = save->refcount[idx];
260
261
0
  for (; idx < curr_size; ++idx)
262
0
    {
263
      /* We don't remove entries from the hash table, just set their
264
   REFCOUNT to zero.  Setting LEN zero will result in the size
265
   growing if the entry is added again.  See _bfd_elf_strtab_add.  */
266
0
      tab->array[idx]->refcount = 0;
267
0
      tab->array[idx]->len = 0;
268
0
    }
269
0
}
270
271
bfd_size_type
272
_bfd_elf_strtab_size (struct elf_strtab_hash *tab)
273
135
{
274
135
  return tab->sec_size ? tab->sec_size : tab->size;
275
135
}
276
277
bfd_size_type
278
_bfd_elf_strtab_len (struct elf_strtab_hash *tab)
279
0
{
280
0
  return tab->size;
281
0
}
282
283
bfd_size_type
284
_bfd_elf_strtab_offset (struct elf_strtab_hash *tab, size_t idx)
285
14.6k
{
286
14.6k
  struct elf_strtab_hash_entry *entry;
287
288
14.6k
  if (idx == 0)
289
1
    return 0;
290
14.6k
  BFD_ASSERT (idx < tab->size);
291
14.6k
  BFD_ASSERT (tab->sec_size);
292
14.6k
  entry = tab->array[idx];
293
14.6k
  BFD_ASSERT (entry->refcount > 0);
294
14.6k
  entry->refcount--;
295
14.6k
  return tab->array[idx]->u.index;
296
14.6k
}
297
298
const char *
299
_bfd_elf_strtab_str (struct elf_strtab_hash *tab, size_t idx,
300
         bfd_size_type *offset)
301
0
{
302
0
  if (idx == 0)
303
0
    return NULL;
304
0
  BFD_ASSERT (idx < tab->size);
305
0
  BFD_ASSERT (tab->sec_size);
306
0
  if (tab->array[idx]->refcount == 0)
307
0
    return NULL;
308
0
  if (offset)
309
0
    *offset = tab->array[idx]->u.index;
310
0
  return tab->array[idx]->root.string;
311
0
}
312
313
bool
314
_bfd_elf_strtab_emit (register bfd *abfd, struct elf_strtab_hash *tab)
315
134
{
316
134
  bfd_size_type off = 1;
317
134
  size_t i;
318
319
134
  if (bfd_write ("", 1, abfd) != 1)
320
0
    return false;
321
322
12.9k
  for (i = 1; i < tab->size; ++i)
323
12.8k
    {
324
12.8k
      register const char *str;
325
12.8k
      register unsigned int len;
326
327
12.8k
      BFD_ASSERT (tab->array[i]->refcount == 0);
328
12.8k
      len = tab->array[i]->len;
329
12.8k
      if ((int) len <= 0)
330
1.47k
  continue;
331
332
11.3k
      str = tab->array[i]->root.string;
333
11.3k
      if (bfd_write (str, len, abfd) != len)
334
0
  return false;
335
336
11.3k
      off += len;
337
11.3k
    }
338
339
134
  BFD_ASSERT (off == tab->sec_size);
340
134
  return true;
341
134
}
342
343
/* Compare two elf_strtab_hash_entry structures.  Called via qsort.
344
   Won't ever return zero as all entries differ, so there is no issue
345
   with qsort stability here.  */
346
347
static int
348
strrevcmp (const void *a, const void *b)
349
92.8k
{
350
92.8k
  struct elf_strtab_hash_entry *A = *(struct elf_strtab_hash_entry **) a;
351
92.8k
  struct elf_strtab_hash_entry *B = *(struct elf_strtab_hash_entry **) b;
352
92.8k
  unsigned int lenA = A->len;
353
92.8k
  unsigned int lenB = B->len;
354
92.8k
  const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1;
355
92.8k
  const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1;
356
92.8k
  int l = lenA < lenB ? lenA : lenB;
357
358
386k
  while (l)
359
384k
    {
360
384k
      if (*s != *t)
361
91.2k
  return (int) *s - (int) *t;
362
293k
      s--;
363
293k
      t--;
364
293k
      l--;
365
293k
    }
366
1.59k
  return lenA - lenB;
367
92.8k
}
368
369
static inline int
370
is_suffix (const struct elf_strtab_hash_entry *A,
371
     const struct elf_strtab_hash_entry *B)
372
12.6k
{
373
12.6k
  if (A->len <= B->len)
374
    /* B cannot be a suffix of A unless A is equal to B, which is guaranteed
375
       not to be equal by the hash table.  */
376
6.34k
    return 0;
377
378
6.26k
  return memcmp (A->root.string + (A->len - B->len),
379
6.26k
     B->root.string, B->len - 1) == 0;
380
12.6k
}
381
382
/* This function assigns final string table offsets for used strings,
383
   merging strings matching suffixes of longer strings if possible.  */
384
385
void
386
_bfd_elf_strtab_finalize (struct elf_strtab_hash *tab)
387
135
{
388
135
  struct elf_strtab_hash_entry **array, **a, *e;
389
135
  bfd_size_type amt, sec_size;
390
135
  size_t size, i;
391
392
  /* Sort the strings by suffix and length.  */
393
135
  amt = tab->size;
394
135
  amt *= sizeof (struct elf_strtab_hash_entry *);
395
135
  array = (struct elf_strtab_hash_entry **) bfd_malloc (amt);
396
135
  if (array == NULL)
397
0
    goto alloc_failure;
398
399
12.9k
  for (i = 1, a = array; i < tab->size; ++i)
400
12.8k
    {
401
12.8k
      e = tab->array[i];
402
12.8k
      if (e->refcount)
403
12.7k
  {
404
12.7k
    *a++ = e;
405
    /* Adjust the length to not include the zero terminator.  */
406
12.7k
    e->len -= 1;
407
12.7k
  }
408
112
      else
409
112
  e->len = 0;
410
12.8k
    }
411
412
135
  size = a - array;
413
135
  if (size != 0)
414
132
    {
415
132
      qsort (array, size, sizeof (struct elf_strtab_hash_entry *), strrevcmp);
416
417
      /* Loop over the sorted array and merge suffixes.  Start from the
418
   end because we want eg.
419
420
   s1 -> "d"
421
   s2 -> "bcd"
422
   s3 -> "abcd"
423
424
   to end up as
425
426
   s3 -> "abcd"
427
   s2 _____^
428
   s1 _______^
429
430
   ie. we don't want s1 pointing into the old s2.  */
431
132
      e = *--a;
432
132
      e->len += 1;
433
12.7k
      while (--a >= array)
434
12.6k
  {
435
12.6k
    struct elf_strtab_hash_entry *cmp = *a;
436
437
12.6k
    cmp->len += 1;
438
12.6k
    if (is_suffix (e, cmp))
439
1.36k
      {
440
1.36k
        cmp->u.suffix = e;
441
1.36k
        cmp->len = -cmp->len;
442
1.36k
      }
443
11.2k
    else
444
11.2k
      e = cmp;
445
12.6k
  }
446
132
    }
447
448
135
 alloc_failure:
449
135
  free (array);
450
451
  /* Assign positions to the strings we want to keep.  */
452
135
  sec_size = 1;
453
12.9k
  for (i = 1; i < tab->size; ++i)
454
12.8k
    {
455
12.8k
      e = tab->array[i];
456
12.8k
      if (e->refcount && e->len > 0)
457
11.3k
  {
458
11.3k
    e->u.index = sec_size;
459
11.3k
    sec_size += e->len;
460
11.3k
  }
461
12.8k
    }
462
463
135
  tab->sec_size = sec_size;
464
465
  /* Adjust the rest.  */
466
12.9k
  for (i = 1; i < tab->size; ++i)
467
12.8k
    {
468
12.8k
      e = tab->array[i];
469
12.8k
      if (e->refcount && e->len < 0)
470
1.36k
  e->u.index = e->u.suffix->u.index + (e->u.suffix->len + e->len);
471
12.8k
    }
472
135
}