/* BFD back-end for Hitachi Super-H COFF binaries.
- Copyright 1993, 1994, 1995 Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
Contributed by Cygnus Support.
Written by Steve Chamberlain, <sac@cygnus.com>.
+ Relaxing code written by Ian Lance Taylor, <ian@cygnus.com>.
This file is part of BFD, the Binary File Descriptor library.
PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
static boolean sh_relax_delete_bytes
PARAMS ((bfd *, asection *, bfd_vma, int));
+static const struct sh_opcode *sh_insn_info PARAMS ((unsigned int));
+static boolean sh_align_loads
+ PARAMS ((bfd *, asection *, struct internal_reloc *, bfd_byte *, boolean *));
+static boolean sh_swap_insns
+ PARAMS ((bfd *, asection *, struct internal_reloc *, bfd_byte *, bfd_vma));
static boolean sh_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
struct internal_reloc *, struct internal_syment *, asection **));
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
+
+ HOWTO (R_SH_CODE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ sh_reloc, /* special_function */
+ "r_code", /* name */
+ true, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
+
+ HOWTO (R_SH_DATA, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ sh_reloc, /* special_function */
+ "r_data", /* name */
+ true, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
+
+ HOWTO (R_SH_LABEL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ sh_reloc, /* special_function */
+ "r_label", /* name */
+ true, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
false) /* pcrel_offset */
};
sh_coff_get_relocated_section_contents
#include "coffcode.h"
-
+\f
/* This function handles relaxing on the SH.
Function calls on the SH look like this:
the R_SH_COUNT reloc will be the number of references. If the
linker is able to eliminate a register load, it can use the
R_SH_COUNT reloc to see whether it can also eliminate the function
- address. */
+ address.
+
+ SH relaxing also handles another, unrelated, matter. On the SH, if
+ a load or store instruction is not aligned on a four byte boundary,
+ the memory cycle interferes with the 32 bit instruction fetch,
+ causing a one cycle bubble in the pipeline. Therefore, we try to
+ align load and store instructions on four byte boundaries if we
+ can, by swapping them with one of the adjacent instructions. */
static boolean
sh_relax_section (abfd, sec, link_info, again)
{
struct internal_reloc *internal_relocs;
struct internal_reloc *free_relocs = NULL;
+ boolean have_code;
struct internal_reloc *irel, *irelend;
bfd_byte *contents = NULL;
bfd_byte *free_contents = NULL;
if (! link_info->keep_memory)
free_relocs = internal_relocs;
+ have_code = false;
+
irelend = internal_relocs + sec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
struct internal_syment sym;
bfd_signed_vma foff;
+ if (irel->r_type == R_SH_CODE)
+ have_code = true;
+
if (irel->r_type != R_SH_USES)
continue;
/* We've done all we can with that function call. */
}
+ /* Look for load and store instructions that we can align on four
+ byte boundaries. */
+ if (have_code)
+ {
+ boolean swapped;
+
+ /* Get the section contents. */
+ if (contents == NULL)
+ {
+ if (coff_section_data (abfd, sec) != NULL
+ && coff_section_data (abfd, sec)->contents != NULL)
+ contents = coff_section_data (abfd, sec)->contents;
+ else
+ {
+ contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
+ if (contents == NULL)
+ goto error_return;
+ free_contents = contents;
+
+ if (! bfd_get_section_contents (abfd, sec, contents,
+ (file_ptr) 0, sec->_raw_size))
+ goto error_return;
+ }
+ }
+
+ if (! sh_align_loads (abfd, sec, internal_relocs, contents, &swapped))
+ goto error_return;
+
+ if (swapped)
+ {
+ if (coff_section_data (abfd, sec) == NULL)
+ {
+ sec->used_by_bfd =
+ ((PTR) bfd_zalloc (abfd, sizeof (struct coff_section_tdata)));
+ if (sec->used_by_bfd == NULL)
+ goto error_return;
+ }
+
+ coff_section_data (abfd, sec)->relocs = internal_relocs;
+ coff_section_data (abfd, sec)->keep_relocs = true;
+ free_relocs = NULL;
+
+ coff_section_data (abfd, sec)->contents = contents;
+ coff_section_data (abfd, sec)->keep_contents = true;
+ free_contents = NULL;
+
+ obj_coff_keep_syms (abfd) = true;
+ }
+ }
+
if (free_relocs != NULL)
{
free (free_relocs);
if (irelalign == NULL)
sec->_cooked_size -= count;
else
- memset (contents + toaddr - count, 0, count);
+ {
+ int i;
+
+#define NOP_OPCODE (0x0009)
+
+ BFD_ASSERT ((count & 1) == 0);
+ for (i = 0; i < count; i += 2)
+ bfd_put_16 (abfd, NOP_OPCODE, contents + toaddr - count + i);
+ }
/* Adjust all the relocs. */
for (irel = coff_section_data (abfd, sec)->relocs; irel < irelend; irel++)
nraddr -= count;
/* See if this reloc was for the bytes we have deleted, in which
- case we no longer care about it. */
+ case we no longer care about it. Don't delete relocs which
+ represent addresses, though. */
if (irel->r_vaddr - sec->vma >= addr
&& irel->r_vaddr - sec->vma < addr + count
- && irel->r_type != R_SH_ALIGN)
+ && irel->r_type != R_SH_ALIGN
+ && irel->r_type != R_SH_CODE
+ && irel->r_type != R_SH_DATA)
irel->r_type = R_SH_UNUSED;
/* If this is a PC relative reloc, see if the range it covers
case R_SH_USES:
start = irel->r_vaddr - sec->vma;
- stop = (bfd_vma) ((bfd_signed_vma) start + (long) irel->r_offset);
+ stop = (bfd_vma) ((bfd_signed_vma) start
+ + (long) irel->r_offset
+ + 4);
break;
}
return true;
}
+\f
+/* This is yet another version of the SH opcode table, used to rapidly
+ get information about a particular instruction. */
+
+/* The opcode map is represented by an array of these structures. The
+ array is indexed by the high order four bits in the instruction. */
+
+struct sh_major_opcode
+{
+ /* A pointer to the instruction list. This is an array which
+ contains all the instructions with this major opcode. */
+ const struct sh_minor_opcode *minor_opcodes;
+ /* The number of elements in minor_opcodes. */
+ unsigned short count;
+};
+
+/* This structure holds information for a set of SH opcodes. The
+ instruction code is anded with the mask value, and the resulting
+ value is used to search the order opcode list. */
+
+struct sh_minor_opcode
+{
+ /* The sorted opcode list. */
+ const struct sh_opcode *opcodes;
+ /* The number of elements in opcodes. */
+ unsigned short count;
+ /* The mask value to use when searching the opcode list. */
+ unsigned short mask;
+};
+
+/* This structure holds information for an SH instruction. An array
+ of these structures is sorted in order by opcode. */
+
+struct sh_opcode
+{
+ /* The code for this instruction, after it has been anded with the
+ mask value in the sh_major_opcode structure. */
+ unsigned short opcode;
+ /* Flags for this instruction. */
+ unsigned short flags;
+};
+
+/* Flag which appear in the sh_opcode structure. */
+
+/* This instruction loads a value from memory. */
+#define LOAD (0x1)
+
+/* This instruction stores a value to memory. */
+#define STORE (0x2)
+
+/* This instruction is a branch. */
+#define BRANCH (0x4)
+
+/* This instruction has a delay slot. */
+#define DELAY (0x8)
+
+/* This instruction uses the value in the register in the field at
+ mask 0x0f00 of the instruction. */
+#define USES1 (0x10)
+
+/* This instruction uses the value in the register in the field at
+ mask 0x00f0 of the instruction. */
+#define USES2 (0x20)
+
+/* This instruction uses the value in register 0. */
+#define USESR0 (0x40)
+
+/* This instruction sets the value in the register in the field at
+ mask 0x0f00 of the instruction. */
+#define SETS1 (0x80)
+
+/* This instruction sets the value in the register in the field at
+ mask 0x00f0 of the instruction. */
+#define SETS2 (0x100)
+
+/* This instruction sets register 0. */
+#define SETSR0 (0x200)
+
+/* This instruction sets a special register. */
+#define SETSSP (0x400)
+
+/* This instruction uses a special register. */
+#define USESSP (0x800)
+
+/* This instruction uses the floating point register in the field at
+ mask 0x0f00 of the instruction. */
+#define USESF1 (0x1000)
+
+/* This instruction uses the floating point register in the field at
+ mask 0x00f0 of the instruction. */
+#define USESF2 (0x2000)
+
+/* This instruction uses floating point register 0. */
+#define USESF0 (0x4000)
+
+/* This instruction sets the floating point register in the field at
+ mask 0x0f00 of the instruction. */
+#define SETSF1 (0x8000)
+
+static boolean sh_insn_uses_reg
+ PARAMS ((unsigned int, const struct sh_opcode *, unsigned int));
+static boolean sh_insn_uses_freg
+ PARAMS ((unsigned int, const struct sh_opcode *, unsigned int));
+static boolean sh_insns_conflict
+ PARAMS ((unsigned int, const struct sh_opcode *, unsigned int,
+ const struct sh_opcode *));
+static boolean sh_load_use
+ PARAMS ((unsigned int, const struct sh_opcode *, unsigned int,
+ const struct sh_opcode *));
+
+/* The opcode maps. */
+
+#define MAP(a) a, sizeof a / sizeof a[0]
+
+static const struct sh_opcode sh_opcode00[] =
+{
+ { 0x0008, SETSSP }, /* clrt */
+ { 0x0009, 0 }, /* nop */
+ { 0x000b, BRANCH | DELAY | USESSP }, /* rts */
+ { 0x0018, SETSSP }, /* sett */
+ { 0x0019, SETSSP }, /* div0u */
+ { 0x001b, 0 }, /* sleep */
+ { 0x0028, SETSSP }, /* clrmac */
+ { 0x002b, BRANCH | DELAY | SETSSP }, /* rte */
+ { 0x0038, USESSP | SETSSP }, /* ldtlb */
+ { 0x0048, SETSSP }, /* clrs */
+ { 0x0058, SETSSP } /* sets */
+};
+
+static const struct sh_opcode sh_opcode01[] =
+{
+ { 0x0002, SETS1 | USESSP }, /* stc sr,rn */
+ { 0x0003, BRANCH | DELAY | USES1 | SETSSP }, /* bsrf rn */
+ { 0x000a, SETS1 | USESSP }, /* sts mach,rn */
+ { 0x0012, SETS1 | USESSP }, /* stc gbr,rn */
+ { 0x001a, SETS1 | USESSP }, /* sts macl,rn */
+ { 0x0022, SETS1 | USESSP }, /* stc vbr,rn */
+ { 0x0023, BRANCH | DELAY | USES1 }, /* braf rn */
+ { 0x0029, SETS1 | USESSP }, /* movt rn */
+ { 0x002a, SETS1 | USESSP }, /* sts pr,rn */
+ { 0x0032, SETS1 | USESSP }, /* stc ssr,rn */
+ { 0x0042, SETS1 | USESSP }, /* stc spc,rn */
+ { 0x005a, SETS1 | USESSP }, /* sts fpul,rn */
+ { 0x006a, SETS1 | USESSP }, /* sts fpscr,rn */
+ { 0x0082, SETS1 | USESSP }, /* stc r0_bank,rn */
+ { 0x0083, LOAD | USES1 }, /* pref @rn */
+ { 0x0092, SETS1 | USESSP }, /* stc r1_bank,rn */
+ { 0x00a2, SETS1 | USESSP }, /* stc r2_bank,rn */
+ { 0x00b2, SETS1 | USESSP }, /* stc r3_bank,rn */
+ { 0x00c2, SETS1 | USESSP }, /* stc r4_bank,rn */
+ { 0x00d2, SETS1 | USESSP }, /* stc r5_bank,rn */
+ { 0x00e2, SETS1 | USESSP }, /* stc r6_bank,rn */
+ { 0x00f2, SETS1 | USESSP } /* stc r7_bank,rn */
+};
+
+static const struct sh_opcode sh_opcode02[] =
+{
+ { 0x0004, STORE | USES1 | USES2 | USESR0 }, /* mov.b rm,@(r0,rn) */
+ { 0x0005, STORE | USES1 | USES2 | USESR0 }, /* mov.w rm,@(r0,rn) */
+ { 0x0006, STORE | USES1 | USES2 | USESR0 }, /* mov.l rm,@(r0,rn) */
+ { 0x0007, SETSSP | USES1 | USES2 }, /* mul.l rm,rn */
+ { 0x000c, LOAD | SETS1 | USES2 | USESR0 }, /* mov.b @(r0,rm),rn */
+ { 0x000d, LOAD | SETS1 | USES2 | USESR0 }, /* mov.w @(r0,rm),rn */
+ { 0x000e, LOAD | SETS1 | USES2 | USESR0 }, /* mov.l @(r0,rm),rn */
+ { 0x000f, LOAD|SETS1|SETS2|SETSSP|USES1|USES2|USESSP }, /* mac.l @rm+,@rn+ */
+};
+
+static const struct sh_minor_opcode sh_opcode0[] =
+{
+ { MAP (sh_opcode00), 0xffff },
+ { MAP (sh_opcode01), 0xf0ff },
+ { MAP (sh_opcode02), 0xf00f }
+};
+
+static const struct sh_opcode sh_opcode10[] =
+{
+ { 0x1000, STORE | USES1 | USES2 } /* mov.l rm,@(disp,rn) */
+};
+
+static const struct sh_minor_opcode sh_opcode1[] =
+{
+ { MAP (sh_opcode10), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcode20[] =
+{
+ { 0x2000, STORE | USES1 | USES2 }, /* mov.b rm,@rn */
+ { 0x2001, STORE | USES1 | USES2 }, /* mov.w rm,@rn */
+ { 0x2002, STORE | USES1 | USES2 }, /* mov.l rm,@rn */
+ { 0x2004, STORE | SETS1 | USES1 | USES2 }, /* mov.b rm,@-rn */
+ { 0x2005, STORE | SETS1 | USES1 | USES2 }, /* mov.w rm,@-rn */
+ { 0x2006, STORE | SETS1 | USES1 | USES2 }, /* mov.l rm,@-rn */
+ { 0x2007, SETSSP | USES1 | USES2 | USESSP }, /* div0s */
+ { 0x2008, SETSSP | USES1 | USES2 }, /* tst rm,rn */
+ { 0x2009, SETS1 | USES1 | USES2 }, /* and rm,rn */
+ { 0x200a, SETS1 | USES1 | USES2 }, /* xor rm,rn */
+ { 0x200b, SETS1 | USES1 | USES2 }, /* or rm,rn */
+ { 0x200c, SETSSP | USES1 | USES2 }, /* cmp/str rm,rn */
+ { 0x200d, SETS1 | USES1 | USES2 }, /* xtrct rm,rn */
+ { 0x200e, SETSSP | USES1 | USES2 }, /* mulu.w rm,rn */
+ { 0x200f, SETSSP | USES1 | USES2 } /* muls.w rm,rn */
+};
+
+static const struct sh_minor_opcode sh_opcode2[] =
+{
+ { MAP (sh_opcode20), 0xf00f }
+};
+
+static const struct sh_opcode sh_opcode30[] =
+{
+ { 0x3000, SETSSP | USES1 | USES2 }, /* cmp/eq rm,rn */
+ { 0x3002, SETSSP | USES1 | USES2 }, /* cmp/hs rm,rn */
+ { 0x3003, SETSSP | USES1 | USES2 }, /* cmp/ge rm,rn */
+ { 0x3004, SETSSP | USESSP | USES1 | USES2 }, /* div1 rm,rn */
+ { 0x3005, SETSSP | USES1 | USES2 }, /* dmulu.l rm,rn */
+ { 0x3006, SETSSP | USES1 | USES2 }, /* cmp/hi rm,rn */
+ { 0x3007, SETSSP | USES1 | USES2 }, /* cmp/gt rm,rn */
+ { 0x3008, SETS1 | USES1 | USES2 }, /* sub rm,rn */
+ { 0x300a, SETS1 | SETSSP | USES1 | USES2 | USESSP }, /* subc rm,rn */
+ { 0x300b, SETS1 | SETSSP | USES1 | USES2 }, /* subv rm,rn */
+ { 0x300c, SETS1 | USES1 | USES2 }, /* add rm,rn */
+ { 0x300d, SETSSP | USES1 | USES2 }, /* dmuls.l rm,rn */
+ { 0x300e, SETS1 | SETSSP | USES1 | USES2 | USESSP }, /* addc rm,rn */
+ { 0x300f, SETS1 | SETSSP | USES1 | USES2 } /* addv rm,rn */
+};
+
+static const struct sh_minor_opcode sh_opcode3[] =
+{
+ { MAP (sh_opcode30), 0xf00f }
+};
+
+static const struct sh_opcode sh_opcode40[] =
+{
+ { 0x4000, SETS1 | SETSSP | USES1 }, /* shll rn */
+ { 0x4001, SETS1 | SETSSP | USES1 }, /* shlr rn */
+ { 0x4002, STORE | SETS1 | USES1 | USESSP }, /* sts.l mach,@-rn */
+ { 0x4003, STORE | SETS1 | USES1 | USESSP }, /* stc.l sr,@-rn */
+ { 0x4004, SETS1 | SETSSP | USES1 }, /* rotl rn */
+ { 0x4005, SETS1 | SETSSP | USES1 }, /* rotr rn */
+ { 0x4006, LOAD | SETS1 | SETSSP | USES1 }, /* lds.l @rm+,mach */
+ { 0x4007, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,sr */
+ { 0x4008, SETS1 | USES1 }, /* shll2 rn */
+ { 0x4009, SETS1 | USES1 }, /* shlr2 rn */
+ { 0x400a, SETSSP | USES1 }, /* lds rm,mach */
+ { 0x400b, BRANCH | DELAY | USES1 }, /* jsr @rn */
+ { 0x400e, SETSSP | USES1 }, /* ldc rm,sr */
+ { 0x4010, SETS1 | SETSSP | USES1 }, /* dt rn */
+ { 0x4011, SETSSP | USES1 }, /* cmp/pz rn */
+ { 0x4012, STORE | SETS1 | USES1 | USESSP }, /* sts.l macl,@-rn */
+ { 0x4013, STORE | SETS1 | USES1 | USESSP }, /* stc.l gbr,@-rn */
+ { 0x4015, SETSSP | USES1 }, /* cmp/pl rn */
+ { 0x4016, LOAD | SETS1 | SETSSP | USES1 }, /* lds.l @rm+,macl */
+ { 0x4017, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,gbr */
+ { 0x4018, SETS1 | USES1 }, /* shll8 rn */
+ { 0x4019, SETS1 | USES1 }, /* shlr8 rn */
+ { 0x401a, SETSSP | USES1 }, /* lds rm,macl */
+ { 0x401b, LOAD | SETSSP | USES1 }, /* tas.b @rn */
+ { 0x401e, SETSSP | USES1 }, /* ldc rm,gbr */
+ { 0x4020, SETS1 | SETSSP | USES1 }, /* shal rn */
+ { 0x4021, SETS1 | SETSSP | USES1 }, /* shar rn */
+ { 0x4022, STORE | SETS1 | USES1 | USESSP }, /* sts.l pr,@-rn */
+ { 0x4023, STORE | SETS1 | USES1 | USESSP }, /* stc.l vbr,@-rn */
+ { 0x4024, SETS1 | SETSSP | USES1 | USESSP }, /* rotcl rn */
+ { 0x4025, SETS1 | SETSSP | USES1 | USESSP }, /* rotcr rn */
+ { 0x4026, LOAD | SETS1 | SETSSP | USES1 }, /* lds.l @rm+,pr */
+ { 0x4027, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,vbr */
+ { 0x4028, SETS1 | USES1 }, /* shll16 rn */
+ { 0x4029, SETS1 | USES1 }, /* shlr16 rn */
+ { 0x402a, SETSSP | USES1 }, /* lds rm,pr */
+ { 0x402b, BRANCH | DELAY | USES1 }, /* jmp @rn */
+ { 0x402e, SETSSP | USES1 }, /* ldc rm,vbr */
+ { 0x4033, STORE | SETS1 | USES1 | USESSP }, /* stc.l ssr,@-rn */
+ { 0x4037, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,ssr */
+ { 0x403e, SETSSP | USES1 }, /* ldc rm,ssr */
+ { 0x4043, STORE | SETS1 | USES1 | USESSP }, /* stc.l spc,@-rn */
+ { 0x4047, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,spc */
+ { 0x404e, SETSSP | USES1 }, /* ldc rm,spc */
+ { 0x4052, STORE | SETS1 | USES1 | USESSP }, /* sts.l fpul,@-rn */
+ { 0x4056, LOAD | SETS1 | SETSSP | USES1 }, /* lds.l @rm+,fpul */
+ { 0x405a, SETSSP | USES1 }, /* lds.l rm,fpul */
+ { 0x4062, STORE | SETS1 | USES1 | USESSP }, /* sts.l fpscr,@-rn */
+ { 0x4066, LOAD | SETS1 | SETSSP | USES1 }, /* lds.l @rm+,fpscr */
+ { 0x406a, SETSSP | USES1 } /* lds rm,fpscr */
+};
+
+static const struct sh_opcode sh_opcode41[] =
+{
+ { 0x4083, STORE | SETS1 | USES1 | USESSP }, /* stc.l rx_bank,@-rn */
+ { 0x4087, LOAD | SETS1 | SETSSP | USES1 }, /* ldc.l @rm+,rx_bank */
+ { 0x408e, SETSSP | USES1 } /* ldc rm,rx_bank */
+};
+
+static const struct sh_opcode sh_opcode42[] =
+{
+ { 0x400c, SETS1 | USES1 | USES2 }, /* shad rm,rn */
+ { 0x400d, SETS1 | USES1 | USES2 }, /* shld rm,rn */
+ { 0x400f, LOAD|SETS1|SETS2|SETSSP|USES1|USES2|USESSP }, /* mac.w @rm+,@rn+ */
+};
+
+static const struct sh_minor_opcode sh_opcode4[] =
+{
+ { MAP (sh_opcode40), 0xf0ff },
+ { MAP (sh_opcode41), 0xf08f },
+ { MAP (sh_opcode42), 0xf00f }
+};
+
+static const struct sh_opcode sh_opcode50[] =
+{
+ { 0x5000, LOAD | SETS1 | USES2 } /* mov.l @(disp,rm),rn */
+};
+
+static const struct sh_minor_opcode sh_opcode5[] =
+{
+ { MAP (sh_opcode50), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcode60[] =
+{
+ { 0x6000, LOAD | SETS1 | USES2 }, /* mov.b @rm,rn */
+ { 0x6001, LOAD | SETS1 | USES2 }, /* mov.w @rm,rn */
+ { 0x6002, LOAD | SETS1 | USES2 }, /* mov.l @rm,rn */
+ { 0x6003, SETS1 | USES2 }, /* mov rm,rn */
+ { 0x6004, LOAD | SETS1 | SETS2 | USES2 }, /* mov.b @rm+,rn */
+ { 0x6005, LOAD | SETS1 | SETS2 | USES2 }, /* mov.w @rm+,rn */
+ { 0x6006, LOAD | SETS1 | SETS2 | USES2 }, /* mov.l @rm+,rn */
+ { 0x6007, SETS1 | USES2 }, /* not rm,rn */
+ { 0x6008, SETS1 | USES2 }, /* swap.b rm,rn */
+ { 0x6009, SETS1 | USES2 }, /* swap.w rm,rn */
+ { 0x600a, SETS1 | SETSSP | USES2 | USESSP }, /* negc rm,rn */
+ { 0x600b, SETS1 | USES2 }, /* neg rm,rn */
+ { 0x600c, SETS1 | USES2 }, /* extu.b rm,rn */
+ { 0x600d, SETS1 | USES2 }, /* extu.w rm,rn */
+ { 0x600e, SETS1 | USES2 }, /* exts.b rm,rn */
+ { 0x600f, SETS1 | USES2 } /* exts.w rm,rn */
+};
+
+static const struct sh_minor_opcode sh_opcode6[] =
+{
+ { MAP (sh_opcode60), 0xf00f }
+};
+
+static const struct sh_opcode sh_opcode70[] =
+{
+ { 0x7000, SETS1 | USES1 } /* add #imm,rn */
+};
+
+static const struct sh_minor_opcode sh_opcode7[] =
+{
+ { MAP (sh_opcode70), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcode80[] =
+{
+ { 0x8000, STORE | USES2 | USESR0 }, /* mov.b r0,@(disp,rn) */
+ { 0x8100, STORE | USES2 | USESR0 }, /* mov.w r0,@(disp,rn) */
+ { 0x8400, LOAD | SETSR0 | USES2 }, /* mov.b @(disp,rm),r0 */
+ { 0x8500, LOAD | SETSR0 | USES2 }, /* mov.w @(disp,rn),r0 */
+ { 0x8800, SETSSP | USESR0 }, /* cmp/eq #imm,r0 */
+ { 0x8900, BRANCH | USESSP }, /* bt label */
+ { 0x8b00, BRANCH | USESSP }, /* bf label */
+ { 0x8d00, BRANCH | DELAY | USESSP }, /* bt/s label */
+ { 0x8f00, BRANCH | DELAY | USESSP } /* bf/s label */
+};
+
+static const struct sh_minor_opcode sh_opcode8[] =
+{
+ { MAP (sh_opcode80), 0xff00 }
+};
+
+static const struct sh_opcode sh_opcode90[] =
+{
+ { 0x9000, LOAD | SETS1 } /* mov.w @(disp,pc),rn */
+};
+
+static const struct sh_minor_opcode sh_opcode9[] =
+{
+ { MAP (sh_opcode90), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcodea0[] =
+{
+ { 0xa000, BRANCH | DELAY } /* bra label */
+};
+
+static const struct sh_minor_opcode sh_opcodea[] =
+{
+ { MAP (sh_opcodea0), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcodeb0[] =
+{
+ { 0xb000, BRANCH | DELAY } /* bsr label */
+};
+
+static const struct sh_minor_opcode sh_opcodeb[] =
+{
+ { MAP (sh_opcodeb0), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcodec0[] =
+{
+ { 0xc000, STORE | USESR0 | USESSP }, /* mov.b r0,@(disp,gbr) */
+ { 0xc100, STORE | USESR0 | USESSP }, /* mov.w r0,@(disp,gbr) */
+ { 0xc200, STORE | USESR0 | USESSP }, /* mov.l r0,@(disp,gbr) */
+ { 0xc300, BRANCH | USESSP }, /* trapa #imm */
+ { 0xc400, LOAD | SETSR0 | USESSP }, /* mov.b @(disp,gbr),r0 */
+ { 0xc500, LOAD | SETSR0 | USESSP }, /* mov.w @(disp,gbr),r0 */
+ { 0xc600, LOAD | SETSR0 | USESSP }, /* mov.l @(disp,gbr),r0 */
+ { 0xc700, SETSR0 }, /* mova @(disp,pc),r0 */
+ { 0xc800, SETSSP | USESR0 }, /* tst #imm,r0 */
+ { 0xc900, SETSR0 | USESR0 }, /* and #imm,r0 */
+ { 0xca00, SETSR0 | USESR0 }, /* xor #imm,r0 */
+ { 0xcb00, SETSR0 | USESR0 }, /* or #imm,r0 */
+ { 0xcc00, LOAD | SETSSP | USESR0 | USESSP }, /* tst.b #imm,@(r0,gbr) */
+ { 0xcd00, LOAD | STORE | USESR0 | USESSP }, /* and.b #imm,@(r0,gbr) */
+ { 0xce00, LOAD | STORE | USESR0 | USESSP }, /* xor.b #imm,@(r0,gbr) */
+ { 0xcf00, LOAD | STORE | USESR0 | USESSP } /* or.b #imm,@(r0,gbr) */
+};
+
+static const struct sh_minor_opcode sh_opcodec[] =
+{
+ { MAP (sh_opcodec0), 0xff00 }
+};
+
+static const struct sh_opcode sh_opcoded0[] =
+{
+ { 0xd000, LOAD | SETS1 } /* mov.l @(disp,pc),rn */
+};
+
+static const struct sh_minor_opcode sh_opcoded[] =
+{
+ { MAP (sh_opcoded0), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcodee0[] =
+{
+ { 0xe000, SETS1 } /* mov #imm,rn */
+};
+
+static const struct sh_minor_opcode sh_opcodee[] =
+{
+ { MAP (sh_opcodee0), 0xf000 }
+};
+
+static const struct sh_opcode sh_opcodef0[] =
+{
+ { 0xf000, SETSF1 | USESF1 | USESF2 }, /* fadd fm,fn */
+ { 0xf001, SETSF1 | USESF1 | USESF2 }, /* fsub fm,fn */
+ { 0xf002, SETSF1 | USESF1 | USESF2 }, /* fmul fm,fn */
+ { 0xf003, SETSF1 | USESF1 | USESF2 }, /* fdiv fm,fn */
+ { 0xf004, SETSSP | USESF1 | USESF2 }, /* fcmp/eq fm,fn */
+ { 0xf005, SETSSP | USESF1 | USESF2 }, /* fcmp/gt fm,fn */
+ { 0xf006, LOAD | SETSF1 | USES2 | USESR0 }, /* fmov.s @(r0,rm),fn */
+ { 0xf007, STORE | USES1 | USESF2 | USESR0 }, /* fmov.s fm,@(r0,rn) */
+ { 0xf008, LOAD | SETSF1 | USES2 }, /* fmov.s @rm,fn */
+ { 0xf009, LOAD | SETS2 | SETSF1 | USES2 }, /* fmov.s @rm+,fn */
+ { 0xf00a, STORE | USES1 | USESF2 }, /* fmov.s fm,@rn */
+ { 0xf00b, STORE | SETS1 | USES1 | USESF2 }, /* fmov.s fm,@-rn */
+ { 0xf00c, SETSF1 | USESF2 }, /* fmov fm,fn */
+ { 0xf00e, SETSF1 | USESF1 | USESF2 | USESF0 } /* fmac f0,fm,fn */
+};
+
+static const struct sh_opcode sh_opcodef1[] =
+{
+ { 0xf00d, SETSF1 | USESSP }, /* fsts fpul,fn */
+ { 0xf01d, SETSSP | USESF1 }, /* flds fn,fpul */
+ { 0xf02d, SETSF1 | USESSP }, /* float fpul,fn */
+ { 0xf03d, SETSSP | USESF1 }, /* ftrc fn,fpul */
+ { 0xf04d, SETSF1 | USESF1 }, /* fneg fn */
+ { 0xf05d, SETSF1 | USESF1 }, /* fabs fn */
+ { 0xf06d, SETSF1 | USESF1 }, /* fsqrt fn */
+ { 0xf07d, SETSSP | USESF1 }, /* ftst/nan fn */
+ { 0xf08d, SETSF1 }, /* fldi0 fn */
+ { 0xf09d, SETSF1 } /* fldi1 fn */
+};
+
+static const struct sh_minor_opcode sh_opcodef[] =
+{
+ { MAP (sh_opcodef0), 0xf00f },
+ { MAP (sh_opcodef1), 0xf0ff }
+};
+
+static const struct sh_major_opcode sh_opcodes[] =
+{
+ { MAP (sh_opcode0) },
+ { MAP (sh_opcode1) },
+ { MAP (sh_opcode2) },
+ { MAP (sh_opcode3) },
+ { MAP (sh_opcode4) },
+ { MAP (sh_opcode5) },
+ { MAP (sh_opcode6) },
+ { MAP (sh_opcode7) },
+ { MAP (sh_opcode8) },
+ { MAP (sh_opcode9) },
+ { MAP (sh_opcodea) },
+ { MAP (sh_opcodeb) },
+ { MAP (sh_opcodec) },
+ { MAP (sh_opcoded) },
+ { MAP (sh_opcodee) },
+ { MAP (sh_opcodef) }
+};
+
+/* Given an instruction, return a pointer to the corresponding
+ sh_opcode structure. Return NULL if the instruction is not
+ recognized. */
+
+static const struct sh_opcode *
+sh_insn_info (insn)
+ unsigned int insn;
+{
+ const struct sh_major_opcode *maj;
+ const struct sh_minor_opcode *min, *minend;
+
+ maj = &sh_opcodes[(insn & 0xf000) >> 12];
+ min = maj->minor_opcodes;
+ minend = min + maj->count;
+ for (; min < minend; min++)
+ {
+ unsigned int l;
+ const struct sh_opcode *op, *opend;
+
+ l = insn & min->mask;
+ op = min->opcodes;
+ opend = op + min->count;
+
+ /* Since the opcodes tables are sorted, we could use a binary
+ search here if the count were above some cutoff value. */
+ for (; op < opend; op++)
+ if (op->opcode == l)
+ return op;
+ }
+
+ return NULL;
+}
+
+/* See whether an instruction uses a general purpose register. */
+
+static boolean
+sh_insn_uses_reg (insn, op, reg)
+ unsigned int insn;
+ const struct sh_opcode *op;
+ unsigned int reg;
+{
+ unsigned int f;
+
+ f = op->flags;
+
+ if ((f & USES1) != 0
+ && ((insn & 0x0f00) >> 8) == reg)
+ return true;
+ if ((f & USES2) != 0
+ && ((insn & 0x00f0) >> 4) == reg)
+ return true;
+ if ((f & USESR0) != 0
+ && reg == 0)
+ return true;
+
+ return false;
+}
+
+/* See whether an instruction uses a floating point register. */
+
+static boolean
+sh_insn_uses_freg (insn, op, freg)
+ unsigned int insn;
+ const struct sh_opcode *op;
+ unsigned int freg;
+{
+ unsigned int f;
+
+ f = op->flags;
+
+ if ((f & USESF1) != 0
+ && ((insn & 0x0f00) >> 8) == freg)
+ return true;
+ if ((f & USESF2) != 0
+ && ((insn & 0x00f0) >> 4) == freg)
+ return true;
+ if ((f & USESF0) != 0
+ && freg == 0)
+ return true;
+
+ return false;
+}
+
+/* See whether instructions I1 and I2 conflict, assuming I1 comes
+ before I2. OP1 and OP2 are the corresponding sh_opcode structures.
+ This should return true if the instructions can be swapped safely. */
+
+static boolean
+sh_insns_conflict (i1, op1, i2, op2)
+ unsigned int i1;
+ const struct sh_opcode *op1;
+ unsigned int i2;
+ const struct sh_opcode *op2;
+{
+ unsigned int f1, f2;
+
+ f1 = op1->flags;
+ f2 = op2->flags;
+
+ if ((f1 & (BRANCH | DELAY)) != 0
+ || (f2 & (BRANCH | DELAY)) != 0)
+ return true;
+
+ if ((f1 & SETSSP) != 0 && (f2 & USESSP) != 0)
+ return false;
+ if ((f2 & SETSSP) != 0 && (f1 & USESSP) != 0)
+ return true;
+
+ if ((f1 & SETS1) != 0
+ && sh_insn_uses_reg (i2, op2, (i1 & 0x0f00) >> 8))
+ return true;
+ if ((f1 & SETS2) != 0
+ && sh_insn_uses_reg (i2, op2, (i1 & 0x00f0) >> 4))
+ return true;
+ if ((f1 & SETSR0) != 0
+ && sh_insn_uses_reg (i2, op2, 0))
+ return true;
+ if ((f1 & SETSF1) != 0
+ && sh_insn_uses_freg (i2, op2, (i1 & 0x0f00) >> 8))
+ return true;
+
+ if ((f2 & SETS1) != 0
+ && sh_insn_uses_reg (i1, op1, (i2 & 0x0f00) >> 8))
+ return true;
+ if ((f2 & SETS2) != 0
+ && sh_insn_uses_reg (i1, op1, (i2 & 0x00f0) >> 4))
+ return true;
+ if ((f2 & SETSR0) != 0
+ && sh_insn_uses_reg (i1, op1, 0))
+ return true;
+ if ((f2 & SETSF1) != 0
+ && sh_insn_uses_freg (i1, op1, (i2 & 0x0f00) >> 8))
+ return true;
+
+ /* The instructions do not conflict. */
+ return false;
+}
+
+/* I1 is a load instruction, and I2 is some other instruction. Return
+ true if I1 loads a register which I2 uses. */
+
+static boolean
+sh_load_use (i1, op1, i2, op2)
+ unsigned int i1;
+ const struct sh_opcode *op1;
+ unsigned int i2;
+ const struct sh_opcode *op2;
+{
+ unsigned int f1;
+
+ f1 = op1->flags;
+
+ if ((f1 & LOAD) == 0)
+ return false;
+
+ /* If both SETS1 and SETSSP are set, that means a load to a special
+ register using postincrement addressing mode, which we don't care
+ about here. */
+ if ((f1 & SETS1) != 0
+ && (f1 & SETSSP) == 0
+ && sh_insn_uses_reg (i2, op2, (i1 & 0x0f00) >> 8))
+ return true;
+
+ if ((f1 & SETSR0) != 0
+ && sh_insn_uses_reg (i2, op2, 0))
+ return true;
+
+ if ((f1 & SETSF1) != 0
+ && sh_insn_uses_freg (i2, op2, (i1 & 0x0f00) >> 8))
+ return true;
+
+ return false;
+}
+
+/* Look for loads and stores which we can align to four byte
+ boundaries. See the longer comment above sh_relax_section for why
+ this is desirable. This sets *PSWAPPED if some instruction was
+ swapped. */
+
+static boolean
+sh_align_loads (abfd, sec, internal_relocs, contents, pswapped)
+ bfd *abfd;
+ asection *sec;
+ struct internal_reloc *internal_relocs;
+ bfd_byte *contents;
+ boolean *pswapped;
+{
+ struct internal_reloc *irel, *irelend;
+ bfd_vma *labels = NULL;
+ bfd_vma *label, *label_end;
+
+ *pswapped = false;
+
+ irelend = internal_relocs + sec->reloc_count;
+
+ /* Get all the addresses with labels on them. */
+ labels = (bfd_vma *) bfd_malloc (sec->reloc_count * sizeof (bfd_vma));
+ if (labels == NULL)
+ goto error_return;
+ label_end = labels;
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ if (irel->r_type == R_SH_LABEL)
+ {
+ *label_end = irel->r_vaddr - sec->vma;
+ ++label_end;
+ }
+ }
+
+ /* Note that the assembler currently always outputs relocs in
+ address order. If that ever changes, this code will need to sort
+ the label values and the relocs. */
+
+ label = labels;
+
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ bfd_vma start, stop, i;
+
+ if (irel->r_type != R_SH_CODE)
+ continue;
+
+ start = irel->r_vaddr - sec->vma;
+
+ for (irel++; irel < irelend; irel++)
+ if (irel->r_type == R_SH_DATA)
+ break;
+ if (irel < irelend)
+ stop = irel->r_vaddr - sec->vma;
+ else
+ stop = sec->_cooked_size;
+
+ /* Instructions should be aligned on 2 byte boundaries. */
+ if ((start & 1) == 1)
+ ++start;
+
+ /* Now look through the unaligned addresses. */
+ i = start;
+ if ((i & 2) == 0)
+ i += 2;
+ for (; i < stop; i += 4)
+ {
+ unsigned int insn;
+ const struct sh_opcode *op;
+ unsigned int prev_insn = 0;
+ const struct sh_opcode *prev_op = NULL;
+
+ insn = bfd_get_16 (abfd, contents + i);
+ op = sh_insn_info (insn);
+ if (op == NULL
+ || (op->flags & (LOAD | STORE)) == 0)
+ continue;
+
+ /* This is a load or store which is not on a four byte
+ boundary. */
+
+ while (label < label_end && *label < i)
+ ++label;
+
+ if (i > start)
+ {
+ prev_insn = bfd_get_16 (abfd, contents + i - 2);
+ prev_op = sh_insn_info (prev_insn);
+
+ /* If the load/store instruction is in a delay slot, we
+ can't swap. */
+ if (prev_op == NULL
+ || (prev_op->flags & DELAY) != 0)
+ continue;
+ }
+ if (i > start
+ && (label >= label_end || *label != i)
+ && prev_op != NULL
+ && (prev_op->flags & (LOAD | STORE)) == 0
+ && ! sh_insns_conflict (prev_insn, prev_op, insn, op))
+ {
+ boolean ok;
+
+ /* The load/store instruction does not have a label, and
+ there is a previous instruction; PREV_INSN is not
+ itself a load/store instruction, and PREV_INSN and
+ INSN do not conflict. */
+
+ ok = true;
+
+ if (i >= start + 4)
+ {
+ unsigned int prev2_insn;
+ const struct sh_opcode *prev2_op;
+
+ prev2_insn = bfd_get_16 (abfd, contents + i - 4);
+ prev2_op = sh_insn_info (prev2_insn);
+
+ /* If the instruction before PREV_INSN has a delay
+ slot--that is, PREV_INSN is in a delay slot--we
+ can not swap. */
+ if (prev2_op == NULL
+ || (prev2_op->flags & DELAY) != 0)
+ ok = false;
+
+ /* If the instruction before PREV_INSN is a load,
+ and it sets a register which INSN uses, then
+ putting INSN immediately after PREV_INSN will
+ cause a pipeline bubble, so there is no point to
+ making the swap. */
+ if (ok
+ && (prev2_op->flags & LOAD) != 0
+ && sh_load_use (prev2_insn, prev2_op, insn, op))
+ ok = false;
+ }
+
+ if (ok)
+ {
+ if (! sh_swap_insns (abfd, sec, internal_relocs,
+ contents, i - 2))
+ goto error_return;
+ *pswapped = true;
+ continue;
+ }
+ }
+
+ while (label < label_end && *label < i + 2)
+ ++label;
+
+ if (i + 2 < stop
+ && (label >= label_end || *label != i + 2))
+ {
+ unsigned int next_insn;
+ const struct sh_opcode *next_op;
+
+ /* There is an instruction after the load/store
+ instruction, and it does not have a label. */
+ next_insn = bfd_get_16 (abfd, contents + i + 2);
+ next_op = sh_insn_info (next_insn);
+ if (next_op != NULL
+ && (next_op->flags & (LOAD | STORE)) == 0
+ && ! sh_insns_conflict (insn, op, next_insn, next_op))
+ {
+ boolean ok;
+
+ /* NEXT_INSN is not itself a load/store instruction,
+ and it does not conflict with INSN. */
+
+ ok = true;
+
+ /* If PREV_INSN is a load, and it sets a register
+ which NEXT_INSN uses, then putting NEXT_INSN
+ immediately after PREV_INSN will cause a pipeline
+ bubble, so there is no reason to make this swap. */
+ if (prev_op != NULL
+ && (prev_op->flags & LOAD) != 0
+ && sh_load_use (prev_insn, prev_op, next_insn, next_op))
+ ok = false;
+
+ /* If INSN is a load, and it sets a register which
+ the insn after NEXT_INSN uses, then doing the
+ swap will cause a pipeline bubble, so there is no
+ reason to make the swap. However, if the insn
+ after NEXT_INSN is itself a load or store
+ instruction, then it is misaligned, so
+ optimistically hope that it will be swapped
+ itself, and just live with the pipeline bubble if
+ it isn't. */
+ if (ok
+ && i + 4 < stop
+ && (op->flags & LOAD) != 0)
+ {
+ unsigned int next2_insn;
+ const struct sh_opcode *next2_op;
+
+ next2_insn = bfd_get_16 (abfd, contents + i + 4);
+ next2_op = sh_insn_info (next2_insn);
+ if ((next2_op->flags & (LOAD | STORE)) == 0
+ && sh_load_use (insn, op, next2_insn, next2_op))
+ ok = false;
+ }
+
+ if (ok)
+ {
+ if (! sh_swap_insns (abfd, sec, internal_relocs,
+ contents, i))
+ goto error_return;
+ *pswapped = true;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ free (labels);
+
+ return true;
+
+ error_return:
+ if (labels != NULL)
+ free (labels);
+ return false;
+}
+
+/* Swap two SH instructions. */
+
+static boolean
+sh_swap_insns (abfd, sec, internal_relocs, contents, addr)
+ bfd *abfd;
+ asection *sec;
+ struct internal_reloc *internal_relocs;
+ bfd_byte *contents;
+ bfd_vma addr;
+{
+ unsigned short i1, i2;
+ struct internal_reloc *irel, *irelend;
+
+ /* Swap the instructions themselves. */
+ i1 = bfd_get_16 (abfd, contents + addr);
+ i2 = bfd_get_16 (abfd, contents + addr + 2);
+ bfd_put_16 (abfd, i2, contents + addr);
+ bfd_put_16 (abfd, i1, contents + addr + 2);
+ /* Adjust all reloc addresses. */
+ irelend = internal_relocs + sec->reloc_count;
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ int type, add;
+
+ /* There are a few special types of relocs that we don't want to
+ adjust. These relocs do not apply to the instruction itself,
+ but are only associated with the address. */
+ type = irel->r_type;
+ if (type == R_SH_ALIGN
+ || type == R_SH_CODE
+ || type == R_SH_DATA
+ || type == R_SH_LABEL)
+ continue;
+
+ /* If an R_SH_USES reloc points to one of the addresses being
+ swapped, we must adjust it. It would be incorrect to do this
+ for a jump, though, since we want to execute both
+ instructions after the jump. (We have avoided swapping
+ around a label, so the jump will not wind up executing an
+ instruction it shouldn't). */
+ if (type == R_SH_USES)
+ {
+ bfd_vma off;
+
+ off = irel->r_vaddr - sec->vma + 4 + irel->r_offset;
+ if (off == addr)
+ irel->r_offset += 2;
+ else if (off == addr + 2)
+ irel->r_offset -= 2;
+ }
+
+ if (irel->r_vaddr - sec->vma == addr)
+ {
+ irel->r_vaddr += 2;
+ add = -2;
+ }
+ else if (irel->r_vaddr - sec->vma == addr + 2)
+ {
+ irel->r_vaddr -= 2;
+ add = 2;
+ }
+ else
+ add = 0;
+
+ if (add != 0)
+ {
+ bfd_byte *loc;
+ unsigned short insn, oinsn;
+ boolean overflow;
+
+ loc = contents + irel->r_vaddr - sec->vma;
+ overflow = false;
+ switch (type)
+ {
+ default:
+ break;
+
+ case R_SH_PCDISP8BY2:
+ case R_SH_PCRELIMM8BY2:
+ insn = bfd_get_16 (abfd, loc);
+ oinsn = insn;
+ insn += add / 2;
+ if ((oinsn & 0xff00) != (insn & 0xff00))
+ overflow = true;
+ bfd_put_16 (abfd, insn, loc);
+ break;
+
+ case R_SH_PCDISP:
+ insn = bfd_get_16 (abfd, loc);
+ oinsn = insn;
+ insn += add / 2;
+ if ((oinsn & 0xf000) != (insn & 0xf000))
+ overflow = true;
+ bfd_put_16 (abfd, insn, loc);
+ break;
+
+ case R_SH_PCRELIMM8BY4:
+ /* This reloc ignores the least significant 3 bits of
+ the program counter before adding in the offset.
+ This means that if ADDR is at an even address, the
+ swap will not affect the offset. If ADDR is an at an
+ odd address, then the instruction will be crossing a
+ four byte boundary, and must be adjusted. */
+ if ((addr & 3) != 0)
+ {
+ insn = bfd_get_16 (abfd, loc);
+ oinsn = insn;
+ insn += add / 2;
+ if ((oinsn & 0xff00) != (insn & 0xff00))
+ overflow = true;
+ bfd_put_16 (abfd, insn, loc);
+ }
+
+ break;
+ }
+
+ if (overflow)
+ {
+ ((*_bfd_error_handler)
+ ("%s: 0x%lx: fatal: reloc overflow while relaxing",
+ bfd_get_filename (abfd), (unsigned long) irel->r_vaddr));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+\f
/* This is a modification of _bfd_coff_generic_relocate_section, which
will handle SH relaxing. */
{
"coff-sh", /* name */
bfd_target_coff_flavour,
- true, /* data byte order is big */
- true, /* header byte order is big */
+ BFD_ENDIAN_BIG, /* data byte order is big */
+ BFD_ENDIAN_BIG, /* header byte order is big */
(HAS_RELOC | EXEC_P | /* object flags */
HAS_LINENO | HAS_DEBUG |
{
"coff-shl", /* name */
bfd_target_coff_flavour,
- false, /* data byte order is little */
- false, /* header byte order is little endian too*/
+ BFD_ENDIAN_LITTLE, /* data byte order is little */
+ BFD_ENDIAN_LITTLE, /* header byte order is little endian too*/
(HAS_RELOC | EXEC_P | /* object flags */
HAS_LINENO | HAS_DEBUG |