diff options
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/tc-mips.c | 152 |
1 files changed, 96 insertions, 56 deletions
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index b7a6081..c554ba1 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -279,6 +279,9 @@ static int mips_32bitmode = 0; #define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI) +/* True if relocations are stored in-place. */ +#define HAVE_IN_PLACE_ADDENDS (!HAVE_NEWABI) + /* We can only have 64bit addresses if the object file format supports it. */ #define HAVE_32BIT_ADDRESSES \ (HAVE_32BIT_GPRS \ @@ -1398,8 +1401,9 @@ md_assemble (char *str) static inline bfd_boolean reloc_needs_lo_p (bfd_reloc_code_real_type reloc) { - return (reloc == BFD_RELOC_HI16_S - || reloc == BFD_RELOC_MIPS_GOT16); + return (HAVE_IN_PLACE_ADDENDS + && (reloc == BFD_RELOC_HI16_S + || reloc == BFD_RELOC_MIPS_GOT16)); } /* Return true if the given fixup is followed by a matching R_MIPS_LO16 @@ -10670,10 +10674,53 @@ mips_frob_file_before_adjust (void) #endif } -/* Sort any unmatched HI16_S relocs so that they immediately precede - the corresponding LO reloc. This is called before md_apply_fix3 and - tc_gen_reloc. Unmatched HI16_S relocs can only be generated by - explicit use of the %hi modifier. */ +/* Sort any unmatched HI16 and GOT16 relocs so that they immediately precede + the corresponding LO16 reloc. This is called before md_apply_fix3 and + tc_gen_reloc. Unmatched relocs can only be generated by use of explicit + relocation operators. + + For our purposes, a %lo() expression matches a %got() or %hi() + expression if: + + (a) it refers to the same symbol; and + (b) the offset applied in the %lo() expression is no lower than + the offset applied in the %got() or %hi(). + + (b) allows us to cope with code like: + + lui $4,%hi(foo) + lh $4,%lo(foo+2)($4) + + ...which is legal on RELA targets, and has a well-defined behaviour + if the user knows that adding 2 to "foo" will not induce a carry to + the high 16 bits. + + When several %lo()s match a particular %got() or %hi(), we use the + following rules to distinguish them: + + (1) %lo()s with smaller offsets are a better match than %lo()s with + higher offsets. + + (2) %lo()s with no matching %got() or %hi() are better than those + that already have a matching %got() or %hi(). + + (3) later %lo()s are better than earlier %lo()s. + + These rules are applied in order. + + (1) means, among other things, that %lo()s with identical offsets are + chosen if they exist. + + (2) means that we won't associate several high-part relocations with + the same low-part relocation unless there's no alternative. Having + several high parts for the same low part is a GNU extension; this rule + allows careful users to avoid it. + + (3) is purely cosmetic. mips_hi_fixup_list is is in reverse order, + with the last high-part relocation being at the front of the list. + It therefore makes sense to choose the last matching low-part + relocation, all other things being equal. It's also easier + to code that way. */ void mips_frob_file (void) @@ -10683,7 +10730,8 @@ mips_frob_file (void) for (l = mips_hi_fixup_list; l != NULL; l = l->next) { segment_info_type *seginfo; - int pass; + bfd_boolean matched_lo_p; + fixS **hi_pos, **lo_pos, **pos; assert (reloc_needs_lo_p (l->fixp->fx_r_type)); @@ -10697,59 +10745,51 @@ mips_frob_file (void) if (fixup_has_matching_lo_p (l->fixp)) continue; - /* Look through the fixups for this segment for a matching %lo. - When we find one, move the %hi just in front of it. We do - this in two passes. In the first pass, we try to find a - unique %lo. In the second pass, we permit multiple %hi - relocs for a single %lo (this is a GNU extension). */ seginfo = seg_info (l->seg); - for (pass = 0; pass < 2; pass++) - { - fixS *f, *prev; - prev = NULL; - for (f = seginfo->fix_root; f != NULL; f = f->fx_next) + /* Set HI_POS to the position of this relocation in the chain. + Set LO_POS to the position of the chosen low-part relocation. + MATCHED_LO_P is true on entry to the loop if *POS is a low-part + relocation that matches an immediately-preceding high-part + relocation. */ + hi_pos = NULL; + lo_pos = NULL; + matched_lo_p = FALSE; + for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next) + { + if (*pos == l->fixp) + hi_pos = pos; + + if ((*pos)->fx_r_type == BFD_RELOC_LO16 + && (*pos)->fx_addsy == l->fixp->fx_addsy + && (*pos)->fx_offset >= l->fixp->fx_offset + && (lo_pos == NULL + || (*pos)->fx_offset < (*lo_pos)->fx_offset + || (!matched_lo_p + && (*pos)->fx_offset == (*lo_pos)->fx_offset))) + lo_pos = pos; + + matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type) + && fixup_has_matching_lo_p (*pos)); + } + + /* If we found a match, remove the high-part relocation from its + current position and insert it before the low-part relocation. + Make the offsets match so that fixup_has_matching_lo_p() + will return true. + + We don't warn about unmatched high-part relocations since some + versions of gcc have been known to emit dead "lui ...%hi(...)" + instructions. */ + if (lo_pos != NULL) + { + l->fixp->fx_offset = (*lo_pos)->fx_offset; + if (l->fixp->fx_next != *lo_pos) { - /* Check whether this is a %lo fixup which matches l->fixp. */ - if (f->fx_r_type == BFD_RELOC_LO16 - && f->fx_addsy == l->fixp->fx_addsy - && f->fx_offset == l->fixp->fx_offset - && (pass == 1 - || prev == NULL - || !reloc_needs_lo_p (prev->fx_r_type) - || !fixup_has_matching_lo_p (prev))) - { - fixS **pf; - - /* Move l->fixp before f. */ - for (pf = &seginfo->fix_root; - *pf != l->fixp; - pf = &(*pf)->fx_next) - assert (*pf != NULL); - - *pf = l->fixp->fx_next; - - l->fixp->fx_next = f; - if (prev == NULL) - seginfo->fix_root = l->fixp; - else - prev->fx_next = l->fixp; - - break; - } - - prev = f; + *hi_pos = l->fixp->fx_next; + l->fixp->fx_next = *lo_pos; + *lo_pos = l->fixp; } - - if (f != NULL) - break; - -#if 0 /* GCC code motion plus incomplete dead code elimination - can leave a %hi without a %lo. */ - if (pass == 1) - as_warn_where (l->fixp->fx_file, l->fixp->fx_line, - _("Unmatched %%hi reloc")); -#endif } } } |