summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/binutils/bfd/reloc16.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/binutils/bfd/reloc16.c')
-rw-r--r--gnu/usr.bin/binutils/bfd/reloc16.c61
1 files changed, 52 insertions, 9 deletions
diff --git a/gnu/usr.bin/binutils/bfd/reloc16.c b/gnu/usr.bin/binutils/bfd/reloc16.c
index ec15c6c6873..ddf561b591b 100644
--- a/gnu/usr.bin/binutils/bfd/reloc16.c
+++ b/gnu/usr.bin/binutils/bfd/reloc16.c
@@ -148,13 +148,14 @@ bfd_coff_reloc16_relax_section (abfd, i, link_info, again)
/* Get enough memory to hold the stuff */
bfd *input_bfd = i->owner;
asection *input_section = i;
- int shrink = 0 ;
+ unsigned *shrinks;
+ int shrink = 0;
long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
arelent **reloc_vector = NULL;
long reloc_count;
- /* We only run this relaxation once. It might work to run it more
- often, but it hasn't been tested. */
+ /* We only do global relaxation once. It is not safe to do it multiple
+ times (see discussion of the "shrinks" array below). */
*again = false;
if (reloc_size < 0)
@@ -174,14 +175,56 @@ bfd_coff_reloc16_relax_section (abfd, i, link_info, again)
return false;
}
+ /* The reloc16.c and related relaxing code is very simple, the price
+ for that simplicity is we can only call this function once for
+ each section.
+
+ So, to get the best results within that limitation, we do multiple
+ relaxing passes over each section here. That involves keeping track
+ of the "shrink" at each reloc in the section. This allows us to
+ accurately determine the relative location of two relocs within
+ this section.
+
+ In theory, if we kept the "shrinks" array for each section for the
+ entire link, we could use the generic relaxing code in the linker
+ and get better results, particularly for jsr->bsr and 24->16 bit
+ memory reference relaxations. */
+
if (reloc_count > 0)
{
- arelent **parent;
- for (parent = reloc_vector; *parent; parent++)
- {
- shrink = bfd_coff_reloc16_estimate (abfd, input_section,
- *parent, shrink, link_info);
- }
+ int another_pass = 0;
+
+ /* Allocate and initialize the shrinks array for this section. */
+ shrinks = (unsigned *)bfd_malloc (reloc_count * sizeof (unsigned));
+ memset (shrinks, 0, reloc_count * sizeof (unsigned));
+
+ /* Loop until nothing changes in this section. */
+ do {
+ arelent **parent;
+ unsigned int i, j;
+
+ another_pass = 0;
+
+ for (i = 0, parent = reloc_vector; *parent; parent++, i++)
+ {
+ /* Let the target/machine dependent code examine each reloc
+ in this section and attempt to shrink it. */
+ shrink = bfd_coff_reloc16_estimate (abfd, input_section, *parent,
+ shrinks[i], link_info);
+
+ /* If it shrunk, note it in the shrinks array and set up for
+ another pass. */
+ if (shrink != shrinks[i])
+ {
+ another_pass = 1;
+ for (j = i + 1; j < reloc_count; j++)
+ shrinks[j] += shrink - shrinks[i];
+ }
+ }
+
+ } while (another_pass);
+
+ free((char *)shrinks);
}
input_section->_cooked_size -= shrink;