X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Fmerge.c;h=de1e9510819577628538e4ebfa856e021eaa199c;hb=c5e38e547e14e9f282c9d98a7c70272cecdcac10;hp=ace069eca0d6f1c185956fa93a5de8d91f1146a1;hpb=f5fa8ca231d47662321addbfbde105e2bed0be07;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/merge.c b/bfd/merge.c index ace069eca0..de1e951081 100644 --- a/bfd/merge.c +++ b/bfd/merge.c @@ -1,43 +1,56 @@ /* SEC_MERGE support. - Copyright 2001 Free Software Foundation, Inc. + Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Written by Jakub Jelinek . -This file is part of BFD, the Binary File Descriptor library. + This file is part of BFD, the Binary File Descriptor library. -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This file contains support for merging duplicate entities within sections, as used in ELF SHF_MERGE. */ -#include "bfd.h" #include "sysdep.h" +#include "bfd.h" #include "libbfd.h" +#include "hashtab.h" +#include "libiberty.h" -#include +struct sec_merge_sec_info; /* An entry in the section merge hash table. */ struct sec_merge_hash_entry { struct bfd_hash_entry root; - /* Length of this entry. */ + /* Length of this entry. This includes the zero terminator. */ unsigned int len; - /* Index within the merged section. */ - bfd_size_type index; + /* Start of this string needs to be aligned to + alignment octets (not 1 << align). */ + unsigned int alignment; + union + { + /* Index within the merged section. */ + bfd_size_type index; + /* Entry this is a suffix of (if alignment is 0). */ + struct sec_merge_hash_entry *suffix; + } u; /* Which section is it in. */ - asection *sec; + struct sec_merge_sec_info *secinfo; /* Next entity in the hash table. */ struct sec_merge_hash_entry *next; }; @@ -55,84 +68,72 @@ struct sec_merge_hash struct sec_merge_hash_entry *last; /* Entity size. */ unsigned int entsize; - /* Start of each string needs to be aligned to 1 << alignment_power - octets. */ - unsigned int alignment_power; /* Are entries fixed size or zero terminated strings? */ - boolean strings; + bfd_boolean strings; }; struct sec_merge_info { /* Chain of sec_merge_infos. */ struct sec_merge_info *next; + /* Chain of sec_merge_sec_infos. */ + struct sec_merge_sec_info *chain; /* A hash table used to hold section content. */ struct sec_merge_hash *htab; - /* The last section attempted for merge. */ - asection *last; }; struct sec_merge_sec_info { + /* Chain of sec_merge_sec_infos. */ + struct sec_merge_sec_info *next; + /* The corresponding section. */ + asection *sec; + /* Pointer to merge_info pointing to us. */ + void **psecinfo; /* A hash table used to hold section content. */ struct sec_merge_hash *htab; /* First string in this section. */ - struct sec_merge_hash_entry *first; + struct sec_merge_hash_entry *first_str; /* Original section content. */ unsigned char contents[1]; }; -static struct bfd_hash_entry *sec_merge_hash_newfunc - PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); -static struct sec_merge_hash_entry *sec_merge_hash_lookup - PARAMS ((struct sec_merge_hash *, const char *, boolean)); -static struct sec_merge_hash *sec_merge_init - PARAMS ((unsigned int, unsigned int, boolean)); -static struct sec_merge_hash_entry *sec_merge_add - PARAMS ((struct sec_merge_hash *, const char *)); -static boolean sec_merge_emit - PARAMS ((bfd *, struct sec_merge_hash *, struct sec_merge_hash_entry *)); /* Routine to create an entry in a section merge hashtab. */ static struct bfd_hash_entry * -sec_merge_hash_newfunc (entry, table, string) - struct bfd_hash_entry *entry; - struct bfd_hash_table *table; - const char *string; +sec_merge_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) { - struct sec_merge_hash_entry *ret = (struct sec_merge_hash_entry *) entry; - /* Allocate the structure if it has not already been allocated by a subclass. */ - if (ret == (struct sec_merge_hash_entry *) NULL) - ret = ((struct sec_merge_hash_entry *) - bfd_hash_allocate (table, sizeof (struct sec_merge_hash_entry))); - if (ret == (struct sec_merge_hash_entry *) NULL) + if (entry == NULL) + entry = bfd_hash_allocate (table, sizeof (struct sec_merge_hash_entry)); + if (entry == NULL) return NULL; /* Call the allocation method of the superclass. */ - ret = ((struct sec_merge_hash_entry *) - bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + entry = bfd_hash_newfunc (entry, table, string); - if (ret) + if (entry != NULL) { /* Initialize the local fields. */ - ret->index = (bfd_size_type) -1; - ret->sec = NULL; + struct sec_merge_hash_entry *ret = (struct sec_merge_hash_entry *) entry; + + ret->u.suffix = NULL; + ret->alignment = 0; + ret->secinfo = NULL; ret->next = NULL; } - return (struct bfd_hash_entry *)ret; + return entry; } /* Look up an entry in a section merge hash table. */ static struct sec_merge_hash_entry * -sec_merge_hash_lookup (table, string, create) - struct sec_merge_hash *table; - const char *string; - boolean create; +sec_merge_hash_lookup (struct sec_merge_hash *table, const char *string, + unsigned int alignment, bfd_boolean create) { register const unsigned char *s; register unsigned long hash; @@ -178,7 +179,7 @@ sec_merge_hash_lookup (table, string, create) } hash ^= hash >> 2; len += table->entsize; - } + } else { for (i = 0; i < table->entsize; ++i) @@ -192,47 +193,54 @@ sec_merge_hash_lookup (table, string, create) index = hash % table->table.size; for (hashp = (struct sec_merge_hash_entry *) table->table.table[index]; - hashp != (struct sec_merge_hash_entry *) NULL; - hashp = (struct sec_merge_hash_entry *) hashp->next) + hashp != NULL; + hashp = (struct sec_merge_hash_entry *) hashp->root.next) { if (hashp->root.hash == hash && len == hashp->len && memcmp (hashp->root.string, string, len) == 0) - return hashp; + { + /* If the string we found does not have at least the required + alignment, we need to insert another copy. */ + if (hashp->alignment < alignment) + { + if (create) + { + /* Mark the less aligned copy as deleted. */ + hashp->len = 0; + hashp->alignment = 0; + } + break; + } + return hashp; + } } if (! create) - return (struct sec_merge_hash_entry *) NULL; - - hashp = (struct sec_merge_hash_entry *) - sec_merge_hash_newfunc ((struct bfd_hash_entry *) NULL, - (struct bfd_hash_table *) table, string); - if (hashp == (struct sec_merge_hash_entry *) NULL) - return (struct sec_merge_hash_entry *) NULL; - hashp->root.string = string; - hashp->root.hash = hash; - hashp->len = len; - hashp->root.next = table->table.table[index]; - table->table.table[index] = (struct bfd_hash_entry *) hashp; + return NULL; + hashp = ((struct sec_merge_hash_entry *) + bfd_hash_insert (&table->table, string, hash)); + if (hashp == NULL) + return NULL; + hashp->len = len; + hashp->alignment = alignment; return hashp; } /* Create a new hash table. */ static struct sec_merge_hash * -sec_merge_init (alignment_power, entsize, strings) - unsigned int alignment_power, entsize; - boolean strings; +sec_merge_init (unsigned int entsize, bfd_boolean strings) { struct sec_merge_hash *table; - table = ((struct sec_merge_hash *) - bfd_malloc (sizeof (struct sec_merge_hash))); + table = bfd_malloc (sizeof (struct sec_merge_hash)); if (table == NULL) return NULL; - if (! bfd_hash_table_init (&table->table, sec_merge_hash_newfunc)) + if (! bfd_hash_table_init_n (&table->table, sec_merge_hash_newfunc, + sizeof (struct sec_merge_hash_entry), 16699)) { free (table); return NULL; @@ -241,7 +249,6 @@ sec_merge_init (alignment_power, entsize, strings) table->size = 0; table->first = NULL; table->last = NULL; - table->alignment_power = alignment_power; table->entsize = entsize; table->strings = strings; @@ -252,21 +259,19 @@ sec_merge_init (alignment_power, entsize, strings) already present. */ static struct sec_merge_hash_entry * -sec_merge_add (tab, str) - struct sec_merge_hash *tab; - const char *str; +sec_merge_add (struct sec_merge_hash *tab, const char *str, + unsigned int alignment, struct sec_merge_sec_info *secinfo) { register struct sec_merge_hash_entry *entry; - entry = sec_merge_hash_lookup (tab, str, true); + entry = sec_merge_hash_lookup (tab, str, alignment, TRUE); if (entry == NULL) return NULL; - if (entry->index == (bfd_size_type) -1) + if (entry->secinfo == NULL) { - entry->index = tab->size; - tab->size += entry->len; - tab->size = align_power (tab->size, tab->alignment_power); + tab->size++; + entry->secinfo = secinfo; if (tab->first == NULL) tab->first = entry; else @@ -277,154 +282,193 @@ sec_merge_add (tab, str) return entry; } -static boolean -sec_merge_emit (abfd, tab, entry) - register bfd *abfd; - struct sec_merge_hash *tab; - struct sec_merge_hash_entry *entry; +static bfd_boolean +sec_merge_emit (bfd *abfd, struct sec_merge_hash_entry *entry) { - asection *sec = entry->sec; - char *pad = ""; + struct sec_merge_sec_info *secinfo = entry->secinfo; + asection *sec = secinfo->sec; + char *pad = NULL; + bfd_size_type off = 0; + int alignment_power = sec->output_section->alignment_power; - if (tab->alignment_power) - pad = bfd_zmalloc (1 << tab->alignment_power); + if (alignment_power) + { + pad = bfd_zmalloc ((bfd_size_type) 1 << alignment_power); + if (pad == NULL) + return FALSE; + } - for (; entry != NULL && entry->sec == sec; entry = entry->next) + for (; entry != NULL && entry->secinfo == secinfo; entry = entry->next) { - register const char *str; - register size_t len; + const char *str; + bfd_size_type len; + + len = -off & (entry->alignment - 1); + if (len != 0) + { + if (bfd_bwrite (pad, len, abfd) != len) + goto err; + off += len; + } str = entry->root.string; len = entry->len; - if (bfd_write ((PTR) str, 1, len, abfd) != len) - break; + if (bfd_bwrite (str, len, abfd) != len) + goto err; - if (tab->alignment_power) - { - len &= (1 << tab->alignment_power) - 1; - if (len && bfd_write ((PTR) pad, 1, - (1 << tab->alignment_power) - len, - abfd) != (1 << tab->alignment_power) - len) - break; - } + off += len; } - if (tab->alignment_power) + /* Trailing alignment needed? */ + off = sec->size - off; + if (off != 0 + && bfd_bwrite (pad, off, abfd) != off) + goto err; + + if (pad != NULL) free (pad); + return TRUE; - return entry == NULL || entry->sec != sec; + err: + if (pad != NULL) + free (pad); + return FALSE; } -/* This function is called for each input file from the add_symbols - pass of the linker. */ +/* Register a SEC_MERGE section as a candidate for merging. + This function is called for all non-dynamic SEC_MERGE input sections. */ -boolean -_bfd_merge_section (abfd, psinfo, sec, psecinfo) - bfd *abfd; - PTR *psinfo; - asection *sec; - PTR *psecinfo; +bfd_boolean +_bfd_add_merge_section (bfd *abfd, void **psinfo, asection *sec, + void **psecinfo) { - boolean first, nul; struct sec_merge_info *sinfo; struct sec_merge_sec_info *secinfo; - unsigned char *p, *end; - bfd_vma mask; unsigned int align; - unsigned int i; + bfd_size_type amt; + + if ((abfd->flags & DYNAMIC) != 0 + || (sec->flags & SEC_MERGE) == 0) + abort (); - if (sec->_raw_size == 0 - || (sec->flags & SEC_MERGE) == 0 + if (sec->size == 0 + || (sec->flags & SEC_EXCLUDE) != 0 || sec->entsize == 0) - return true; + return TRUE; if ((sec->flags & SEC_RELOC) != 0) { /* We aren't prepared to handle relocations in merged sections. */ - return true; + return TRUE; } - if (sec->output_section != NULL - && bfd_is_abs_section (sec->output_section)) - { - /* The section is being discarded from the link, so we should - just ignore it. */ - return true; - } - - align = bfd_get_section_alignment (abfd, sec); - if ((sec->flags & SEC_STRINGS) - && ((sec->entsize < (unsigned int)(1 << align) - && (sec->entsize & (sec->entsize - 1))) - || (sec->entsize > (unsigned int)(1 << align) - && (sec->entsize & ((1 << align) - 1))))) + align = sec->alignment_power; + if ((sec->entsize < (unsigned) 1 << align + && ((sec->entsize & (sec->entsize - 1)) + || !(sec->flags & SEC_STRINGS))) + || (sec->entsize > (unsigned) 1 << align + && (sec->entsize & (((unsigned) 1 << align) - 1)))) { /* Sanity check. If string character size is smaller than alignment, then we require character size to be a power of 2, otherwise character size must be integer multiple - of alignment. */ - return true; + of alignment. For non-string constants, alignment must + be smaller than or equal to entity size and entity size + must be integer multiple of alignment. */ + return TRUE; } - first = false; - for (sinfo = (struct sec_merge_info *) *psinfo; sinfo; sinfo = sinfo->next) - if (! ((sinfo->last->flags ^ sec->flags) & (SEC_MERGE | SEC_STRINGS)) - && sinfo->last->entsize == sec->entsize - && ! strcmp (sinfo->last->name, sec->name) - && bfd_get_section_alignment (abfd, sinfo->last) == align) + if ((secinfo = sinfo->chain) + && ! ((secinfo->sec->flags ^ sec->flags) & (SEC_MERGE | SEC_STRINGS)) + && secinfo->sec->entsize == sec->entsize + && secinfo->sec->alignment_power == sec->alignment_power + && secinfo->sec->output_section == sec->output_section) break; if (sinfo == NULL) { /* Initialize the information we need to keep track of. */ - first = true; - sinfo = (struct sec_merge_info *) - bfd_alloc (abfd, sizeof (struct sec_merge_info)); + sinfo = bfd_alloc (abfd, sizeof (struct sec_merge_info)); if (sinfo == NULL) goto error_return; sinfo->next = (struct sec_merge_info *) *psinfo; - *psinfo = (PTR) sinfo; - sinfo->htab = - sec_merge_init ((sec->flags & SEC_STRINGS) ? align : 0, - sec->entsize, (sec->flags & SEC_STRINGS)); + sinfo->chain = NULL; + *psinfo = sinfo; + sinfo->htab = sec_merge_init (sec->entsize, (sec->flags & SEC_STRINGS)); if (sinfo->htab == NULL) goto error_return; } /* Read the section from abfd. */ - *psecinfo = bfd_alloc (abfd, sizeof (struct sec_merge_sec_info) - + sec->_raw_size - 1); + amt = sizeof (struct sec_merge_sec_info) - 1 + sec->size; + if (sec->flags & SEC_STRINGS) + /* Some versions of gcc may emit a string without a zero terminator. + See http://gcc.gnu.org/ml/gcc-patches/2006-06/msg01004.html + Allocate space for an extra zero. */ + amt += sec->entsize; + *psecinfo = bfd_alloc (abfd, amt); if (*psecinfo == NULL) goto error_return; - secinfo = (struct sec_merge_sec_info *)*psecinfo; + secinfo = (struct sec_merge_sec_info *) *psecinfo; + if (sinfo->chain) + { + secinfo->next = sinfo->chain->next; + sinfo->chain->next = secinfo; + } + else + secinfo->next = secinfo; + sinfo->chain = secinfo; + secinfo->sec = sec; + secinfo->psecinfo = psecinfo; secinfo->htab = sinfo->htab; - sinfo->htab->size = 0; - secinfo->first = NULL; + secinfo->first_str = NULL; - if (! bfd_get_section_contents (abfd, sec, secinfo->contents, 0, - sec->_raw_size)) + sec->rawsize = sec->size; + if (sec->flags & SEC_STRINGS) + memset (secinfo->contents + sec->size, 0, sec->entsize); + if (! bfd_get_section_contents (sec->owner, sec, secinfo->contents, + 0, sec->size)) goto error_return; - end = secinfo->contents + sec->_raw_size; - nul = false; - mask = ((bfd_vma)1 << sinfo->htab->alignment_power) - 1; + return TRUE; + + error_return: + *psecinfo = NULL; + return FALSE; +} + +/* Record one section into the hash table. */ +static bfd_boolean +record_section (struct sec_merge_info *sinfo, + struct sec_merge_sec_info *secinfo) +{ + asection *sec = secinfo->sec; + struct sec_merge_hash_entry *entry; + bfd_boolean nul; + unsigned char *p, *end; + bfd_vma mask, eltalign; + unsigned int align, i; + + align = sec->alignment_power; + end = secinfo->contents + sec->size; + nul = FALSE; + mask = ((bfd_vma) 1 << align) - 1; if (sec->flags & SEC_STRINGS) { - for (p = secinfo->contents; p < end;) + for (p = secinfo->contents; p < end; ) { - struct sec_merge_hash_entry *entry; - - entry = sec_merge_add (sinfo->htab, p); - if (entry->sec == NULL) - { - if (secinfo->first == NULL) - secinfo->first = entry; - entry->sec = sec; - } + eltalign = p - secinfo->contents; + eltalign = ((eltalign ^ (eltalign - 1)) + 1) >> 1; + if (!eltalign || eltalign > mask) + eltalign = mask + 1; + entry = sec_merge_add (sinfo->htab, (char *) p, (unsigned) eltalign, + secinfo); + if (! entry) + goto error_return; p += entry->len; if (sec->entsize == 1) { @@ -432,17 +476,14 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) { if (!nul && !((p - secinfo->contents) & mask)) { - nul = true; - entry = sec_merge_add (sinfo->htab, ""); - if (entry->sec == NULL) - { - if (secinfo->first == NULL) - secinfo->first = entry; - entry->sec = sec; - } + nul = TRUE; + entry = sec_merge_add (sinfo->htab, "", + (unsigned) mask + 1, secinfo); + if (! entry) + goto error_return; } p++; - } + } } else { @@ -455,14 +496,11 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) break; if (!nul && !((p - secinfo->contents) & mask)) { - nul = true; - entry = sec_merge_add (sinfo->htab, p); - if (entry->sec == NULL) - { - if (secinfo->first == NULL) - secinfo->first = entry; - entry->sec = sec; - } + nul = TRUE; + entry = sec_merge_add (sinfo->htab, (char *) p, + (unsigned) mask + 1, secinfo); + if (! entry) + goto error_return; } p += sec->entsize; } @@ -473,55 +511,295 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) { for (p = secinfo->contents; p < end; p += sec->entsize) { - struct sec_merge_hash_entry *entry; + entry = sec_merge_add (sinfo->htab, (char *) p, 1, secinfo); + if (! entry) + goto error_return; + } + } + + return TRUE; - entry = sec_merge_add (sinfo->htab, p); - if (entry->sec == NULL) +error_return: + for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) + *secinfo->psecinfo = NULL; + return FALSE; +} + +static int +strrevcmp (const void *a, const void *b) +{ + struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; + struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; + unsigned int lenA = A->len; + unsigned int lenB = B->len; + const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1; + const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1; + int l = lenA < lenB ? lenA : lenB; + + while (l) + { + if (*s != *t) + return (int) *s - (int) *t; + s--; + t--; + l--; + } + return lenA - lenB; +} + +/* Like strrevcmp, but for the case where all strings have the same + alignment > entsize. */ + +static int +strrevcmp_align (const void *a, const void *b) +{ + struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; + struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; + unsigned int lenA = A->len; + unsigned int lenB = B->len; + const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1; + const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1; + int l = lenA < lenB ? lenA : lenB; + int tail_align = (lenA & (A->alignment - 1)) - (lenB & (A->alignment - 1)); + + if (tail_align != 0) + return tail_align; + + while (l) + { + if (*s != *t) + return (int) *s - (int) *t; + s--; + t--; + l--; + } + return lenA - lenB; +} + +static inline int +is_suffix (const struct sec_merge_hash_entry *A, + const struct sec_merge_hash_entry *B) +{ + if (A->len <= B->len) + /* B cannot be a suffix of A unless A is equal to B, which is guaranteed + not to be equal by the hash table. */ + return 0; + + return memcmp (A->root.string + (A->len - B->len), + B->root.string, B->len) == 0; +} + +/* This is a helper function for _bfd_merge_sections. It attempts to + merge strings matching suffixes of longer strings. */ +static void +merge_strings (struct sec_merge_info *sinfo) +{ + struct sec_merge_hash_entry **array, **a, *e; + struct sec_merge_sec_info *secinfo; + bfd_size_type size, amt; + unsigned int alignment = 0; + + /* Now sort the strings */ + amt = sinfo->htab->size * sizeof (struct sec_merge_hash_entry *); + array = bfd_malloc (amt); + if (array == NULL) + goto alloc_failure; + + for (e = sinfo->htab->first, a = array; e; e = e->next) + if (e->alignment) + { + *a++ = e; + /* Adjust the length to not include the zero terminator. */ + e->len -= sinfo->htab->entsize; + if (alignment != e->alignment) + { + if (alignment == 0) + alignment = e->alignment; + else + alignment = (unsigned) -1; + } + } + + sinfo->htab->size = a - array; + if (sinfo->htab->size != 0) + { + qsort (array, (size_t) sinfo->htab->size, + sizeof (struct sec_merge_hash_entry *), + (alignment != (unsigned) -1 && alignment > sinfo->htab->entsize + ? strrevcmp_align : strrevcmp)); + + /* Loop over the sorted array and merge suffixes */ + e = *--a; + e->len += sinfo->htab->entsize; + while (--a >= array) + { + struct sec_merge_hash_entry *cmp = *a; + + cmp->len += sinfo->htab->entsize; + if (e->alignment >= cmp->alignment + && !((e->len - cmp->len) & (cmp->alignment - 1)) + && is_suffix (e, cmp)) { - if (secinfo->first == NULL) - secinfo->first = entry; - entry->sec = sec; + cmp->u.suffix = e; + cmp->alignment = 0; } + else + e = cmp; } } - sec->_cooked_size = sinfo->htab->size; - if (!sec->_cooked_size) - sec->flags |= SEC_EXCLUDE; - sinfo->last = sec; - return true; +alloc_failure: + if (array) + free (array); - error_return: - if (*psecinfo != NULL) - free (*psecinfo); - *psecinfo = NULL; - return false; + /* Now assign positions to the strings we want to keep. */ + size = 0; + secinfo = sinfo->htab->first->secinfo; + for (e = sinfo->htab->first; e; e = e->next) + { + if (e->secinfo != secinfo) + { + secinfo->sec->size = size; + secinfo = e->secinfo; + } + if (e->alignment) + { + if (e->secinfo->first_str == NULL) + { + e->secinfo->first_str = e; + size = 0; + } + size = (size + e->alignment - 1) & ~((bfd_vma) e->alignment - 1); + e->u.index = size; + size += e->len; + } + } + secinfo->sec->size = size; + if (secinfo->sec->alignment_power != 0) + { + bfd_size_type align = (bfd_size_type) 1 << secinfo->sec->alignment_power; + secinfo->sec->size = (secinfo->sec->size + align - 1) & -align; + } + + /* And now adjust the rest, removing them from the chain (but not hashtable) + at the same time. */ + for (a = &sinfo->htab->first, e = *a; e; e = e->next) + if (e->alignment) + a = &e->next; + else + { + *a = e->next; + if (e->len) + { + e->secinfo = e->u.suffix->secinfo; + e->alignment = e->u.suffix->alignment; + e->u.index = e->u.suffix->u.index + (e->u.suffix->len - e->len); + } + } +} + +/* This function is called once after all SEC_MERGE sections are registered + with _bfd_merge_section. */ + +bfd_boolean +_bfd_merge_sections (bfd *abfd, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + void *xsinfo, + void (*remove_hook) (bfd *, asection *)) +{ + struct sec_merge_info *sinfo; + + for (sinfo = (struct sec_merge_info *) xsinfo; sinfo; sinfo = sinfo->next) + { + struct sec_merge_sec_info * secinfo; + + if (! sinfo->chain) + continue; + + /* Move sinfo->chain to head of the chain, terminate it. */ + secinfo = sinfo->chain; + sinfo->chain = secinfo->next; + secinfo->next = NULL; + + /* Record the sections into the hash table. */ + for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) + if (secinfo->sec->flags & SEC_EXCLUDE) + { + *secinfo->psecinfo = NULL; + if (remove_hook) + (*remove_hook) (abfd, secinfo->sec); + } + else if (! record_section (sinfo, secinfo)) + break; + + if (secinfo) + continue; + + if (sinfo->htab->first == NULL) + continue; + + if (sinfo->htab->strings) + merge_strings (sinfo); + else + { + struct sec_merge_hash_entry *e; + bfd_size_type size = 0; + + /* Things are much simpler for non-strings. + Just assign them slots in the section. */ + secinfo = NULL; + for (e = sinfo->htab->first; e; e = e->next) + { + if (e->secinfo->first_str == NULL) + { + if (secinfo) + secinfo->sec->size = size; + e->secinfo->first_str = e; + size = 0; + } + size = (size + e->alignment - 1) + & ~((bfd_vma) e->alignment - 1); + e->u.index = size; + size += e->len; + secinfo = e->secinfo; + } + secinfo->sec->size = size; + } + + /* Finally remove all input sections which have not made it into + the hash table at all. */ + for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) + if (secinfo->first_str == NULL) + secinfo->sec->flags |= SEC_EXCLUDE | SEC_KEEP; + } + + return TRUE; } /* Write out the merged section. */ -boolean -_bfd_write_merged_section (output_bfd, sec, psecinfo) - bfd *output_bfd; - asection *sec; - PTR psecinfo; +bfd_boolean +_bfd_write_merged_section (bfd *output_bfd, asection *sec, void *psecinfo) { struct sec_merge_sec_info *secinfo; + file_ptr pos; secinfo = (struct sec_merge_sec_info *) psecinfo; - if (!secinfo->first) - return true; + if (!secinfo) + return FALSE; + + if (secinfo->first_str == NULL) + return TRUE; - if (bfd_seek (output_bfd, - (sec->output_section->filepos + sec->output_offset), - SEEK_SET) != 0) - return false; + pos = sec->output_section->filepos + sec->output_offset; + if (bfd_seek (output_bfd, pos, SEEK_SET) != 0) + return FALSE; - if (! sec_merge_emit (output_bfd, secinfo->htab, secinfo->first)) - return false; + if (! sec_merge_emit (output_bfd, secinfo->first_str)) + return FALSE; - return true; + return TRUE; } /* Adjust an address in the SEC_MERGE section. Given OFFSET within @@ -529,11 +807,8 @@ _bfd_write_merged_section (output_bfd, sec, psecinfo) section and writes the new section back into *PSEC. */ bfd_vma -_bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend) - bfd *output_bfd ATTRIBUTE_UNUSED; - asection **psec; - PTR psecinfo; - bfd_vma offset, addend; +_bfd_merged_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, asection **psec, + void *psecinfo, bfd_vma offset) { struct sec_merge_sec_info *secinfo; struct sec_merge_hash_entry *entry; @@ -542,28 +817,32 @@ _bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend) secinfo = (struct sec_merge_sec_info *) psecinfo; - if (offset + addend >= sec->_raw_size) + if (!secinfo) + return offset; + + if (offset >= sec->rawsize) { - if (offset + addend > sec->_raw_size) - (*_bfd_error_handler) (_("%s: access beyond end of merged section (%ld + %ld)"), - bfd_get_filename (sec->owner), (long)offset, - (long) addend); - return (secinfo->first ? sec->_cooked_size : 0); + if (offset > sec->rawsize) + { + (*_bfd_error_handler) + (_("%s: access beyond end of merged section (%ld)"), + bfd_get_filename (sec->owner), (long) offset); + } + return secinfo->first_str ? sec->size : 0; } if (secinfo->htab->strings) { if (sec->entsize == 1) { - p = secinfo->contents + offset + addend - 1; - while (*p && p >= secinfo->contents) + p = secinfo->contents + offset - 1; + while (p >= secinfo->contents && *p) --p; ++p; } else { - p = secinfo->contents - + ((offset + addend) / sec->entsize) * sec->entsize; + p = secinfo->contents + (offset / sec->entsize) * sec->entsize; p -= sec->entsize; while (p >= secinfo->contents) { @@ -581,10 +860,9 @@ _bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend) } else { - p = secinfo->contents - + ((offset + addend) / sec->entsize) * sec->entsize; + p = secinfo->contents + (offset / sec->entsize) * sec->entsize; } - entry = sec_merge_hash_lookup (secinfo->htab, p, false); + entry = sec_merge_hash_lookup (secinfo->htab, (char *) p, 0, FALSE); if (!entry) { if (! secinfo->htab->strings) @@ -596,11 +874,10 @@ _bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend) if (! secinfo->htab->first) abort (); entry = secinfo->htab->first; - p = secinfo->contents - + ((offset + addend) / sec->entsize + 1) * sec->entsize - - entry->len; + p = (secinfo->contents + (offset / sec->entsize + 1) * sec->entsize + - entry->len); } - *psec = entry->sec; - return entry->index + (secinfo->contents + offset - p); + *psec = entry->secinfo->sec; + return entry->u.index + (secinfo->contents + offset - p); }