diff options
Diffstat (limited to 'bfd/merge.c')
-rw-r--r-- | bfd/merge.c | 112 |
1 files changed, 65 insertions, 47 deletions
diff --git a/bfd/merge.c b/bfd/merge.c index ace069e..6619dcd 100644 --- a/bfd/merge.c +++ b/bfd/merge.c @@ -34,6 +34,9 @@ struct sec_merge_hash_entry struct bfd_hash_entry root; /* Length of this entry. */ unsigned int len; + /* Start of this string needs to be aligned to + alignment octets (not 1 << align). */ + unsigned int alignment; /* Index within the merged section. */ bfd_size_type index; /* Which section is it in. */ @@ -55,9 +58,6 @@ 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; }; @@ -85,13 +85,13 @@ struct sec_merge_sec_info 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)); + PARAMS ((struct sec_merge_hash *, const char *, unsigned int, boolean)); static struct sec_merge_hash *sec_merge_init - PARAMS ((unsigned int, unsigned int, boolean)); + PARAMS ((unsigned int, boolean)); static struct sec_merge_hash_entry *sec_merge_add - PARAMS ((struct sec_merge_hash *, const char *)); + PARAMS ((struct sec_merge_hash *, const char *, unsigned int)); static boolean sec_merge_emit - PARAMS ((bfd *, struct sec_merge_hash *, struct sec_merge_hash_entry *)); + PARAMS ((bfd *, struct sec_merge_hash_entry *)); /* Routine to create an entry in a section merge hashtab. */ @@ -119,6 +119,7 @@ sec_merge_hash_newfunc (entry, table, string) { /* Initialize the local fields. */ ret->index = (bfd_size_type) -1; + ret->alignment = 0; ret->sec = NULL; ret->next = NULL; } @@ -129,9 +130,10 @@ sec_merge_hash_newfunc (entry, table, string) /* Look up an entry in a section merge hash table. */ static struct sec_merge_hash_entry * -sec_merge_hash_lookup (table, string, create) +sec_merge_hash_lookup (table, string, alignment, create) struct sec_merge_hash *table; const char *string; + unsigned int alignment; boolean create; { register const unsigned char *s; @@ -193,12 +195,20 @@ 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 = (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. + FIXME: The old copy could be removed and the space allocated + for it filled by some new string (similarly with padding). */ + if (hashp->alignment < alignment) + break; + return hashp; + } } if (! create) @@ -212,6 +222,7 @@ sec_merge_hash_lookup (table, string, create) hashp->root.string = string; hashp->root.hash = hash; hashp->len = len; + hashp->alignment = alignment; hashp->root.next = table->table.table[index]; table->table.table[index] = (struct bfd_hash_entry *) hashp; @@ -221,8 +232,8 @@ sec_merge_hash_lookup (table, string, create) /* Create a new hash table. */ static struct sec_merge_hash * -sec_merge_init (alignment_power, entsize, strings) - unsigned int alignment_power, entsize; +sec_merge_init (entsize, strings) + unsigned int entsize; boolean strings; { struct sec_merge_hash *table; @@ -241,7 +252,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 +262,22 @@ sec_merge_init (alignment_power, entsize, strings) already present. */ static struct sec_merge_hash_entry * -sec_merge_add (tab, str) +sec_merge_add (tab, str, alignment) struct sec_merge_hash *tab; const char *str; + unsigned int alignment; { 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) { + tab->size = (tab->size + alignment - 1) & ~((bfd_vma) alignment - 1); entry->index = tab->size; tab->size += entry->len; - tab->size = align_power (tab->size, tab->alignment_power); if (tab->first == NULL) tab->first = entry; else @@ -278,39 +289,42 @@ sec_merge_add (tab, str) } static boolean -sec_merge_emit (abfd, tab, entry) +sec_merge_emit (abfd, entry) register bfd *abfd; - struct sec_merge_hash *tab; struct sec_merge_hash_entry *entry; { asection *sec = entry->sec; char *pad = ""; + bfd_size_type off = 0; + int alignment_power = bfd_get_section_alignment (abfd, sec->output_section); - if (tab->alignment_power) - pad = bfd_zmalloc (1 << tab->alignment_power); + if (alignment_power) + pad = bfd_zmalloc (1 << alignment_power); for (; entry != NULL && entry->sec == sec; entry = entry->next) { register const char *str; register size_t len; + len = off & (entry->alignment - 1); + if (len) + { + len = entry->alignment - len; + if (bfd_write ((PTR) pad, 1, len, abfd) != len) + break; + off += len; + } + str = entry->root.string; len = entry->len; if (bfd_write ((PTR) str, 1, len, abfd) != len) break; - 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) + if (alignment_power) free (pad); return entry == NULL || entry->sec != sec; @@ -330,7 +344,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) struct sec_merge_info *sinfo; struct sec_merge_sec_info *secinfo; unsigned char *p, *end; - bfd_vma mask; + bfd_vma mask, eltalign; unsigned int align; unsigned int i; @@ -354,16 +368,18 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) } 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))))) + if ((sec->entsize < (unsigned int)(1 << align) + && ((sec->entsize & (sec->entsize - 1)) + || !(sec->flags & SEC_STRINGS))) + || (sec->entsize > (unsigned int)(1 << align) + && (sec->entsize & ((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. */ + 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; } @@ -372,8 +388,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) 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) + && ! strcmp (sinfo->last->name, sec->name)) break; if (sinfo == NULL) @@ -387,8 +402,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) 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)); + sec_merge_init (sec->entsize, (sec->flags & SEC_STRINGS)); if (sinfo->htab == NULL) goto error_return; } @@ -411,14 +425,18 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) end = secinfo->contents + sec->_raw_size; nul = false; - mask = ((bfd_vma)1 << sinfo->htab->alignment_power) - 1; + mask = ((bfd_vma)1 << align) - 1; if (sec->flags & SEC_STRINGS) { for (p = secinfo->contents; p < end;) { struct sec_merge_hash_entry *entry; - entry = sec_merge_add (sinfo->htab, p); + eltalign = p - secinfo->contents; + eltalign = ((eltalign ^ (eltalign - 1)) + 1) >> 1; + if (!eltalign || eltalign > mask) + eltalign = mask + 1; + entry = sec_merge_add (sinfo->htab, p, eltalign); if (entry->sec == NULL) { if (secinfo->first == NULL) @@ -433,7 +451,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) if (!nul && !((p - secinfo->contents) & mask)) { nul = true; - entry = sec_merge_add (sinfo->htab, ""); + entry = sec_merge_add (sinfo->htab, "", mask + 1); if (entry->sec == NULL) { if (secinfo->first == NULL) @@ -456,7 +474,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) if (!nul && !((p - secinfo->contents) & mask)) { nul = true; - entry = sec_merge_add (sinfo->htab, p); + entry = sec_merge_add (sinfo->htab, p, mask + 1); if (entry->sec == NULL) { if (secinfo->first == NULL) @@ -475,7 +493,7 @@ _bfd_merge_section (abfd, psinfo, sec, psecinfo) { struct sec_merge_hash_entry *entry; - entry = sec_merge_add (sinfo->htab, p); + entry = sec_merge_add (sinfo->htab, p, 1); if (entry->sec == NULL) { if (secinfo->first == NULL) @@ -518,7 +536,7 @@ _bfd_write_merged_section (output_bfd, sec, psecinfo) SEEK_SET) != 0) return false; - if (! sec_merge_emit (output_bfd, secinfo->htab, secinfo->first)) + if (! sec_merge_emit (output_bfd, secinfo->first)) return false; return true; @@ -584,7 +602,7 @@ _bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend) p = secinfo->contents + ((offset + addend) / sec->entsize) * sec->entsize; } - entry = sec_merge_hash_lookup (secinfo->htab, p, false); + entry = sec_merge_hash_lookup (secinfo->htab, p, 0, false); if (!entry) { if (! secinfo->htab->strings) |