#include <ctype.h>
#include "as.h"
+#include "subsegs.h"
/* careful, this file includes data *declarations* */
#include "opcode/sparc.h"
struct sparc_it the_insn, set_insn;
+static INLINE int
+in_signed_range (val, max)
+ bfd_signed_vma val, max;
+{
+ if (max <= 0)
+ abort ();
+ if (val > max)
+ return 0;
+ if (val < ~max)
+ return 0;
+ return 1;
+}
+
#if 0
static void print_insn PARAMS ((struct sparc_it *insn));
#endif
goto immediate;
case 'i': /* 13 bit immediate */
- /* What's the difference between base13 and 13? */
- the_insn.reloc = BFD_RELOC_SPARC_BASE13;
+ the_insn.reloc = BFD_RELOC_SPARC13;
immediate_max = 0x0FFF;
/*FALLTHROUGH */
{
/* start-sanitize-v9 */
#ifndef NO_V9
+ /* Handle %uhi/%ulo by moving the upper word to the lower
+ one and pretending it's %hi/%lo. We also need to watch
+ for %hi/%lo: the top word needs to be zeroed otherwise
+ fixup_segment will complain the value is too big. */
switch (the_insn.reloc)
{
case BFD_RELOC_SPARC_HH22:
break;
default:
break;
+ case BFD_RELOC_HI22:
+ case BFD_RELOC_LO10:
+ the_insn.exp.X_add_number &= 0xffffffff;
+ break;
}
#endif
/* end-sanitize-v9 */
+ /* For pc-relative call instructions, we reject
+ constants to get better code. */
+ if (the_insn.pcrel
+ && the_insn.reloc == BFD_RELOC_32_PCREL_S2
+ && in_signed_range (the_insn.exp.X_add_number, 0x3fff)
+ )
+ {
+ error_message = ": PC-relative operand can't be a constant";
+ goto error;
+ }
/* Check for invalid constant values. Don't warn if
constant was inside %hi or %lo, since these
truncate the constant to fit. */
if (immediate_max != 0
&& the_insn.reloc != BFD_RELOC_LO10
&& the_insn.reloc != BFD_RELOC_HI22
- && (the_insn.exp.X_add_number > immediate_max
- || the_insn.exp.X_add_number < ~immediate_max))
- as_bad ("constant value must be between %ld and %ld",
- ~immediate_max, immediate_max);
+ && !in_signed_range (the_insn.exp.X_add_number,
+ immediate_max)
+ )
+ {
+ if (the_insn.pcrel)
+ /* Who knows? After relocation, we may be within
+ range. Let the linker figure it out. */
+ {
+ the_insn.exp.X_op = O_symbol;
+ the_insn.exp.X_add_symbol = section_symbol (absolute_section);
+ }
+ else
+ /* Immediate value is non-pcrel, and out of
+ range. */
+ as_bad ("constant value %ld out of range (%ld .. %ld)",
+ the_insn.exp.X_add_number,
+ ~immediate_max, immediate_max);
+ }
}
/* Reset to prevent extraneous range check. */
buf[3] = val & 0xff;
break;
- case BFD_RELOC_SPARC13:
- if (val & ~0x00001fff)
- {
- as_bad ("relocation overflow");
- } /* on overflow */
- buf[2] |= (val >> 8) & 0x1f;
- buf[3] = val & 0xff;
- break;
-
/* start-sanitize-v9 */
#ifndef NO_V9
case BFD_RELOC_SPARC_HM10:
else
buf[3] = 0;
break;
- case BFD_RELOC_SPARC_BASE13:
- if (((val > 0) && (val & ~(offsetT)0x00001fff))
- || ((val < 0) && (~(val - 1) & ~(offsetT)0x00001fff)))
- {
- as_bad ("relocation overflow");
- }
+
+ case BFD_RELOC_SPARC13:
+ if (! in_signed_range (val, 0x1fff))
+ as_bad ("relocation overflow");
+
buf[2] |= (val >> 8) & 0x1f;
buf[3] = val;
break;
break;
}
+ /* Are we finished with this relocation now? */
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ fixP->fx_done = 1;
+
return 1;
}
case BFD_RELOC_HI22:
case BFD_RELOC_LO10:
case BFD_RELOC_32_PCREL_S2:
+ case BFD_RELOC_SPARC13:
case BFD_RELOC_SPARC_BASE13:
case BFD_RELOC_SPARC_WDISP22:
/* start-sanitize-v9 */
/* @@ Why fx_addnumber sometimes and fx_offset other times? */
if (reloc->howto->pc_relative == 0)
reloc->addend = fixp->fx_addnumber;
+#if defined (OBJ_ELF) || defined (OBJ_COFF)
+ else if ((fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ reloc->addend = (section->vma
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+#endif
else
reloc->addend = fixp->fx_offset - reloc->address;