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