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