aboutsummaryrefslogtreecommitdiff
path: root/bfd/merge.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/merge.c')
-rw-r--r--bfd/merge.c112
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)