Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5197 | serge | 1 | /* SEC_MERGE support. |
2 | Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
||
3 | Free Software Foundation, Inc. |
||
4 | Written by Jakub Jelinek |
||
5 | |||
6 | This file is part of BFD, the Binary File Descriptor library. |
||
7 | |||
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 |
||
10 | the Free Software Foundation; either version 3 of the License, or |
||
11 | (at your option) any later version. |
||
12 | |||
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. |
||
17 | |||
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 |
||
20 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
||
21 | MA 02110-1301, USA. */ |
||
22 | |||
23 | |||
24 | /* This file contains support for merging duplicate entities within sections, |
||
25 | as used in ELF SHF_MERGE. */ |
||
26 | |||
27 | #include "sysdep.h" |
||
28 | #include "bfd.h" |
||
29 | #include "libbfd.h" |
||
30 | #include "hashtab.h" |
||
31 | #include "libiberty.h" |
||
32 | |||
33 | struct sec_merge_sec_info; |
||
34 | |||
35 | /* An entry in the section merge hash table. */ |
||
36 | |||
37 | struct sec_merge_hash_entry |
||
38 | { |
||
39 | struct bfd_hash_entry root; |
||
40 | /* Length of this entry. This includes the zero terminator. */ |
||
41 | unsigned int len; |
||
42 | /* Start of this string needs to be aligned to |
||
43 | alignment octets (not 1 << align). */ |
||
44 | unsigned int alignment; |
||
45 | union |
||
46 | { |
||
47 | /* Index within the merged section. */ |
||
48 | bfd_size_type index; |
||
49 | /* Entry this is a suffix of (if alignment is 0). */ |
||
50 | struct sec_merge_hash_entry *suffix; |
||
51 | } u; |
||
52 | /* Which section is it in. */ |
||
53 | struct sec_merge_sec_info *secinfo; |
||
54 | /* Next entity in the hash table. */ |
||
55 | struct sec_merge_hash_entry *next; |
||
56 | }; |
||
57 | |||
58 | /* The section merge hash table. */ |
||
59 | |||
60 | struct sec_merge_hash |
||
61 | { |
||
62 | struct bfd_hash_table table; |
||
63 | /* Next available index. */ |
||
64 | bfd_size_type size; |
||
65 | /* First entity in the SEC_MERGE sections of this type. */ |
||
66 | struct sec_merge_hash_entry *first; |
||
67 | /* Last entity in the SEC_MERGE sections of this type. */ |
||
68 | struct sec_merge_hash_entry *last; |
||
69 | /* Entity size. */ |
||
70 | unsigned int entsize; |
||
71 | /* Are entries fixed size or zero terminated strings? */ |
||
72 | bfd_boolean strings; |
||
73 | }; |
||
74 | |||
75 | struct sec_merge_info |
||
76 | { |
||
77 | /* Chain of sec_merge_infos. */ |
||
78 | struct sec_merge_info *next; |
||
79 | /* Chain of sec_merge_sec_infos. */ |
||
80 | struct sec_merge_sec_info *chain; |
||
81 | /* A hash table used to hold section content. */ |
||
82 | struct sec_merge_hash *htab; |
||
83 | }; |
||
84 | |||
85 | struct sec_merge_sec_info |
||
86 | { |
||
87 | /* Chain of sec_merge_sec_infos. */ |
||
88 | struct sec_merge_sec_info *next; |
||
89 | /* The corresponding section. */ |
||
90 | asection *sec; |
||
91 | /* Pointer to merge_info pointing to us. */ |
||
92 | void **psecinfo; |
||
93 | /* A hash table used to hold section content. */ |
||
94 | struct sec_merge_hash *htab; |
||
95 | /* First string in this section. */ |
||
96 | struct sec_merge_hash_entry *first_str; |
||
97 | /* Original section content. */ |
||
98 | unsigned char contents[1]; |
||
99 | }; |
||
100 | |||
101 | |||
102 | /* Routine to create an entry in a section merge hashtab. */ |
||
103 | |||
104 | static struct bfd_hash_entry * |
||
105 | sec_merge_hash_newfunc (struct bfd_hash_entry *entry, |
||
106 | struct bfd_hash_table *table, const char *string) |
||
107 | { |
||
108 | /* Allocate the structure if it has not already been allocated by a |
||
109 | subclass. */ |
||
110 | if (entry == NULL) |
||
111 | entry = (struct bfd_hash_entry *) |
||
112 | bfd_hash_allocate (table, sizeof (struct sec_merge_hash_entry)); |
||
113 | if (entry == NULL) |
||
114 | return NULL; |
||
115 | |||
116 | /* Call the allocation method of the superclass. */ |
||
117 | entry = bfd_hash_newfunc (entry, table, string); |
||
118 | |||
119 | if (entry != NULL) |
||
120 | { |
||
121 | /* Initialize the local fields. */ |
||
122 | struct sec_merge_hash_entry *ret = (struct sec_merge_hash_entry *) entry; |
||
123 | |||
124 | ret->u.suffix = NULL; |
||
125 | ret->alignment = 0; |
||
126 | ret->secinfo = NULL; |
||
127 | ret->next = NULL; |
||
128 | } |
||
129 | |||
130 | return entry; |
||
131 | } |
||
132 | |||
133 | /* Look up an entry in a section merge hash table. */ |
||
134 | |||
135 | static struct sec_merge_hash_entry * |
||
136 | sec_merge_hash_lookup (struct sec_merge_hash *table, const char *string, |
||
137 | unsigned int alignment, bfd_boolean create) |
||
138 | { |
||
139 | const unsigned char *s; |
||
140 | unsigned long hash; |
||
141 | unsigned int c; |
||
142 | struct sec_merge_hash_entry *hashp; |
||
143 | unsigned int len, i; |
||
144 | unsigned int _index; |
||
145 | |||
146 | hash = 0; |
||
147 | len = 0; |
||
148 | s = (const unsigned char *) string; |
||
149 | if (table->strings) |
||
150 | { |
||
151 | if (table->entsize == 1) |
||
152 | { |
||
153 | while ((c = *s++) != '\0') |
||
154 | { |
||
155 | hash += c + (c << 17); |
||
156 | hash ^= hash >> 2; |
||
157 | ++len; |
||
158 | } |
||
159 | hash += len + (len << 17); |
||
160 | } |
||
161 | else |
||
162 | { |
||
163 | for (;;) |
||
164 | { |
||
165 | for (i = 0; i < table->entsize; ++i) |
||
166 | if (s[i] != '\0') |
||
167 | break; |
||
168 | if (i == table->entsize) |
||
169 | break; |
||
170 | for (i = 0; i < table->entsize; ++i) |
||
171 | { |
||
172 | c = *s++; |
||
173 | hash += c + (c << 17); |
||
174 | hash ^= hash >> 2; |
||
175 | } |
||
176 | ++len; |
||
177 | } |
||
178 | hash += len + (len << 17); |
||
179 | len *= table->entsize; |
||
180 | } |
||
181 | hash ^= hash >> 2; |
||
182 | len += table->entsize; |
||
183 | } |
||
184 | else |
||
185 | { |
||
186 | for (i = 0; i < table->entsize; ++i) |
||
187 | { |
||
188 | c = *s++; |
||
189 | hash += c + (c << 17); |
||
190 | hash ^= hash >> 2; |
||
191 | } |
||
192 | len = table->entsize; |
||
193 | } |
||
194 | |||
195 | _index = hash % table->table.size; |
||
196 | for (hashp = (struct sec_merge_hash_entry *) table->table.table[_index]; |
||
197 | hashp != NULL; |
||
198 | hashp = (struct sec_merge_hash_entry *) hashp->root.next) |
||
199 | { |
||
200 | if (hashp->root.hash == hash |
||
201 | && len == hashp->len |
||
202 | && memcmp (hashp->root.string, string, len) == 0) |
||
203 | { |
||
204 | /* If the string we found does not have at least the required |
||
205 | alignment, we need to insert another copy. */ |
||
206 | if (hashp->alignment < alignment) |
||
207 | { |
||
208 | if (create) |
||
209 | { |
||
210 | /* Mark the less aligned copy as deleted. */ |
||
211 | hashp->len = 0; |
||
212 | hashp->alignment = 0; |
||
213 | } |
||
214 | break; |
||
215 | } |
||
216 | return hashp; |
||
217 | } |
||
218 | } |
||
219 | |||
220 | if (! create) |
||
221 | return NULL; |
||
222 | |||
223 | hashp = ((struct sec_merge_hash_entry *) |
||
224 | bfd_hash_insert (&table->table, string, hash)); |
||
225 | if (hashp == NULL) |
||
226 | return NULL; |
||
227 | hashp->len = len; |
||
228 | hashp->alignment = alignment; |
||
229 | return hashp; |
||
230 | } |
||
231 | |||
232 | /* Create a new hash table. */ |
||
233 | |||
234 | static struct sec_merge_hash * |
||
235 | sec_merge_init (unsigned int entsize, bfd_boolean strings) |
||
236 | { |
||
237 | struct sec_merge_hash *table; |
||
238 | |||
239 | table = (struct sec_merge_hash *) bfd_malloc (sizeof (struct sec_merge_hash)); |
||
240 | if (table == NULL) |
||
241 | return NULL; |
||
242 | |||
243 | if (! bfd_hash_table_init_n (&table->table, sec_merge_hash_newfunc, |
||
244 | sizeof (struct sec_merge_hash_entry), 16699)) |
||
245 | { |
||
246 | free (table); |
||
247 | return NULL; |
||
248 | } |
||
249 | |||
250 | table->size = 0; |
||
251 | table->first = NULL; |
||
252 | table->last = NULL; |
||
253 | table->entsize = entsize; |
||
254 | table->strings = strings; |
||
255 | |||
256 | return table; |
||
257 | } |
||
258 | |||
259 | /* Get the index of an entity in a hash table, adding it if it is not |
||
260 | already present. */ |
||
261 | |||
262 | static struct sec_merge_hash_entry * |
||
263 | sec_merge_add (struct sec_merge_hash *tab, const char *str, |
||
264 | unsigned int alignment, struct sec_merge_sec_info *secinfo) |
||
265 | { |
||
266 | struct sec_merge_hash_entry *entry; |
||
267 | |||
268 | entry = sec_merge_hash_lookup (tab, str, alignment, TRUE); |
||
269 | if (entry == NULL) |
||
270 | return NULL; |
||
271 | |||
272 | if (entry->secinfo == NULL) |
||
273 | { |
||
274 | tab->size++; |
||
275 | entry->secinfo = secinfo; |
||
276 | if (tab->first == NULL) |
||
277 | tab->first = entry; |
||
278 | else |
||
279 | tab->last->next = entry; |
||
280 | tab->last = entry; |
||
281 | } |
||
282 | |||
283 | return entry; |
||
284 | } |
||
285 | |||
286 | static bfd_boolean |
||
287 | sec_merge_emit (bfd *abfd, struct sec_merge_hash_entry *entry) |
||
288 | { |
||
289 | struct sec_merge_sec_info *secinfo = entry->secinfo; |
||
290 | asection *sec = secinfo->sec; |
||
291 | char *pad = NULL; |
||
292 | bfd_size_type off = 0; |
||
293 | int alignment_power = sec->output_section->alignment_power; |
||
294 | |||
295 | if (alignment_power) |
||
296 | { |
||
297 | pad = (char *) bfd_zmalloc ((bfd_size_type) 1 << alignment_power); |
||
298 | if (pad == NULL) |
||
299 | return FALSE; |
||
300 | } |
||
301 | |||
302 | for (; entry != NULL && entry->secinfo == secinfo; entry = entry->next) |
||
303 | { |
||
304 | const char *str; |
||
305 | bfd_size_type len; |
||
306 | |||
307 | len = -off & (entry->alignment - 1); |
||
308 | if (len != 0) |
||
309 | { |
||
310 | if (bfd_bwrite (pad, len, abfd) != len) |
||
311 | goto err; |
||
312 | off += len; |
||
313 | } |
||
314 | |||
315 | str = entry->root.string; |
||
316 | len = entry->len; |
||
317 | |||
318 | if (bfd_bwrite (str, len, abfd) != len) |
||
319 | goto err; |
||
320 | |||
321 | off += len; |
||
322 | } |
||
323 | |||
324 | /* Trailing alignment needed? */ |
||
325 | off = sec->size - off; |
||
326 | if (off != 0 |
||
327 | && bfd_bwrite (pad, off, abfd) != off) |
||
328 | goto err; |
||
329 | |||
330 | if (pad != NULL) |
||
331 | free (pad); |
||
332 | return TRUE; |
||
333 | |||
334 | err: |
||
335 | if (pad != NULL) |
||
336 | free (pad); |
||
337 | return FALSE; |
||
338 | } |
||
339 | |||
340 | /* Register a SEC_MERGE section as a candidate for merging. |
||
341 | This function is called for all non-dynamic SEC_MERGE input sections. */ |
||
342 | |||
343 | bfd_boolean |
||
344 | _bfd_add_merge_section (bfd *abfd, void **psinfo, asection *sec, |
||
345 | void **psecinfo) |
||
346 | { |
||
347 | struct sec_merge_info *sinfo; |
||
348 | struct sec_merge_sec_info *secinfo; |
||
349 | unsigned int align; |
||
350 | bfd_size_type amt; |
||
351 | bfd_byte *contents; |
||
352 | |||
353 | if ((abfd->flags & DYNAMIC) != 0 |
||
354 | || (sec->flags & SEC_MERGE) == 0) |
||
355 | abort (); |
||
356 | |||
357 | if (sec->size == 0 |
||
358 | || (sec->flags & SEC_EXCLUDE) != 0 |
||
359 | || sec->entsize == 0) |
||
360 | return TRUE; |
||
361 | |||
362 | if ((sec->flags & SEC_RELOC) != 0) |
||
363 | { |
||
364 | /* We aren't prepared to handle relocations in merged sections. */ |
||
365 | return TRUE; |
||
366 | } |
||
367 | |||
368 | align = sec->alignment_power; |
||
369 | if ((sec->entsize < (unsigned) 1 << align |
||
370 | && ((sec->entsize & (sec->entsize - 1)) |
||
371 | || !(sec->flags & SEC_STRINGS))) |
||
372 | || (sec->entsize > (unsigned) 1 << align |
||
373 | && (sec->entsize & (((unsigned) 1 << align) - 1)))) |
||
374 | { |
||
375 | /* Sanity check. If string character size is smaller than |
||
376 | alignment, then we require character size to be a power |
||
377 | of 2, otherwise character size must be integer multiple |
||
378 | of alignment. For non-string constants, alignment must |
||
379 | be smaller than or equal to entity size and entity size |
||
380 | must be integer multiple of alignment. */ |
||
381 | return TRUE; |
||
382 | } |
||
383 | |||
384 | for (sinfo = (struct sec_merge_info *) *psinfo; sinfo; sinfo = sinfo->next) |
||
385 | if ((secinfo = sinfo->chain) |
||
386 | && ! ((secinfo->sec->flags ^ sec->flags) & (SEC_MERGE | SEC_STRINGS)) |
||
387 | && secinfo->sec->entsize == sec->entsize |
||
388 | && secinfo->sec->alignment_power == sec->alignment_power |
||
389 | && secinfo->sec->output_section == sec->output_section) |
||
390 | break; |
||
391 | |||
392 | if (sinfo == NULL) |
||
393 | { |
||
394 | /* Initialize the information we need to keep track of. */ |
||
395 | sinfo = (struct sec_merge_info *) |
||
396 | bfd_alloc (abfd, sizeof (struct sec_merge_info)); |
||
397 | if (sinfo == NULL) |
||
398 | goto error_return; |
||
399 | sinfo->next = (struct sec_merge_info *) *psinfo; |
||
400 | sinfo->chain = NULL; |
||
401 | *psinfo = sinfo; |
||
402 | sinfo->htab = sec_merge_init (sec->entsize, (sec->flags & SEC_STRINGS)); |
||
403 | if (sinfo->htab == NULL) |
||
404 | goto error_return; |
||
405 | } |
||
406 | |||
407 | /* Read the section from abfd. */ |
||
408 | |||
409 | amt = sizeof (struct sec_merge_sec_info) - 1 + sec->size; |
||
410 | if (sec->flags & SEC_STRINGS) |
||
411 | /* Some versions of gcc may emit a string without a zero terminator. |
||
412 | See http://gcc.gnu.org/ml/gcc-patches/2006-06/msg01004.html |
||
413 | Allocate space for an extra zero. */ |
||
414 | amt += sec->entsize; |
||
415 | *psecinfo = bfd_alloc (abfd, amt); |
||
416 | if (*psecinfo == NULL) |
||
417 | goto error_return; |
||
418 | |||
419 | secinfo = (struct sec_merge_sec_info *) *psecinfo; |
||
420 | if (sinfo->chain) |
||
421 | { |
||
422 | secinfo->next = sinfo->chain->next; |
||
423 | sinfo->chain->next = secinfo; |
||
424 | } |
||
425 | else |
||
426 | secinfo->next = secinfo; |
||
427 | sinfo->chain = secinfo; |
||
428 | secinfo->sec = sec; |
||
429 | secinfo->psecinfo = psecinfo; |
||
430 | secinfo->htab = sinfo->htab; |
||
431 | secinfo->first_str = NULL; |
||
432 | |||
433 | sec->rawsize = sec->size; |
||
434 | if (sec->flags & SEC_STRINGS) |
||
435 | memset (secinfo->contents + sec->size, 0, sec->entsize); |
||
436 | contents = secinfo->contents; |
||
437 | if (! bfd_get_full_section_contents (sec->owner, sec, &contents)) |
||
438 | goto error_return; |
||
439 | |||
440 | return TRUE; |
||
441 | |||
442 | error_return: |
||
443 | *psecinfo = NULL; |
||
444 | return FALSE; |
||
445 | } |
||
446 | |||
447 | /* Record one section into the hash table. */ |
||
448 | static bfd_boolean |
||
449 | record_section (struct sec_merge_info *sinfo, |
||
450 | struct sec_merge_sec_info *secinfo) |
||
451 | { |
||
452 | asection *sec = secinfo->sec; |
||
453 | struct sec_merge_hash_entry *entry; |
||
454 | bfd_boolean nul; |
||
455 | unsigned char *p, *end; |
||
456 | bfd_vma mask, eltalign; |
||
457 | unsigned int align, i; |
||
458 | |||
459 | align = sec->alignment_power; |
||
460 | end = secinfo->contents + sec->size; |
||
461 | nul = FALSE; |
||
462 | mask = ((bfd_vma) 1 << align) - 1; |
||
463 | if (sec->flags & SEC_STRINGS) |
||
464 | { |
||
465 | for (p = secinfo->contents; p < end; ) |
||
466 | { |
||
467 | eltalign = p - secinfo->contents; |
||
468 | eltalign = ((eltalign ^ (eltalign - 1)) + 1) >> 1; |
||
469 | if (!eltalign || eltalign > mask) |
||
470 | eltalign = mask + 1; |
||
471 | entry = sec_merge_add (sinfo->htab, (char *) p, (unsigned) eltalign, |
||
472 | secinfo); |
||
473 | if (! entry) |
||
474 | goto error_return; |
||
475 | p += entry->len; |
||
476 | if (sec->entsize == 1) |
||
477 | { |
||
478 | while (p < end && *p == 0) |
||
479 | { |
||
480 | if (!nul && !((p - secinfo->contents) & mask)) |
||
481 | { |
||
482 | nul = TRUE; |
||
483 | entry = sec_merge_add (sinfo->htab, "", |
||
484 | (unsigned) mask + 1, secinfo); |
||
485 | if (! entry) |
||
486 | goto error_return; |
||
487 | } |
||
488 | p++; |
||
489 | } |
||
490 | } |
||
491 | else |
||
492 | { |
||
493 | while (p < end) |
||
494 | { |
||
495 | for (i = 0; i < sec->entsize; i++) |
||
496 | if (p[i] != '\0') |
||
497 | break; |
||
498 | if (i != sec->entsize) |
||
499 | break; |
||
500 | if (!nul && !((p - secinfo->contents) & mask)) |
||
501 | { |
||
502 | nul = TRUE; |
||
503 | entry = sec_merge_add (sinfo->htab, (char *) p, |
||
504 | (unsigned) mask + 1, secinfo); |
||
505 | if (! entry) |
||
506 | goto error_return; |
||
507 | } |
||
508 | p += sec->entsize; |
||
509 | } |
||
510 | } |
||
511 | } |
||
512 | } |
||
513 | else |
||
514 | { |
||
515 | for (p = secinfo->contents; p < end; p += sec->entsize) |
||
516 | { |
||
517 | entry = sec_merge_add (sinfo->htab, (char *) p, 1, secinfo); |
||
518 | if (! entry) |
||
519 | goto error_return; |
||
520 | } |
||
521 | } |
||
522 | |||
523 | return TRUE; |
||
524 | |||
525 | error_return: |
||
526 | for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) |
||
527 | *secinfo->psecinfo = NULL; |
||
528 | return FALSE; |
||
529 | } |
||
530 | |||
531 | static int |
||
532 | strrevcmp (const void *a, const void *b) |
||
533 | { |
||
534 | struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; |
||
535 | struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; |
||
536 | unsigned int lenA = A->len; |
||
537 | unsigned int lenB = B->len; |
||
538 | const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1; |
||
539 | const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1; |
||
540 | int l = lenA < lenB ? lenA : lenB; |
||
541 | |||
542 | while (l) |
||
543 | { |
||
544 | if (*s != *t) |
||
545 | return (int) *s - (int) *t; |
||
546 | s--; |
||
547 | t--; |
||
548 | l--; |
||
549 | } |
||
550 | return lenA - lenB; |
||
551 | } |
||
552 | |||
553 | /* Like strrevcmp, but for the case where all strings have the same |
||
554 | alignment > entsize. */ |
||
555 | |||
556 | static int |
||
557 | strrevcmp_align (const void *a, const void *b) |
||
558 | { |
||
559 | struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; |
||
560 | struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; |
||
561 | unsigned int lenA = A->len; |
||
562 | unsigned int lenB = B->len; |
||
563 | const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1; |
||
564 | const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1; |
||
565 | int l = lenA < lenB ? lenA : lenB; |
||
566 | int tail_align = (lenA & (A->alignment - 1)) - (lenB & (A->alignment - 1)); |
||
567 | |||
568 | if (tail_align != 0) |
||
569 | return tail_align; |
||
570 | |||
571 | while (l) |
||
572 | { |
||
573 | if (*s != *t) |
||
574 | return (int) *s - (int) *t; |
||
575 | s--; |
||
576 | t--; |
||
577 | l--; |
||
578 | } |
||
579 | return lenA - lenB; |
||
580 | } |
||
581 | |||
582 | static inline int |
||
583 | is_suffix (const struct sec_merge_hash_entry *A, |
||
584 | const struct sec_merge_hash_entry *B) |
||
585 | { |
||
586 | if (A->len <= B->len) |
||
587 | /* B cannot be a suffix of A unless A is equal to B, which is guaranteed |
||
588 | not to be equal by the hash table. */ |
||
589 | return 0; |
||
590 | |||
591 | return memcmp (A->root.string + (A->len - B->len), |
||
592 | B->root.string, B->len) == 0; |
||
593 | } |
||
594 | |||
595 | /* This is a helper function for _bfd_merge_sections. It attempts to |
||
596 | merge strings matching suffixes of longer strings. */ |
||
597 | static void |
||
598 | merge_strings (struct sec_merge_info *sinfo) |
||
599 | { |
||
600 | struct sec_merge_hash_entry **array, **a, *e; |
||
601 | struct sec_merge_sec_info *secinfo; |
||
602 | bfd_size_type size, amt; |
||
603 | unsigned int alignment = 0; |
||
604 | |||
605 | /* Now sort the strings */ |
||
606 | amt = sinfo->htab->size * sizeof (struct sec_merge_hash_entry *); |
||
607 | array = (struct sec_merge_hash_entry **) bfd_malloc (amt); |
||
608 | if (array == NULL) |
||
609 | goto alloc_failure; |
||
610 | |||
611 | for (e = sinfo->htab->first, a = array; e; e = e->next) |
||
612 | if (e->alignment) |
||
613 | { |
||
614 | *a++ = e; |
||
615 | /* Adjust the length to not include the zero terminator. */ |
||
616 | e->len -= sinfo->htab->entsize; |
||
617 | if (alignment != e->alignment) |
||
618 | { |
||
619 | if (alignment == 0) |
||
620 | alignment = e->alignment; |
||
621 | else |
||
622 | alignment = (unsigned) -1; |
||
623 | } |
||
624 | } |
||
625 | |||
626 | sinfo->htab->size = a - array; |
||
627 | if (sinfo->htab->size != 0) |
||
628 | { |
||
629 | qsort (array, (size_t) sinfo->htab->size, |
||
630 | sizeof (struct sec_merge_hash_entry *), |
||
631 | (alignment != (unsigned) -1 && alignment > sinfo->htab->entsize |
||
632 | ? strrevcmp_align : strrevcmp)); |
||
633 | |||
634 | /* Loop over the sorted array and merge suffixes */ |
||
635 | e = *--a; |
||
636 | e->len += sinfo->htab->entsize; |
||
637 | while (--a >= array) |
||
638 | { |
||
639 | struct sec_merge_hash_entry *cmp = *a; |
||
640 | |||
641 | cmp->len += sinfo->htab->entsize; |
||
642 | if (e->alignment >= cmp->alignment |
||
643 | && !((e->len - cmp->len) & (cmp->alignment - 1)) |
||
644 | && is_suffix (e, cmp)) |
||
645 | { |
||
646 | cmp->u.suffix = e; |
||
647 | cmp->alignment = 0; |
||
648 | } |
||
649 | else |
||
650 | e = cmp; |
||
651 | } |
||
652 | } |
||
653 | |||
654 | alloc_failure: |
||
655 | if (array) |
||
656 | free (array); |
||
657 | |||
658 | /* Now assign positions to the strings we want to keep. */ |
||
659 | size = 0; |
||
660 | secinfo = sinfo->htab->first->secinfo; |
||
661 | for (e = sinfo->htab->first; e; e = e->next) |
||
662 | { |
||
663 | if (e->secinfo != secinfo) |
||
664 | { |
||
665 | secinfo->sec->size = size; |
||
666 | secinfo = e->secinfo; |
||
667 | } |
||
668 | if (e->alignment) |
||
669 | { |
||
670 | if (e->secinfo->first_str == NULL) |
||
671 | { |
||
672 | e->secinfo->first_str = e; |
||
673 | size = 0; |
||
674 | } |
||
675 | size = (size + e->alignment - 1) & ~((bfd_vma) e->alignment - 1); |
||
676 | e->u.index = size; |
||
677 | size += e->len; |
||
678 | } |
||
679 | } |
||
680 | secinfo->sec->size = size; |
||
681 | if (secinfo->sec->alignment_power != 0) |
||
682 | { |
||
683 | bfd_size_type align = (bfd_size_type) 1 << secinfo->sec->alignment_power; |
||
684 | secinfo->sec->size = (secinfo->sec->size + align - 1) & -align; |
||
685 | } |
||
686 | |||
687 | /* And now adjust the rest, removing them from the chain (but not hashtable) |
||
688 | at the same time. */ |
||
689 | for (a = &sinfo->htab->first, e = *a; e; e = e->next) |
||
690 | if (e->alignment) |
||
691 | a = &e->next; |
||
692 | else |
||
693 | { |
||
694 | *a = e->next; |
||
695 | if (e->len) |
||
696 | { |
||
697 | e->secinfo = e->u.suffix->secinfo; |
||
698 | e->alignment = e->u.suffix->alignment; |
||
699 | e->u.index = e->u.suffix->u.index + (e->u.suffix->len - e->len); |
||
700 | } |
||
701 | } |
||
702 | } |
||
703 | |||
704 | /* This function is called once after all SEC_MERGE sections are registered |
||
705 | with _bfd_merge_section. */ |
||
706 | |||
707 | bfd_boolean |
||
708 | _bfd_merge_sections (bfd *abfd, |
||
709 | struct bfd_link_info *info ATTRIBUTE_UNUSED, |
||
710 | void *xsinfo, |
||
711 | void (*remove_hook) (bfd *, asection *)) |
||
712 | { |
||
713 | struct sec_merge_info *sinfo; |
||
714 | |||
715 | for (sinfo = (struct sec_merge_info *) xsinfo; sinfo; sinfo = sinfo->next) |
||
716 | { |
||
717 | struct sec_merge_sec_info * secinfo; |
||
718 | |||
719 | if (! sinfo->chain) |
||
720 | continue; |
||
721 | |||
722 | /* Move sinfo->chain to head of the chain, terminate it. */ |
||
723 | secinfo = sinfo->chain; |
||
724 | sinfo->chain = secinfo->next; |
||
725 | secinfo->next = NULL; |
||
726 | |||
727 | /* Record the sections into the hash table. */ |
||
728 | for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) |
||
729 | if (secinfo->sec->flags & SEC_EXCLUDE) |
||
730 | { |
||
731 | *secinfo->psecinfo = NULL; |
||
732 | if (remove_hook) |
||
733 | (*remove_hook) (abfd, secinfo->sec); |
||
734 | } |
||
735 | else if (! record_section (sinfo, secinfo)) |
||
736 | break; |
||
737 | |||
738 | if (secinfo) |
||
739 | continue; |
||
740 | |||
741 | if (sinfo->htab->first == NULL) |
||
742 | continue; |
||
743 | |||
744 | if (sinfo->htab->strings) |
||
745 | merge_strings (sinfo); |
||
746 | else |
||
747 | { |
||
748 | struct sec_merge_hash_entry *e; |
||
749 | bfd_size_type size = 0; |
||
750 | |||
751 | /* Things are much simpler for non-strings. |
||
752 | Just assign them slots in the section. */ |
||
753 | secinfo = NULL; |
||
754 | for (e = sinfo->htab->first; e; e = e->next) |
||
755 | { |
||
756 | if (e->secinfo->first_str == NULL) |
||
757 | { |
||
758 | if (secinfo) |
||
759 | secinfo->sec->size = size; |
||
760 | e->secinfo->first_str = e; |
||
761 | size = 0; |
||
762 | } |
||
763 | size = (size + e->alignment - 1) |
||
764 | & ~((bfd_vma) e->alignment - 1); |
||
765 | e->u.index = size; |
||
766 | size += e->len; |
||
767 | secinfo = e->secinfo; |
||
768 | } |
||
769 | secinfo->sec->size = size; |
||
770 | } |
||
771 | |||
772 | /* Finally remove all input sections which have not made it into |
||
773 | the hash table at all. */ |
||
774 | for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) |
||
775 | if (secinfo->first_str == NULL) |
||
776 | secinfo->sec->flags |= SEC_EXCLUDE | SEC_KEEP; |
||
777 | } |
||
778 | |||
779 | return TRUE; |
||
780 | } |
||
781 | |||
782 | /* Write out the merged section. */ |
||
783 | |||
784 | bfd_boolean |
||
785 | _bfd_write_merged_section (bfd *output_bfd, asection *sec, void *psecinfo) |
||
786 | { |
||
787 | struct sec_merge_sec_info *secinfo; |
||
788 | file_ptr pos; |
||
789 | |||
790 | secinfo = (struct sec_merge_sec_info *) psecinfo; |
||
791 | |||
792 | if (!secinfo) |
||
793 | return FALSE; |
||
794 | |||
795 | if (secinfo->first_str == NULL) |
||
796 | return TRUE; |
||
797 | |||
798 | /* FIXME: octets_per_byte. */ |
||
799 | pos = sec->output_section->filepos + sec->output_offset; |
||
800 | if (bfd_seek (output_bfd, pos, SEEK_SET) != 0) |
||
801 | return FALSE; |
||
802 | |||
803 | if (! sec_merge_emit (output_bfd, secinfo->first_str)) |
||
804 | return FALSE; |
||
805 | |||
806 | return TRUE; |
||
807 | } |
||
808 | |||
809 | /* Adjust an address in the SEC_MERGE section. Given OFFSET within |
||
810 | *PSEC, this returns the new offset in the adjusted SEC_MERGE |
||
811 | section and writes the new section back into *PSEC. */ |
||
812 | |||
813 | bfd_vma |
||
814 | _bfd_merged_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, asection **psec, |
||
815 | void *psecinfo, bfd_vma offset) |
||
816 | { |
||
817 | struct sec_merge_sec_info *secinfo; |
||
818 | struct sec_merge_hash_entry *entry; |
||
819 | unsigned char *p; |
||
820 | asection *sec = *psec; |
||
821 | |||
822 | secinfo = (struct sec_merge_sec_info *) psecinfo; |
||
823 | |||
824 | if (!secinfo) |
||
825 | return offset; |
||
826 | |||
827 | if (offset >= sec->rawsize) |
||
828 | { |
||
829 | if (offset > sec->rawsize) |
||
830 | { |
||
831 | (*_bfd_error_handler) |
||
832 | (_("%s: access beyond end of merged section (%ld)"), |
||
833 | bfd_get_filename (sec->owner), (long) offset); |
||
834 | } |
||
835 | return secinfo->first_str ? sec->size : 0; |
||
836 | } |
||
837 | |||
838 | if (secinfo->htab->strings) |
||
839 | { |
||
840 | if (sec->entsize == 1) |
||
841 | { |
||
842 | p = secinfo->contents + offset - 1; |
||
843 | while (p >= secinfo->contents && *p) |
||
844 | --p; |
||
845 | ++p; |
||
846 | } |
||
847 | else |
||
848 | { |
||
849 | p = secinfo->contents + (offset / sec->entsize) * sec->entsize; |
||
850 | p -= sec->entsize; |
||
851 | while (p >= secinfo->contents) |
||
852 | { |
||
853 | unsigned int i; |
||
854 | |||
855 | for (i = 0; i < sec->entsize; ++i) |
||
856 | if (p[i] != '\0') |
||
857 | break; |
||
858 | if (i == sec->entsize) |
||
859 | break; |
||
860 | p -= sec->entsize; |
||
861 | } |
||
862 | p += sec->entsize; |
||
863 | } |
||
864 | } |
||
865 | else |
||
866 | { |
||
867 | p = secinfo->contents + (offset / sec->entsize) * sec->entsize; |
||
868 | } |
||
869 | entry = sec_merge_hash_lookup (secinfo->htab, (char *) p, 0, FALSE); |
||
870 | if (!entry) |
||
871 | { |
||
872 | if (! secinfo->htab->strings) |
||
873 | abort (); |
||
874 | /* This should only happen if somebody points into the padding |
||
875 | after a NUL character but before next entity. */ |
||
876 | if (*p) |
||
877 | abort (); |
||
878 | if (! secinfo->htab->first) |
||
879 | abort (); |
||
880 | entry = secinfo->htab->first; |
||
881 | p = (secinfo->contents + (offset / sec->entsize + 1) * sec->entsize |
||
882 | - entry->len); |
||
883 | } |
||
884 | |||
885 | *psec = entry->secinfo->sec; |
||
886 | return entry->u.index + (secinfo->contents + offset - p); |
||
887 | } |
||
888 | |||
889 | /* Tidy up when done. */ |
||
890 | |||
891 | void |
||
892 | _bfd_merge_sections_free (void *xsinfo) |
||
893 | { |
||
894 | struct sec_merge_info *sinfo; |
||
895 | |||
896 | for (sinfo = (struct sec_merge_info *) xsinfo; sinfo; sinfo = sinfo->next) |
||
897 | { |
||
898 | bfd_hash_table_free (&sinfo->htab->table); |
||
899 | free (sinfo->htab); |
||
900 | } |
||
901 | }>><>=>>>>>>>>><>><>><>><>>><>>><>>><>><>>>><>><>><> |