summaryrefslogtreecommitdiff
path: root/gas/config/tc-mn10300.c
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2007-10-19 17:31:31 +0000
committerNick Clifton <nickc@redhat.com>2007-10-19 17:31:31 +0000
commitbfff1642494227904c6c9a6c285cbaa6cf615bbb (patch)
tree7bf1ece58861b5e9c520e056102994251c0a7504 /gas/config/tc-mn10300.c
parent97030eea009ba78139fe20eae4585984435ac178 (diff)
Add MN10300 linker relaxation support for symbol differences
Diffstat (limited to 'gas/config/tc-mn10300.c')
-rw-r--r--gas/config/tc-mn10300.c144
1 files changed, 102 insertions, 42 deletions
diff --git a/gas/config/tc-mn10300.c b/gas/config/tc-mn10300.c
index 671c0089f7..5ec58bb805 100644
--- a/gas/config/tc-mn10300.c
+++ b/gas/config/tc-mn10300.c
@@ -2141,15 +2141,21 @@ keep_going:
dwarf2_emit_insn (size);
}
+
+ /* Label this frag as one that contains instructions. */
+ frag_now->tc_frag_data = TRUE;
}
/* If while processing a fixup, a reloc really needs to be created
then it is done here. */
-arelent *
+arelent **
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
{
+ static arelent * no_relocs = NULL;
+ static arelent * relocs[MAX_RELOC_EXPANSION + 1];
arelent *reloc;
+
reloc = xmalloc (sizeof (arelent));
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
@@ -2158,9 +2164,13 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
as_bad_where (fixp->fx_file, fixp->fx_line,
_("reloc %d not supported by object file format"),
(int) fixp->fx_r_type);
- return NULL;
+ free (reloc);
+ return & no_relocs;
}
+
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[0] = reloc;
+ relocs[1] = NULL;
if (fixp->fx_subsy
&& S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
@@ -2173,44 +2183,33 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
{
reloc->sym_ptr_ptr = NULL;
- /* If we got a difference between two symbols, and the
- subtracted symbol is in the current section, use a
- PC-relative relocation. If both symbols are in the same
- section, the difference would have already been simplified
- to a constant. */
+ /* If we have a difference between two (non-absolute) symbols we must
+ generate two relocs (one for each symbol) and allow the linker to
+ resolve them - relaxation may change the distances between symbols,
+ even local symbols defined in the same segment. */
if (S_GET_SEGMENT (fixp->fx_subsy) == seg)
{
- reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
- *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
- reloc->addend = (reloc->address - S_GET_VALUE (fixp->fx_subsy)
- + fixp->fx_offset);
+ arelent * reloc2 = xmalloc (sizeof * reloc);
- switch (fixp->fx_r_type)
- {
- case BFD_RELOC_8:
- reloc->howto = bfd_reloc_type_lookup (stdoutput,
- BFD_RELOC_8_PCREL);
- return reloc;
+ relocs[0] = reloc2;
+ relocs[1] = reloc;
- case BFD_RELOC_16:
- reloc->howto = bfd_reloc_type_lookup (stdoutput,
- BFD_RELOC_16_PCREL);
- return reloc;
+ reloc2->address = reloc->address;
+ reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_MN10300_SYM_DIFF);
+ reloc2->addend = - S_GET_VALUE (fixp->fx_subsy);
+ reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
- case BFD_RELOC_24:
- reloc->howto = bfd_reloc_type_lookup (stdoutput,
- BFD_RELOC_24_PCREL);
- return reloc;
+ reloc->addend = fixp->fx_offset;
+ if (S_GET_SEGMENT (fixp->fx_addsy) == absolute_section)
+ reloc->addend += S_GET_VALUE (fixp->fx_addsy);
- case BFD_RELOC_32:
- reloc->howto = bfd_reloc_type_lookup (stdoutput,
- BFD_RELOC_32_PCREL);
- return reloc;
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
- default:
- /* Try to compute the absolute value below. */
- break;
- }
+ fixp->fx_pcrel = 0;
+ fixp->fx_done = 1;
+ return relocs;
}
if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
@@ -2247,14 +2246,14 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
default:
reloc->sym_ptr_ptr
= (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
- return reloc;
+ return relocs;
}
}
if (reloc->sym_ptr_ptr)
free (reloc->sym_ptr_ptr);
free (reloc);
- return NULL;
+ return & no_relocs;
}
else
{
@@ -2262,7 +2261,7 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->addend = fixp->fx_offset;
}
- return reloc;
+ return relocs;
}
int
@@ -2377,11 +2376,14 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg)
bfd_boolean
mn10300_fix_adjustable (struct fix *fixp)
{
- if (TC_FORCE_RELOCATION_LOCAL (fixp))
- return FALSE;
-
- if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ if (fixp->fx_pcrel)
+ {
+ if (TC_FORCE_RELOCATION_LOCAL (fixp))
+ return FALSE;
+ }
+ /* Non-relative relocs can (and must) be adjusted if they do
+ not meet the criteria below, or the generic criteria. */
+ else if (TC_FORCE_RELOCATION (fixp))
return FALSE;
/* Do not adjust relocations involving symbols in code sections,
@@ -2395,8 +2397,9 @@ mn10300_fix_adjustable (struct fix *fixp)
symbols, because they too break relaxation. We do want to adjust
other mergable symbols, like .rodata, because code relaxations
need section-relative symbols to properly relax them. */
- if (! (S_GET_SEGMENT(fixp->fx_addsy)->flags & SEC_MERGE))
+ if (! (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE))
return FALSE;
+
if (strncmp (S_GET_SEGMENT (fixp->fx_addsy)->name, ".debug", 6) == 0)
return FALSE;
@@ -2502,3 +2505,60 @@ const pseudo_typeS md_pseudo_table[] =
{ "mn10300", set_arch_mach, MN103 },
{NULL, 0, 0}
};
+
+/* Returns FALSE if there is some mn10300 specific reason why the
+ subtraction of two same-section symbols cannot be computed by
+ the assembler. */
+
+bfd_boolean
+mn10300_allow_local_subtract (expressionS * left, expressionS * right, segT section)
+{
+ bfd_boolean result;
+ fragS * left_frag;
+ fragS * right_frag;
+ fragS * frag;
+
+ /* If we are not performing linker relaxation then we have nothing
+ to worry about. */
+ if (linkrelax == 0)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ /* Otherwise we have to scan the fragments between the two symbols.
+ If any instructions are found then we have to assume that linker
+ relaxation may change their size and so we must delay resolving
+ the subtraction until the final link. */
+ left_frag = symbol_get_frag (left->X_add_symbol);
+ right_frag = symbol_get_frag (right->X_add_symbol);
+
+ if (left_frag == right_frag)
+ return ! left_frag->tc_frag_data;
+
+ result = TRUE;
+ for (frag = left_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == right_frag)
+ break;
+ }
+
+ if (frag == NULL)
+ for (frag = right_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == left_frag)
+ break;
+ }
+
+ if (frag == NULL)
+ /* The two symbols are on disjoint fragment chains
+ - we cannot possibly compute their difference. */
+ return FALSE;
+
+ return result;
+}