diff options
Diffstat (limited to 'gnu/usr.bin/binutils/bfd/reloc16.c')
-rw-r--r-- | gnu/usr.bin/binutils/bfd/reloc16.c | 61 |
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; |