aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-mips.c152
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
}
}
}