2004-05-28 Andrew Stubbs <andrew.stubbs@superh.com>
[deliverable/binutils-gdb.git] / gas / config / tc-sh.c
index cdf249e3f24026ff719af694ed610bc1714c6f4d..a7e60ea9d0b59cce4776908b9d212696ddfe08b5 100644 (file)
@@ -1,5 +1,6 @@
-/* tc-sh.c -- Assemble code for the Hitachi Super-H
-   Copyright (C) 1993, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation.
+/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003, 2004  Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -26,7 +27,7 @@
 #include "subsegs.h"
 #define DEFINE_TABLE
 #include "opcodes/sh-opc.h"
-#include <ctype.h>
+#include "safe-ctype.h"
 #include "struc-symbol.h"
 
 #ifdef OBJ_ELF
 #endif
 
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+typedef struct
+  {
+    sh_arg_type type;
+    int reg;
+    expressionS immediate;
+  }
+sh_operand_info;
 
 const char comment_chars[] = "!";
 const char line_separator_chars[] = ";";
 const char line_comment_chars[] = "!#";
 
-static void s_uses PARAMS ((int));
-
-static void sh_count_relocs PARAMS ((bfd *, segT, PTR));
-static void sh_frob_section PARAMS ((bfd *, segT, PTR));
-
-void cons ();
-void s_align_bytes ();
-static void s_uacons PARAMS ((int));
-static sh_opcode_info *find_cooked_opcode PARAMS ((char **));
-static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *));
+static void s_uses (int);
+static void s_uacons (int);
 
 #ifdef OBJ_ELF
-static void sh_elf_cons PARAMS ((int));
+static void sh_elf_cons (int);
 
 symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 #endif
 
-int shl = 0;
+static void
+big (int ignore ATTRIBUTE_UNUSED)
+{
+  if (! target_big_endian)
+    as_bad (_("directive .big encountered when option -big required"));
+
+  /* Stop further messages.  */
+  target_big_endian = 1;
+}
 
 static void
-little (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+little (int ignore ATTRIBUTE_UNUSED)
 {
-  shl = 1;
+  if (target_big_endian)
+    as_bad (_("directive .little encountered when option -little required"));
+
+  /* Stop further messages.  */
   target_big_endian = 0;
 }
 
@@ -83,6 +95,7 @@ const pseudo_typeS md_pseudo_table[] =
   {"int", cons, 4},
   {"word", cons, 2},
 #endif /* OBJ_ELF */
+  {"big", big, 0},
   {"form", listing_psize, 0},
   {"little", little, 0},
   {"heading", listing_title, 0},
@@ -92,8 +105,22 @@ const pseudo_typeS md_pseudo_table[] =
   {"uses", s_uses, 0},
   {"uaword", s_uacons, 2},
   {"ualong", s_uacons, 4},
-  { "file", dwarf2_directive_file, 0 },
-  { "loc", dwarf2_directive_loc, 0 },
+  {"uaquad", s_uacons, 8},
+  {"2byte", s_uacons, 2},
+  {"4byte", s_uacons, 4},
+  {"8byte", s_uacons, 8},
+#ifdef HAVE_SH64
+  {"mode", s_sh64_mode, 0 },
+
+  /* Have the old name too.  */
+  {"isa", s_sh64_mode, 0 },
+
+  /* Assert that the right ABI is used.  */
+  {"abi", s_sh64_abi, 0 },
+
+  { "vtable_inherit", sh64_vtable_inherit, 0 },
+  { "vtable_entry", sh64_vtable_entry, 0 },
+#endif /* HAVE_SH64 */
   {0, 0, 0}
 };
 
@@ -105,13 +132,17 @@ int sh_relax;             /* set if -relax seen */
 
 int sh_small;
 
-/* Whether -dsp was seen.  */
+/* Flag to generate relocations against symbol values for local symbols.  */
+
+static int dont_adjust_reloc_32;
 
-static int sh_dsp;
+/* preset architecture set, if given; zero otherwise.  */
+
+static unsigned int preset_target_arch;
 
 /* The bit mask of architectures that could
-   accomodate the insns seen so far.  */
-static int valid_arch;
+   accommodate the insns seen so far.  */
+static unsigned int valid_arch;
 
 const char EXP_CHARS[] = "eE";
 
@@ -125,23 +156,65 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define ENCODE_RELAX(what,length) (((what) << 4) + (length))
 #define GET_WHAT(x) ((x>>4))
 
-/* These are the three types of relaxable instrction.  */
+/* These are the three types of relaxable instruction.  */
+/* These are the types of relaxable instructions; except for END which is
+   a marker.  */
 #define COND_JUMP 1
 #define COND_JUMP_DELAY 2
 #define UNCOND_JUMP  3
+
+#ifdef HAVE_SH64
+
+/* A 16-bit (times four) pc-relative operand, at most expanded to 32 bits.  */
+#define SH64PCREL16_32 4
+/* A 16-bit (times four) pc-relative operand, at most expanded to 64 bits.  */
+#define SH64PCREL16_64 5
+
+/* Variants of the above for adjusting the insn to PTA or PTB according to
+   the label.  */
+#define SH64PCREL16PT_32 6
+#define SH64PCREL16PT_64 7
+
+/* A MOVI expansion, expanding to at most 32 or 64 bits.  */
+#define MOVI_IMM_32 8
+#define MOVI_IMM_32_PCREL 9
+#define MOVI_IMM_64 10
+#define MOVI_IMM_64_PCREL 11
+#define END 12
+
+#else  /* HAVE_SH64 */
+
 #define END 4
 
+#endif /* HAVE_SH64 */
+
 #define UNDEF_DISP 0
 #define COND8  1
 #define COND12 2
 #define COND32 3
-#define UNCOND12 1
-#define UNCOND32 2
 #define UNDEF_WORD_DISP 4
 
 #define UNCOND12 1
 #define UNCOND32 2
 
+#ifdef HAVE_SH64
+#define UNDEF_SH64PCREL 0
+#define SH64PCREL16 1
+#define SH64PCREL32 2
+#define SH64PCREL48 3
+#define SH64PCREL64 4
+#define SH64PCRELPLT 5
+
+#define UNDEF_MOVI 0
+#define MOVI_16 1
+#define MOVI_32 2
+#define MOVI_48 3
+#define MOVI_64 4
+#define MOVI_PLT 5
+#define MOVI_GOTOFF 6
+#define MOVI_GOTPC 7
+#endif /* HAVE_SH64 */
+
 /* Branch displacements are from the address of the branch plus
    four, thus all minimum and maximum values have 4 added to them.  */
 #define COND8_F 258
@@ -172,6 +245,85 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define UNCOND32_M -(1<<30)
 #define UNCOND32_LENGTH 14
 
+#ifdef HAVE_SH64
+/* The trivial expansion of a SH64PCREL16 relaxation is just a "PT label,
+   TRd" as is the current insn, so no extra length.  Note that the "reach"
+   is calculated from the address *after* that insn, but the offset in the
+   insn is calculated from the beginning of the insn.  We also need to
+   take into account the implicit 1 coded as the "A" in PTA when counting
+   forward.  If PTB reaches an odd address, we trap that as an error
+   elsewhere, so we don't have to have different relaxation entries.  We
+   don't add a one to the negative range, since PTB would then have the
+   farthest backward-reaching value skipped, not generated at relaxation.  */
+#define SH64PCREL16_F (32767 * 4 - 4 + 1)
+#define SH64PCREL16_M (-32768 * 4 - 4)
+#define SH64PCREL16_LENGTH 0
+
+/* The next step is to change that PT insn into
+     MOVI ((label - datalabel Ln) >> 16) & 65535, R25
+     SHORI (label - datalabel Ln) & 65535, R25
+    Ln:
+     PTREL R25,TRd
+   which means two extra insns, 8 extra bytes.  This is the limit for the
+   32-bit ABI.
+
+   The expressions look a bit bad since we have to adjust this to avoid overflow on a
+   32-bit host.  */
+#define SH64PCREL32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define SH64PCREL32_LENGTH (2 * 4)
+
+/* Similarly, we just change the MOVI and add a SHORI for the 48-bit
+   expansion.  */
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+   host.  */
+#define SH64PCREL32_M (((long) -1 << 30) * 2 - 4)
+#define SH64PCREL48_F ((((long) 1 << 47) - 1) - 4)
+#define SH64PCREL48_M (((long) -1 << 47) - 4)
+#define SH64PCREL48_LENGTH (3 * 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+   in reach to the 32-bit state.  Note that we have a slightly incorrect
+   reach, but the correct one above will overflow a 32-bit number.  */
+#define SH64PCREL32_M (((long) -1 << 30) * 2)
+#define SH64PCREL48_F SH64PCREL32_F
+#define SH64PCREL48_M SH64PCREL32_M
+#define SH64PCREL48_LENGTH (3 * 4)
+#endif /* BFD_HOST_64BIT_LONG */
+
+/* And similarly for the 64-bit expansion; a MOVI + SHORI + SHORI + SHORI
+   + PTREL sequence.  */
+#define SH64PCREL64_LENGTH (4 * 4)
+
+/* For MOVI, we make the MOVI + SHORI... expansion you can see in the
+   SH64PCREL expansions.  The PCREL one is similar, but the other has no
+   pc-relative reach; it must be fully expanded in
+   shmedia_md_estimate_size_before_relax.  */
+#define MOVI_16_LENGTH 0
+#define MOVI_16_F (32767 - 4)
+#define MOVI_16_M (-32768 - 4)
+#define MOVI_32_LENGTH 4
+#define MOVI_32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define MOVI_48_LENGTH 8
+
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+   host.  */
+#define MOVI_32_M (((long) -1 << 30) * 2 - 4)
+#define MOVI_48_F ((((long) 1 << 47) - 1) - 4)
+#define MOVI_48_M (((long) -1 << 47) - 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+   in reach to the 32-bit state.  Note that we have a slightly incorrect
+   reach, but the correct one above will overflow a 32-bit number.  */
+#define MOVI_32_M (((long) -1 << 30) * 2)
+#define MOVI_48_F MOVI_32_F
+#define MOVI_48_M MOVI_32_M
+#endif /* BFD_HOST_64BIT_LONG */
+
+#define MOVI_64_LENGTH 12
+#endif /* HAVE_SH64 */
+
 #define EMPTY { 0, 0, 0, 0 }
 
 const relax_typeS md_relax_table[C (END, 0)] = {
@@ -185,7 +337,9 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
   /* C (COND_JUMP, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY,
+  /* C (COND_JUMP, UNDEF_WORD_DISP) */
+  { 0, 0, COND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
   EMPTY,
@@ -195,7 +349,9 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
   /* C (COND_JUMP_DELAY, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY,
+  /* C (COND_JUMP_DELAY, UNDEF_WORD_DISP) */
+  { 0, 0, COND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
   EMPTY,
@@ -203,8 +359,123 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
   /* C (UNCOND_JUMP, UNCOND32) */
   { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY,
+  /* C (UNCOND_JUMP, UNDEF_WORD_DISP) */
+  { 0, 0, UNCOND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#ifdef HAVE_SH64
+  /* C (SH64PCREL16_32, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_32, SH64PCREL32) },
+  /* C (SH64PCREL16_32, SH64PCREL32) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (SH64PCREL16_32, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16_64, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_64, SH64PCREL32) },
+  /* C (SH64PCREL16_64, SH64PCREL32) */
+  { SH64PCREL32_F, SH64PCREL32_M, SH64PCREL32_LENGTH, C (SH64PCREL16_64, SH64PCREL48) },
+  /* C (SH64PCREL16_64, SH64PCREL48) */
+  { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16_64, SH64PCREL64) },
+  /* C (SH64PCREL16_64, SH64PCREL64) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  /* C (SH64PCREL16_64, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16PT_32, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_32, SH64PCREL32) },
+  /* C (SH64PCREL16PT_32, SH64PCREL32) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (SH64PCREL16PT_32, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16PT_64, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_64, SH64PCREL32) },
+  /* C (SH64PCREL16PT_64, SH64PCREL32) */
+  { SH64PCREL32_F,
+    SH64PCREL32_M,
+    SH64PCREL32_LENGTH,
+    C (SH64PCREL16PT_64, SH64PCREL48) },
+  /* C (SH64PCREL16PT_64, SH64PCREL48) */
+  { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16PT_64, SH64PCREL64) },
+  /* C (SH64PCREL16PT_64, SH64PCREL64) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  /* C (SH64PCREL16PT_64, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL64_LENGTH, 0},
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_32, UNDEF_MOVI) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  /* C (MOVI_IMM_32, MOVI_16) */
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32, MOVI_32) },
+  /* C (MOVI_IMM_32, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY,
+  /* C (MOVI_IMM_32, MOVI_GOTOFF) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_32_PCREL, MOVI_16) */
+  EMPTY,
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32_PCREL, MOVI_32) },
+  /* C (MOVI_IMM_32_PCREL, MOVI_32) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (MOVI_IMM_32_PCREL, MOVI_PLT) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_32_PCREL, MOVI_GOTPC) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_64, UNDEF_MOVI) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  /* C (MOVI_IMM_64, MOVI_16) */
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64, MOVI_32) },
+  /* C (MOVI_IMM_64, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64, MOVI_48) },
+  /* C (MOVI_IMM_64, MOVI_48) */
+  { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64, MOVI_64) },
+  /* C (MOVI_IMM_64, MOVI_64) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_64, MOVI_GOTOFF) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_64_PCREL, MOVI_16) */
+  EMPTY,
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_32) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_48) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_48) */
+  { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_64) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_64) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  /* C (MOVI_IMM_64_PCREL, MOVI_PLT) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_64_PCREL, MOVI_GOTPC) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#endif /* HAVE_SH64 */
+
 };
 
 #undef EMPTY
@@ -213,92 +484,321 @@ static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
 
 \f
 #ifdef OBJ_ELF
-/* Parse @got, etc. and return the desired relocation.
-   If we have additional arithmetic expression, then we fill in new_exp_p.  */
-static bfd_reloc_code_real_type
-sh_elf_suffix (str_p, exp_p, new_exp_p)
-     char **str_p;
-     expressionS *exp_p, *new_exp_p;
+/* Determinet whether the symbol needs any kind of PIC relocation.  */
+
+inline static int
+sh_PIC_related_p (symbolS *sym)
 {
-  struct map_bfd {
-    char *string;
-    int length;
-    bfd_reloc_code_real_type reloc;
-  };
+  expressionS *exp;
 
-  char ident[20];
-  char *str = *str_p;
-  char *str2;
-  int ch;
-  int len;
-  struct map_bfd *ptr;
+  if (! sym)
+    return 0;
+
+  if (sym == GOT_symbol)
+    return 1;
+
+#ifdef HAVE_SH64
+  if (sh_PIC_related_p (*symbol_get_tc (sym)))
+    return 1;
+#endif
+
+  exp = symbol_get_value_expression (sym);
+
+  return (exp->X_op == O_PIC_reloc
+         || sh_PIC_related_p (exp->X_add_symbol)
+         || sh_PIC_related_p (exp->X_op_symbol));
+}
+
+/* Determine the relocation type to be used to represent the
+   expression, that may be rearranged.  */
+
+static int
+sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
+{
+  expressionS *exp = main_exp;
 
-#define MAP(str,reloc) { str, sizeof(str)-1, reloc }
+  /* This is here for backward-compatibility only.  GCC used to generated:
 
-  static struct map_bfd mapping[] = {
-    MAP ("got",                BFD_RELOC_32_GOT_PCREL),
-    MAP ("plt",                BFD_RELOC_32_PLT_PCREL),
-    MAP ("gotoff",     BFD_RELOC_32_GOTOFF),
-    { (char *)0,       0,      BFD_RELOC_UNUSED }
-  };
+       f@PLT + . - (.LPCS# + 2)
 
-  if (*str++ != '@')
-    return BFD_RELOC_UNUSED;
+     but we'd rather be able to handle this as a PIC-related reference
+     plus/minus a symbol.  However, gas' parser gives us:
 
-  for (ch = *str, str2 = ident;
-       (str2 < ident + sizeof (ident) - 1
-       && (isalnum (ch) || ch == '@'));
-       ch = *++str)
+       O_subtract (O_add (f@PLT, .), .LPCS#+2)
+
+     so we attempt to transform this into:
+
+        O_subtract (f@PLT, O_subtract (.LPCS#+2, .))
+
+     which we can handle simply below.  */
+  if (exp->X_op == O_subtract)
     {
-      *str2++ = (islower (ch)) ? ch : tolower (ch);
+      if (sh_PIC_related_p (exp->X_op_symbol))
+       return 1;
+
+      exp = symbol_get_value_expression (exp->X_add_symbol);
+
+      if (exp && sh_PIC_related_p (exp->X_op_symbol))
+       return 1;
+
+      if (exp && exp->X_op == O_add
+         && sh_PIC_related_p (exp->X_add_symbol))
+       {
+         symbolS *sym = exp->X_add_symbol;
+
+         exp->X_op = O_subtract;
+         exp->X_add_symbol = main_exp->X_op_symbol;
+
+         main_exp->X_op_symbol = main_exp->X_add_symbol;
+         main_exp->X_add_symbol = sym;
+
+         main_exp->X_add_number += exp->X_add_number;
+         exp->X_add_number = 0;
+       }
+
+      exp = main_exp;
     }
+  else if (exp->X_op == O_add && sh_PIC_related_p (exp->X_op_symbol))
+    return 1;
 
-  *str2 = '\0';
-  len = str2 - ident;
+  if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract)
+    {
+#ifdef HAVE_SH64
+      if (exp->X_add_symbol
+         && (exp->X_add_symbol == GOT_symbol
+             || (GOT_symbol
+                 && *symbol_get_tc (exp->X_add_symbol) == GOT_symbol)))
+       {
+         switch (*r_type_p)
+           {
+           case BFD_RELOC_SH_IMM_LOW16:
+             *r_type_p = BFD_RELOC_SH_GOTPC_LOW16;
+             break;
+
+           case BFD_RELOC_SH_IMM_MEDLOW16:
+             *r_type_p = BFD_RELOC_SH_GOTPC_MEDLOW16;
+             break;
+
+           case BFD_RELOC_SH_IMM_MEDHI16:
+             *r_type_p = BFD_RELOC_SH_GOTPC_MEDHI16;
+             break;
+
+           case BFD_RELOC_SH_IMM_HI16:
+             *r_type_p = BFD_RELOC_SH_GOTPC_HI16;
+             break;
+
+           case BFD_RELOC_NONE:
+           case BFD_RELOC_UNUSED:
+             *r_type_p = BFD_RELOC_SH_GOTPC;
+             break;
+
+           default:
+             abort ();
+           }
+         return 0;
+       }
+#else
+      if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+       {
+         *r_type_p = BFD_RELOC_SH_GOTPC;
+         return 0;
+       }
+#endif
+      exp = symbol_get_value_expression (exp->X_add_symbol);
+      if (! exp)
+       return 0;
+    }
 
-  ch = ident[0];
-  for (ptr = &mapping[0]; ptr->length > 0; ptr++)
-    if (ch == ptr->string[0]
-       && len == ptr->length
-       && memcmp (ident, ptr->string, ptr->length) == 0)
+  if (exp->X_op == O_PIC_reloc)
+    {
+#ifdef HAVE_SH64
+      switch (*r_type_p)
+       {
+       case BFD_RELOC_NONE:
+       case BFD_RELOC_UNUSED:
+         *r_type_p = exp->X_md;
+         break;
+
+       case BFD_RELOC_SH_IMM_LOW16:
+         switch (exp->X_md)
+           {
+           case BFD_RELOC_32_GOTOFF:
+             *r_type_p = BFD_RELOC_SH_GOTOFF_LOW16;
+             break;
+
+           case BFD_RELOC_SH_GOTPLT32:
+             *r_type_p = BFD_RELOC_SH_GOTPLT_LOW16;
+             break;
+
+           case BFD_RELOC_32_GOT_PCREL:
+             *r_type_p = BFD_RELOC_SH_GOT_LOW16;
+             break;
+
+           case BFD_RELOC_32_PLT_PCREL:
+             *r_type_p = BFD_RELOC_SH_PLT_LOW16;
+             break;
+
+           default:
+             abort ();
+           }
+         break;
+
+       case BFD_RELOC_SH_IMM_MEDLOW16:
+         switch (exp->X_md)
+           {
+           case BFD_RELOC_32_GOTOFF:
+             *r_type_p = BFD_RELOC_SH_GOTOFF_MEDLOW16;
+             break;
+
+           case BFD_RELOC_SH_GOTPLT32:
+             *r_type_p = BFD_RELOC_SH_GOTPLT_MEDLOW16;
+             break;
+
+           case BFD_RELOC_32_GOT_PCREL:
+             *r_type_p = BFD_RELOC_SH_GOT_MEDLOW16;
+             break;
+
+           case BFD_RELOC_32_PLT_PCREL:
+             *r_type_p = BFD_RELOC_SH_PLT_MEDLOW16;
+             break;
+
+           default:
+             abort ();
+           }
+         break;
+
+       case BFD_RELOC_SH_IMM_MEDHI16:
+         switch (exp->X_md)
+           {
+           case BFD_RELOC_32_GOTOFF:
+             *r_type_p = BFD_RELOC_SH_GOTOFF_MEDHI16;
+             break;
+
+           case BFD_RELOC_SH_GOTPLT32:
+             *r_type_p = BFD_RELOC_SH_GOTPLT_MEDHI16;
+             break;
+
+           case BFD_RELOC_32_GOT_PCREL:
+             *r_type_p = BFD_RELOC_SH_GOT_MEDHI16;
+             break;
+
+           case BFD_RELOC_32_PLT_PCREL:
+             *r_type_p = BFD_RELOC_SH_PLT_MEDHI16;
+             break;
+
+           default:
+             abort ();
+           }
+         break;
+
+       case BFD_RELOC_SH_IMM_HI16:
+         switch (exp->X_md)
+           {
+           case BFD_RELOC_32_GOTOFF:
+             *r_type_p = BFD_RELOC_SH_GOTOFF_HI16;
+             break;
+
+           case BFD_RELOC_SH_GOTPLT32:
+             *r_type_p = BFD_RELOC_SH_GOTPLT_HI16;
+             break;
+
+           case BFD_RELOC_32_GOT_PCREL:
+             *r_type_p = BFD_RELOC_SH_GOT_HI16;
+             break;
+
+           case BFD_RELOC_32_PLT_PCREL:
+             *r_type_p = BFD_RELOC_SH_PLT_HI16;
+             break;
+
+           default:
+             abort ();
+           }
+         break;
+
+       default:
+         abort ();
+       }
+#else
+      *r_type_p = exp->X_md;
+#endif
+      if (exp == main_exp)
+       exp->X_op = O_symbol;
+      else
+       {
+         main_exp->X_add_symbol = exp->X_add_symbol;
+         main_exp->X_add_number += exp->X_add_number;
+       }
+    }
+  else
+    return (sh_PIC_related_p (exp->X_add_symbol)
+           || sh_PIC_related_p (exp->X_op_symbol));
+
+  return 0;
+}
+
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG.  */
+
+void
+sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
+{
+  bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+
+  if (sh_check_fixup (exp, &r_type))
+    as_bad (_("Invalid PIC expression."));
+
+  if (r_type == BFD_RELOC_UNUSED)
+    switch (size)
       {
-       /* Now check for identifier@suffix+constant */
-       if (*str == '-' || *str == '+')
-         {
-           char *orig_line = input_line_pointer;
-
-           input_line_pointer = str;
-           expression (new_exp_p);
-           if (new_exp_p->X_op == O_constant)
-             {
-               exp_p->X_add_number += new_exp_p->X_add_number;
-               str = input_line_pointer;
-             }
-           if (new_exp_p->X_op == O_subtract)
-             str = input_line_pointer;
-
-           if (&input_line_pointer != str_p)
-             input_line_pointer = orig_line;
-         }
+      case 1:
+       r_type = BFD_RELOC_8;
+       break;
 
-       *str_p = str;
-       return ptr->reloc;
+      case 2:
+       r_type = BFD_RELOC_16;
+       break;
+
+      case 4:
+       r_type = BFD_RELOC_32;
+       break;
+
+#ifdef HAVE_SH64
+      case 8:
+       r_type = BFD_RELOC_64;
+       break;
+#endif
+
+      default:
+       goto error;
       }
+  else if (size != 4)
+    {
+    error:
+      as_bad (_("unsupported BFD relocation size %u"), size);
+      r_type = BFD_RELOC_UNUSED;
+    }
 
-  return BFD_RELOC_UNUSED;
+  fix_new_exp (frag, off, size, exp, 0, r_type);
 }
 
 /* The regular cons() function, that reads constants, doesn't support
    suffixes such as @GOT, @GOTOFF and @PLT, that generate
    machine-specific relocation types.  So we must define it here.  */
 /* Clobbers input_line_pointer, checks end-of-line.  */
+/* NBYTES 1=.byte, 2=.word, 4=.long */
 static void
-sh_elf_cons (nbytes)
-     register int nbytes;      /* 1=.byte, 2=.word, 4=.long */
+sh_elf_cons (register int nbytes)
 {
-  expressionS exp, new_exp;
-  bfd_reloc_code_real_type reloc;
-  const char *name;
+  expressionS exp;
+
+#ifdef HAVE_SH64
+
+  /* Update existing range to include a previous insn, if there was one.  */
+  sh64_update_contents_mark (TRUE);
+
+  /* We need to make sure the contents type is set to data.  */
+  sh64_flag_output ();
+
+#endif /* HAVE_SH64 */
 
   if (is_it_end_of_statement ())
     {
@@ -306,89 +806,21 @@ sh_elf_cons (nbytes)
       return;
     }
 
+#ifdef md_cons_align
+  md_cons_align (nbytes);
+#endif
+
   do
     {
       expression (&exp);
-      new_exp.X_op = O_absent;
-      new_exp.X_add_symbol = new_exp.X_op_symbol = NULL;
-      /* If the _GLOBAL_OFFSET_TABLE_ symbol hasn't been found yet,
-        use the name of the symbol to tell whether it's the
-        _GLOBAL_OFFSET_TABLE_.  If it has, comparing the symbols is
-        sufficient.  */
-      if (! GOT_symbol && exp.X_add_symbol)
-       name = S_GET_NAME (exp.X_add_symbol);
-      else
-       name = NULL;
-      /* Check whether this expression involves the
-        _GLOBAL_OFFSET_TABLE_ symbol, by itself or added to a
-        difference of two other symbols.  */
-      if (((GOT_symbol && GOT_symbol == exp.X_add_symbol)
-          || (! GOT_symbol && name
-              && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
-         && (exp.X_op == O_symbol
-             || (exp.X_op == O_add
-                 && ((symbol_get_value_expression (exp.X_op_symbol)->X_op)
-                     == O_subtract))))
-       {
-         reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
-                                                                BFD_RELOC_32);
-         int size = bfd_get_reloc_size (reloc_howto);
-
-         if (GOT_symbol == NULL)
-           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
-
-         if (size > nbytes)
-           as_bad (_("%s relocations do not fit in %d bytes\n"),
-                   reloc_howto->name, nbytes);
-         else
-           {
-             register char *p = frag_more ((int) nbytes);
-             int offset = nbytes - size;
-
-             fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
-                          size, &exp, 0, TC_RELOC_GLOBAL_OFFSET_TABLE);
-           }
-       }
-      /* Check if this symbol involves one of the magic suffixes, such
-        as @GOT, @GOTOFF or @PLT, and determine which relocation type
-        to use.  */
-      else if ((exp.X_op == O_symbol || (exp.X_op == O_add && exp.X_op_symbol))
-         && *input_line_pointer == '@'
-         && ((reloc = sh_elf_suffix (&input_line_pointer, &exp, &new_exp))
-             != BFD_RELOC_UNUSED))
-       {
-         reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
-                                                                reloc);
-         int size = bfd_get_reloc_size (reloc_howto);
-
-         /* Force a GOT to be generated.  */
-         if (GOT_symbol == NULL)
-           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
-
-         if (size > nbytes)
-           as_bad (_("%s relocations do not fit in %d bytes\n"),
-                   reloc_howto->name, nbytes);
-         else
-           {
-             register char *p = frag_more ((int) nbytes);
-             int offset = nbytes - size;
-
-             fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
-                          &exp, 0, reloc);
-             if (new_exp.X_op != O_absent)
-               fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
-                            &new_exp, 0, BFD_RELOC_32);
-           }
-       }
-      else
-       emit_expr (&exp, (unsigned int) nbytes);
+      emit_expr (&exp, (unsigned int) nbytes);
     }
   while (*input_line_pointer++ == ',');
 
   input_line_pointer--;                /* Put terminator back into stream.  */
   if (*input_line_pointer == '#' || *input_line_pointer == '!')
     {
-       while (! is_end_of_line[*input_line_pointer++]);
+       while (! is_end_of_line[(unsigned char) *input_line_pointer++]);
     }
   else
     demand_empty_rest_of_line ();
@@ -400,41 +832,32 @@ sh_elf_cons (nbytes)
    set up all the tables, etc that the MD part of the assembler needs.  */
 
 void
-md_begin ()
+md_begin (void)
 {
-  sh_opcode_info *opcode;
+  const sh_opcode_info *opcode;
   char *prev_name = "";
-  int target_arch;
-
-#ifdef TE_PE
-  /* The WinCE OS only supports little endian executables.  */
-  target_big_endian = 0;
-#else
-  if (! shl)
-    target_big_endian = 1;
-#endif
+  unsigned int target_arch;
 
-  target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
+  target_arch
+    = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_has_dsp;
   valid_arch = target_arch;
 
+#ifdef HAVE_SH64
+  shmedia_md_begin ();
+#endif
+
   opcode_hash_control = hash_new ();
 
   /* Insert unique names into hash table.  */
   for (opcode = sh_table; opcode->name; opcode++)
     {
-      if (strcmp (prev_name, opcode->name))
+      if (strcmp (prev_name, opcode->name) != 0)
        {
-         if (! (opcode->arch & target_arch))
+         if (!SH_MERGE_ARCH_SET_VALID (opcode->arch, target_arch))
            continue;
          prev_name = opcode->name;
          hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
        }
-      else
-       {
-         /* Make all the opcodes with the same name point to the same
-            string.  */
-         opcode->name = prev_name;
-       }
     }
 }
 
@@ -444,31 +867,23 @@ static int reg_x, reg_y;
 static int reg_efg;
 static int reg_b;
 
-typedef struct
-  {
-    sh_arg_type type;
-    int reg;
-    expressionS immediate;
-  }
-sh_operand_info;
-
-#define IDENT_CHAR(c) (isalnum (c) || (c) == '_')
+#define IDENT_CHAR(c) (ISALNUM (c) || (c) == '_')
 
 /* Try to parse a reg name.  Return the number of chars consumed.  */
 
 static int
-parse_reg (src, mode, reg)
-     char *src;
-     int *mode;
-     int *reg;
+parse_reg (char *src, int *mode, int *reg)
 {
+  char l0 = TOLOWER (src[0]);
+  char l1 = l0 ? TOLOWER (src[1]) : 0;
+
   /* We use ! IDENT_CHAR for the next character after the register name, to
      make sure that we won't accidentally recognize a symbol name such as
      'sram' or sr_ram as being a reference to the register 'sr'.  */
 
-  if (src[0] == 'r')
+  if (l0 == 'r')
     {
-      if (src[1] == '1')
+      if (l1 == '1')
        {
          if (src[2] >= '0' && src[2] <= '5'
              && ! IDENT_CHAR ((unsigned char) src[3]))
@@ -478,36 +893,36 @@ parse_reg (src, mode, reg)
              return 3;
            }
        }
-      if (src[1] >= '0' && src[1] <= '9'
+      if (l1 >= '0' && l1 <= '9'
          && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_REG_N;
-         *reg = (src[1] - '0');
+         *reg = (l1 - '0');
          return 2;
        }
-      if (src[1] >= '0' && src[1] <= '7' && strncmp (&src[2], "_bank", 5) == 0
+      if (l1 >= '0' && l1 <= '7' && strncasecmp (&src[2], "_bank", 5) == 0
          && ! IDENT_CHAR ((unsigned char) src[7]))
        {
          *mode = A_REG_B;
-         *reg  = (src[1] - '0');
+         *reg  = (l1 - '0');
          return 7;
        }
 
-      if (src[1] == 'e' && ! IDENT_CHAR ((unsigned char) src[2]))
+      if (l1 == 'e' && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_RE;
          return 2;
        }
-      if (src[1] == 's' && ! IDENT_CHAR ((unsigned char) src[2]))
+      if (l1 == 's' && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_RS;
          return 2;
        }
     }
 
-  if (src[0] == 'a')
+  if (l0 == 'a')
     {
-      if (src[1] == '0')
+      if (l1 == '0')
        {
          if (! IDENT_CHAR ((unsigned char) src[2]))
            {
@@ -515,14 +930,14 @@ parse_reg (src, mode, reg)
              *reg = A_A0_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
+         if (TOLOWER (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = DSP_REG_N;
              *reg = A_A0G_NUM;
              return 3;
            }
        }
-      if (src[1] == '1')
+      if (l1 == '1')
        {
          if (! IDENT_CHAR ((unsigned char) src[2]))
            {
@@ -530,7 +945,7 @@ parse_reg (src, mode, reg)
              *reg = A_A1_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
+         if (TOLOWER (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = DSP_REG_N;
              *reg = A_A1G_NUM;
@@ -538,24 +953,24 @@ parse_reg (src, mode, reg)
            }
        }
 
-      if (src[1] == 'x' && src[2] >= '0' && src[2] <= '1'
+      if (l1 == 'x' && src[2] >= '0' && src[2] <= '1'
          && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = A_REG_N;
-         *reg = 4 + (src[1] - '0');
+         *reg = 4 + (l1 - '0');
          return 3;
        }
-      if (src[1] == 'y' && src[2] >= '0' && src[2] <= '1'
+      if (l1 == 'y' && src[2] >= '0' && src[2] <= '1'
          && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = A_REG_N;
-         *reg = 6 + (src[1] - '0');
+         *reg = 6 + (l1 - '0');
          return 3;
        }
-      if (src[1] == 's' && src[2] >= '0' && src[2] <= '3'
+      if (l1 == 's' && src[2] >= '0' && src[2] <= '3'
          && ! IDENT_CHAR ((unsigned char) src[3]))
        {
-         int n = src[1] - '0';
+         int n = l1 - '0';
 
          *mode = A_REG_N;
          *reg = n | ((~n & 2) << 1);
@@ -563,21 +978,21 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'i' && src[1] && ! IDENT_CHAR ((unsigned char) src[3]))
+  if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[2]))
     {
-      if (src[1] == 's')
+      if (l1 == 's')
        {
          *mode = A_REG_N;
          *reg = 8;
          return 2;
        }
-      if (src[1] == 'x')
+      if (l1 == 'x')
        {
          *mode = A_REG_N;
          *reg = 8;
          return 2;
        }
-      if (src[1] == 'y')
+      if (l1 == 'y')
        {
          *mode = A_REG_N;
          *reg = 9;
@@ -585,125 +1000,125 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'x' && src[1] >= '0' && src[1] <= '1'
+  if (l0 == 'x' && l1 >= '0' && l1 <= '1'
       && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = A_X0_NUM + src[1] - '0';
+      *reg = A_X0_NUM + l1 - '0';
       return 2;
     }
 
-  if (src[0] == 'y' && src[1] >= '0' && src[1] <= '1'
+  if (l0 == 'y' && l1 >= '0' && l1 <= '1'
       && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = A_Y0_NUM + src[1] - '0';
+      *reg = A_Y0_NUM + l1 - '0';
       return 2;
     }
 
-  if (src[0] == 'm' && src[1] >= '0' && src[1] <= '1'
+  if (l0 == 'm' && l1 >= '0' && l1 <= '1'
       && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = src[1] == '0' ? A_M0_NUM : A_M1_NUM;
+      *reg = l1 == '0' ? A_M0_NUM : A_M1_NUM;
       return 2;
     }
 
-  if (src[0] == 's'
-      && src[1] == 's'
-      && src[2] == 'r' && ! IDENT_CHAR ((unsigned char) src[3]))
+  if (l0 == 's'
+      && l1 == 's'
+      && TOLOWER (src[2]) == 'r' && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SSR;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'p' && src[2] == 'c'
+  if (l0 == 's' && l1 == 'p' && TOLOWER (src[2]) == 'c'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SPC;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'g' && src[2] == 'r'
+  if (l0 == 's' && l1 == 'g' && TOLOWER (src[2]) == 'r'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SGR;
       return 3;
     }
 
-  if (src[0] == 'd' && src[1] == 's' && src[2] == 'r'
+  if (l0 == 'd' && l1 == 's' && TOLOWER (src[2]) == 'r'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_DSR;
       return 3;
     }
 
-  if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r'
+  if (l0 == 'd' && l1 == 'b' && TOLOWER (src[2]) == 'r'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_DBR;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
+  if (l0 == 's' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_SR;
       return 2;
     }
 
-  if (src[0] == 's' && src[1] == 'p' && ! IDENT_CHAR ((unsigned char) src[2]))
+  if (l0 == 's' && l1 == 'p' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_REG_N;
       *reg = 15;
       return 2;
     }
 
-  if (src[0] == 'p' && src[1] == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
+  if (l0 == 'p' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_PR;
       return 2;
     }
-  if (src[0] == 'p' && src[1] == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
+  if (l0 == 'p' && l1 == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       /* Don't use A_DISP_PC here - that would accept stuff like 'mova pc,r0'
          and use an uninitialized immediate.  */
       *mode = A_PC;
       return 2;
     }
-  if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r'
+  if (l0 == 'g' && l1 == 'b' && TOLOWER (src[2]) == 'r'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_GBR;
       return 3;
     }
-  if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r'
+  if (l0 == 'v' && l1 == 'b' && TOLOWER (src[2]) == 'r'
       && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_VBR;
       return 3;
     }
 
-  if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c'
+  if (l0 == 'm' && l1 == 'a' && TOLOWER (src[2]) == 'c'
       && ! IDENT_CHAR ((unsigned char) src[4]))
     {
-      if (src[3] == 'l')
+      if (TOLOWER (src[3]) == 'l')
        {
          *mode = A_MACL;
          return 4;
        }
-      if (src[3] == 'h')
+      if (TOLOWER (src[3]) == 'h')
        {
          *mode = A_MACH;
          return 4;
        }
     }
-  if (src[0] == 'm' && src[1] == 'o' && src[2] == 'd'
-      && ! IDENT_CHAR ((unsigned char) src[4]))
+  if (l0 == 'm' && l1 == 'o' && TOLOWER (src[2]) == 'd'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_MOD;
       return 3;
     }
-  if (src[0] == 'f' && src[1] == 'r')
+  if (l0 == 'f' && l1 == 'r')
     {
       if (src[2] == '1')
        {
@@ -723,7 +1138,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'd' && src[1] == 'r')
+  if (l0 == 'd' && l1 == 'r')
     {
       if (src[2] == '1')
        {
@@ -743,7 +1158,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'x' && src[1] == 'd')
+  if (l0 == 'x' && l1 == 'd')
     {
       if (src[2] == '1')
        {
@@ -763,7 +1178,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'v')
+  if (l0 == 'f' && l1 == 'v')
     {
       if (src[2] == '1'&& src[3] == '2' && ! IDENT_CHAR ((unsigned char) src[4]))
        {
@@ -779,22 +1194,25 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
+  if (l0 == 'f' && l1 == 'p' && TOLOWER (src[2]) == 'u'
+      && TOLOWER (src[3]) == 'l'
       && ! IDENT_CHAR ((unsigned char) src[4]))
     {
       *mode = FPUL_N;
       return 4;
     }
 
-  if (src[0] == 'f' && src[1] == 'p' && src[2] == 's' && src[3] == 'c'
-      && src[4] == 'r' && ! IDENT_CHAR ((unsigned char) src[5]))
+  if (l0 == 'f' && l1 == 'p' && TOLOWER (src[2]) == 's'
+      && TOLOWER (src[3]) == 'c'
+      && TOLOWER (src[4]) == 'r' && ! IDENT_CHAR ((unsigned char) src[5]))
     {
       *mode = FPSCR_N;
       return 5;
     }
 
-  if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r'
-      && src[4] == 'x' && ! IDENT_CHAR ((unsigned char) src[5]))
+  if (l0 == 'x' && l1 == 'm' && TOLOWER (src[2]) == 't'
+      && TOLOWER (src[3]) == 'r'
+      && TOLOWER (src[4]) == 'x' && ! IDENT_CHAR ((unsigned char) src[5]))
     {
       *mode = XMTRX_M4;
       return 5;
@@ -803,24 +1221,8 @@ parse_reg (src, mode, reg)
   return 0;
 }
 
-static symbolS *
-dot ()
-{
-  const char *fake;
-
-  /* JF: '.' is pseudo symbol with value of current location
-     in current segment.  */
-  fake = FAKE_LABEL_NAME;
-  return  symbol_new (fake,
-                     now_seg,
-                     (valueT) frag_now_fix (),
-                     frag_now);
-}
-
 static char *
-parse_exp (s, op)
-     char *s;
-     sh_operand_info *op;
+parse_exp (char *s, sh_operand_info *op)
 {
   char *save;
   char *new;
@@ -830,6 +1232,12 @@ parse_exp (s, op)
   expression (&op->immediate);
   if (op->immediate.X_op == O_absent)
     as_bad (_("missing operand"));
+#ifdef OBJ_ELF
+  else if (op->immediate.X_op == O_PIC_reloc
+          || sh_PIC_related_p (op->immediate.X_add_symbol)
+          || sh_PIC_related_p (op->immediate.X_op_symbol))
+    as_bad (_("misplaced PIC operand"));
+#endif
   new = input_line_pointer;
   input_line_pointer = save;
   return new;
@@ -855,9 +1263,7 @@ parse_exp (s, op)
  */
 
 static char *
-parse_at (src, op)
-     char *src;
-     sh_operand_info *op;
+parse_at (char *src, sh_operand_info *op)
 {
   int len;
   int mode;
@@ -877,7 +1283,7 @@ parse_at (src, op)
   else if (src[0] == '(')
     {
       /* Could be @(disp, rn), @(disp, gbr), @(disp, pc),  @(r0, gbr) or
-         @(r0, rn) */
+         @(r0, rn) */
       src++;
       len = parse_reg (src, &mode, &(op->reg));
       if (len && mode == A_REG_N)
@@ -888,29 +1294,42 @@ parse_at (src, op)
              as_bad (_("must be @(r0,...)"));
            }
          if (src[0] == ',')
-           src++;
-         /* Now can be rn or gbr */
-         len = parse_reg (src, &mode, &(op->reg));
-         if (mode == A_GBR)
            {
-             op->type = A_R0_GBR;
+             src++;
+             /* Now can be rn or gbr.  */
+             len = parse_reg (src, &mode, &(op->reg));
            }
-         else if (mode == A_REG_N)
+         else
            {
-             op->type = A_IND_R0_REG_N;
+             len = 0;
+           }
+         if (len)
+           {
+             if (mode == A_GBR)
+               {
+                 op->type = A_R0_GBR;
+               }
+             else if (mode == A_REG_N)
+               {
+                 op->type = A_IND_R0_REG_N;
+               }
+             else
+               {
+                 as_bad (_("syntax error in @(r0,...)"));
+               }
            }
          else
            {
-             as_bad (_("syntax error in @(r0,...)"));
+             as_bad (_("syntax error in @(r0...)"));
            }
        }
       else
        {
-         /* Must be an @(disp,.. thing) */
+         /* Must be an @(disp,.. thing) */
          src = parse_exp (src, op);
          if (src[0] == ',')
            src++;
-         /* Now can be rn, gbr or pc */
+         /* Now can be rn, gbr or pc */
          len = parse_reg (src, &mode, &op->reg);
          if (len)
            {
@@ -924,13 +1343,33 @@ parse_at (src, op)
                }
              else if (mode == A_PC)
                {
-                 /* Turn a plain @(4,pc) into @(.+4,pc).  */
-                 if (op->immediate.X_op == O_constant)
+                 /* We want @(expr, pc) to uniformly address . + expr,
+                    no matter if expr is a constant, or a more complex
+                    expression, e.g. sym-. or sym1-sym2.
+                    However, we also used to accept @(sym,pc)
+                    as addressing sym, i.e. meaning the same as plain sym.
+                    Some existing code does use the @(sym,pc) syntax, so
+                    we give it the old semantics for now, but warn about
+                    its use, so that users have some time to fix their code.
+
+                    Note that due to this backward compatibility hack,
+                    we'll get unexpected results when @(offset, pc) is used,
+                    and offset is a symbol that is set later to an an address
+                    difference, or an external symbol that is set to an
+                    address difference in another source file, so we want to
+                    eventually remove it.  */
+                 if (op->immediate.X_op == O_symbol)
                    {
-                     op->immediate.X_add_symbol = dot();
-                     op->immediate.X_op = O_symbol;
+                     op->type = A_DISP_PC;
+                     as_warn (_("Deprecated syntax."));
+                   }
+                 else
+                   {
+                     op->type = A_DISP_PC_ABS;
+                     /* Such operands don't get corrected for PC==.+4, so
+                        make the correction here.  */
+                     op->immediate.X_add_number -= 4;
                    }
-                 op->type = A_DISP_PC;
                }
              else
                {
@@ -952,39 +1391,39 @@ parse_at (src, op)
     {
       src += parse_reg (src, &mode, &(op->reg));
       if (mode != A_REG_N)
-       {
-         as_bad (_("illegal register after @"));
-       }
+       as_bad (_("illegal register after @"));
+
       if (src[0] == '+')
        {
+         char l0, l1;
+
          src++;
-         if ((src[0] == 'r' && src[1] == '8')
-             || (src[0] == 'i' && (src[1] == 'x' || src[1] == 's')))
+         l0 = TOLOWER (src[0]);
+         l1 = TOLOWER (src[1]);
+
+         if ((l0 == 'r' && l1 == '8')
+             || (l0 == 'i' && (l1 == 'x' || l1 == 's')))
            {
              src += 2;
-             op->type = A_PMOD_N;
+             op->type = AX_PMOD_N;
            }
-         if ((src[0] == 'r' && src[1] == '9')
-             || (src[0] == 'i' && src[1] == 'y'))
+         else if (   (l0 == 'r' && l1 == '9')
+                  || (l0 == 'i' && l1 == 'y'))
            {
              src += 2;
-             op->type = A_PMODY_N;
+             op->type = AY_PMOD_N;
            }
          else
            op->type = A_INC_N;
        }
       else
-       {
-         op->type = A_IND_N;
-       }
+       op->type = A_IND_N;
     }
   return src;
 }
 
 static void
-get_operand (ptr, op)
-     char **ptr;
-     sh_operand_info *op;
+get_operand (char **ptr, sh_operand_info *op)
 {
   char *src = *ptr;
   int mode = -1;
@@ -1020,10 +1459,7 @@ get_operand (ptr, op)
 }
 
 static char *
-get_operands (info, args, operand)
-     sh_opcode_info *info;
-     char *args;
-     sh_operand_info *operand;
+get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
 {
   char *ptr = args;
   if (info->arg[0])
@@ -1081,9 +1517,7 @@ get_operands (info, args, operand)
    provided.  */
 
 static sh_opcode_info *
-get_specific (opcode, operands)
-     sh_opcode_info *opcode;
-     sh_operand_info *operands;
+get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
 {
   sh_opcode_info *this_try = opcode;
   char *name = opcode->name;
@@ -1092,7 +1526,7 @@ get_specific (opcode, operands)
   while (opcode->name)
     {
       this_try = opcode++;
-      if (this_try->name != name)
+      if ((this_try->name != name) && (strcmp (this_try->name, name) != 0))
        {
          /* We've looked so far down the table that we've run out of
             opcodes with the same name.  */
@@ -1110,11 +1544,14 @@ get_specific (opcode, operands)
 
          switch (arg)
            {
+           case A_DISP_PC:
+             if (user->type == A_DISP_PC_ABS)
+               break;
+             /* Fall through.  */
            case A_IMM:
            case A_BDISP12:
            case A_BDISP8:
            case A_DISP_GBR:
-           case A_DISP_PC:
            case A_MACH:
            case A_PR:
            case A_MACL:
@@ -1147,8 +1584,6 @@ get_specific (opcode, operands)
            case V_REG_N:
            case FPUL_N:
            case FPSCR_N:
-           case A_PMOD_N:
-           case A_PMODY_N:
            case DSP_REG_N:
              /* Opcode needs rn */
              if (user->type != arg)
@@ -1194,6 +1629,237 @@ get_specific (opcode, operands)
              reg_m = user->reg;
              break;
 
+           case AS_DEC_N:
+             if (user->type != A_DEC_N)
+               goto fail;
+             if (user->reg < 2 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AS_INC_N:
+             if (user->type != A_INC_N)
+               goto fail;
+             if (user->reg < 2 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AS_IND_N:
+             if (user->type != A_IND_N)
+               goto fail;
+             if (user->reg < 2 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AS_PMOD_N:
+             if (user->type != AX_PMOD_N)
+               goto fail;
+             if (user->reg < 2 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AX_INC_N:
+             if (user->type != A_INC_N)
+               goto fail;
+             if (user->reg < 4 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AX_IND_N:
+             if (user->type != A_IND_N)
+               goto fail;
+             if (user->reg < 4 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AX_PMOD_N:
+             if (user->type != AX_PMOD_N)
+               goto fail;
+             if (user->reg < 4 || user->reg > 5)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AXY_INC_N:
+             if (user->type != A_INC_N)
+               goto fail;
+             if ((user->reg < 4 || user->reg > 5)
+                 && (user->reg < 0 || user->reg > 1))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AXY_IND_N:
+             if (user->type != A_IND_N)
+               goto fail;
+             if ((user->reg < 4 || user->reg > 5)
+                 && (user->reg < 0 || user->reg > 1))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AXY_PMOD_N:
+             if (user->type != AX_PMOD_N)
+               goto fail;
+             if ((user->reg < 4 || user->reg > 5)
+                 && (user->reg < 0 || user->reg > 1))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AY_INC_N:
+             if (user->type != A_INC_N)
+               goto fail;
+             if (user->reg < 6 || user->reg > 7)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AY_IND_N:
+             if (user->type != A_IND_N)
+               goto fail;
+             if (user->reg < 6 || user->reg > 7)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AY_PMOD_N:
+             if (user->type != AY_PMOD_N)
+               goto fail;
+             if (user->reg < 6 || user->reg > 7)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AYX_INC_N:
+             if (user->type != A_INC_N)
+               goto fail;
+             if ((user->reg < 6 || user->reg > 7)
+                 && (user->reg < 2 || user->reg > 3))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AYX_IND_N:
+             if (user->type != A_IND_N)
+               goto fail;
+             if ((user->reg < 6 || user->reg > 7)
+                 && (user->reg < 2 || user->reg > 3))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case AYX_PMOD_N:
+             if (user->type != AY_PMOD_N)
+               goto fail;
+             if ((user->reg < 6 || user->reg > 7)
+                 && (user->reg < 2 || user->reg > 3))
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case DSP_REG_A_M:
+             if (user->type != DSP_REG_N)
+               goto fail;
+             if (user->reg != A_A0_NUM
+                 && user->reg != A_A1_NUM)
+               goto fail;
+             reg_m = user->reg;
+             break;
+
+           case DSP_REG_AX:
+             if (user->type != DSP_REG_N)
+               goto fail;
+             switch (user->reg)
+               {
+               case A_A0_NUM:
+                 reg_x = 0;
+                 break;
+               case A_A1_NUM:
+                 reg_x = 2;
+                 break;
+               case A_X0_NUM:
+                 reg_x = 1;
+                 break;
+               case A_X1_NUM:
+                 reg_x = 3;
+                 break;
+               default:
+                 goto fail;
+               }
+             break;
+
+           case DSP_REG_XY:
+             if (user->type != DSP_REG_N)
+               goto fail;
+             switch (user->reg)
+               {
+               case A_X0_NUM:
+                 reg_x = 0;
+                 break;
+               case A_X1_NUM:
+                 reg_x = 2;
+                 break;
+               case A_Y0_NUM:
+                 reg_x = 1;
+                 break;
+               case A_Y1_NUM:
+                 reg_x = 3;
+                 break;
+               default:
+                 goto fail;
+               }
+             break;
+
+           case DSP_REG_AY:
+             if (user->type != DSP_REG_N)
+               goto fail;
+             switch (user->reg)
+               {
+               case A_A0_NUM:
+                 reg_y = 0;
+                 break;
+               case A_A1_NUM:
+                 reg_y = 1;
+                 break;
+               case A_Y0_NUM:
+                 reg_y = 2;
+                 break;
+               case A_Y1_NUM:
+                 reg_y = 3;
+                 break;
+               default:
+                 goto fail;
+               }
+             break;
+
+           case DSP_REG_YX:
+             if (user->type != DSP_REG_N)
+               goto fail;
+             switch (user->reg)
+               {
+               case A_Y0_NUM:
+                 reg_y = 0;
+                 break;
+               case A_Y1_NUM:
+                 reg_y = 1;
+                 break;
+               case A_X0_NUM:
+                 reg_y = 2;
+                 break;
+               case A_X1_NUM:
+                 reg_y = 3;
+                 break;
+               default:
+                 goto fail;
+               }
+             break;
+
            case DSP_REG_X:
              if (user->type != DSP_REG_N)
                goto fail;
@@ -1352,9 +2018,9 @@ get_specific (opcode, operands)
              goto fail;
            }
        }
-      if ( !(valid_arch & this_try->arch))
+      if ( !SH_MERGE_ARCH_SET_VALID (valid_arch, this_try->arch))
        goto fail;
-      valid_arch &= this_try->arch;
+      valid_arch = SH_MERGE_ARCH_SET (valid_arch, this_try->arch);
       return this_try;
     fail:
       ;
@@ -1363,27 +2029,8 @@ get_specific (opcode, operands)
   return 0;
 }
 
-int
-check (operand, low, high)
-     expressionS *operand;
-     int low;
-     int high;
-{
-  if (operand->X_op != O_constant
-      || operand->X_add_number < low
-      || operand->X_add_number > high)
-    {
-      as_bad (_("operand must be absolute in range %d..%d"), low, high);
-    }
-  return operand->X_add_number;
-}
-
 static void
-insert (where, how, pcrel, op)
-     char *where;
-     int how;
-     int pcrel;
-     sh_operand_info *op;
+insert (char *where, int how, int pcrel, sh_operand_info *op)
 {
   fix_new_exp (frag_now,
               where - frag_now->fr_literal,
@@ -1394,9 +2041,7 @@ insert (where, how, pcrel, op)
 }
 
 static void
-build_relax (opcode, op)
-     sh_opcode_info *opcode;
-     sh_operand_info *op;
+build_relax (sh_opcode_info *opcode, sh_operand_info *op)
 {
   int high_byte = target_big_endian ? 0 : 1;
   char *p;
@@ -1430,9 +2075,7 @@ build_relax (opcode, op)
 /* Insert ldrs & ldre with fancy relocations that relaxation can recognize.  */
 
 static char *
-insert_loop_bounds (output, operand)
-     char *output;
-     sh_operand_info *operand;
+insert_loop_bounds (char *output, sh_operand_info *operand)
 {
   char *name;
   symbolS *end_sym;
@@ -1480,10 +2123,7 @@ insert_loop_bounds (output, operand)
 /* Now we know what sort of opcodes it is, let's build the bytes.  */
 
 static unsigned int
-build_Mytes (opcode, operand)
-     sh_opcode_info *opcode;
-     sh_operand_info *operand;
-
+build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
 {
   int index;
   char nbuf[4];
@@ -1507,6 +2147,7 @@ build_Mytes (opcode, operand)
          switch (i)
            {
            case REG_N:
+           case REG_N_D:
              nbuf[index] = reg_n;
              break;
            case REG_M:
@@ -1523,6 +2164,9 @@ build_Mytes (opcode, operand)
            case REG_B:
              nbuf[index] = reg_b | 0x08;
              break;
+           case REG_N_B01:
+             nbuf[index] = reg_n | 0x01;
+             break;
            case IMM0_4BY4:
              insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
              break;
@@ -1560,10 +2204,12 @@ build_Mytes (opcode, operand)
              insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1);
              break;
            case PCRELIMM_8BY4:
-             insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY4,
+                     operand->type != A_DISP_PC_ABS, operand);
              break;
            case PCRELIMM_8BY2:
-             insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY2,
+                     operand->type != A_DISP_PC_ABS, operand);
              break;
            case REPEAT:
              output = insert_loop_bounds (output, operand);
@@ -1592,8 +2238,7 @@ build_Mytes (opcode, operand)
    *STR_P to the first character after the last one read.  */
 
 static sh_opcode_info *
-find_cooked_opcode (str_p)
-     char **str_p;
+find_cooked_opcode (char **str_p)
 {
   char *str = *str_p;
   unsigned char *op_start;
@@ -1620,9 +2265,9 @@ find_cooked_opcode (str_p)
       /* The machine independent code will convert CMP/EQ into cmp/EQ
         because it thinks the '/' is the end of the symbol.  Moreover,
         all but the first sub-insn is a parallel processing insn won't
-        be capitailzed.  Instead of hacking up the machine independent
+        be capitalized.  Instead of hacking up the machine independent
         code, we just deal with it here.  */
-      c = isupper (c) ? tolower (c) : c;
+      c = TOLOWER (c);
       name[nlen] = c;
       nlen++;
     }
@@ -1640,9 +2285,7 @@ find_cooked_opcode (str_p)
 #define DDT_BASE 0xf000 /* Base value for double data transfer insns */
 
 static unsigned int
-assemble_ppi (op_end, opcode)
-     char *op_end;
-     sh_opcode_info *opcode;
+assemble_ppi (char *op_end, sh_opcode_info *opcode)
 {
   int movx = 0;
   int movy = 0;
@@ -1652,17 +2295,19 @@ assemble_ppi (op_end, opcode)
   int move_code;
   unsigned int size;
 
-  /* Some insn ignore one or more register fields, e.g. psts machl,a0.
-     Make sure we encode a defined insn pattern.  */
-  reg_x = 0;
-  reg_y = 0;
-
   for (;;)
     {
       sh_operand_info operand[3];
 
+      /* Some insn ignore one or more register fields, e.g. psts machl,a0.
+        Make sure we encode a defined insn pattern.  */
+      reg_x = 0;
+      reg_y = 0;
+      reg_n = 0;
+
       if (opcode->arg[0] != A_END)
        op_end = get_operands (opcode, op_end, operand);
+    try_another_opcode:
       opcode = get_specific (opcode, operand);
       if (opcode == 0)
        {
@@ -1693,9 +2338,43 @@ assemble_ppi (op_end, opcode)
          movy = DDT_BASE;
          break;
 
+       case MOVX_NOPY:
+         if (movx)
+           as_bad (_("multiple movx specifications"));
+         if ((reg_n < 4 || reg_n > 5)
+             && (reg_n < 0 || reg_n > 1))
+           as_bad (_("invalid movx address register"));
+         if (movy && movy != DDT_BASE)
+           as_bad (_("insn cannot be combined with non-nopy"));
+         movx = ((((reg_n & 1) != 0) << 9)
+                 + (((reg_n & 4) == 0) << 8)
+                 + (reg_x << 6)
+                 + (opcode->nibbles[2] << 4)
+                 + opcode->nibbles[3]
+                 + DDT_BASE);
+         break;
+
+       case MOVY_NOPX:
+         if (movy)
+           as_bad (_("multiple movy specifications"));
+         if ((reg_n < 6 || reg_n > 7)
+             && (reg_n < 2 || reg_n > 3))
+           as_bad (_("invalid movy address register"));
+         if (movx && movx != DDT_BASE)
+           as_bad (_("insn cannot be combined with non-nopx"));
+         movy = ((((reg_n & 1) != 0) << 8)
+                 + (((reg_n & 4) == 0) << 9)
+                 + (reg_y << 6)
+                 + (opcode->nibbles[2] << 4)
+                 + opcode->nibbles[3]
+                 + DDT_BASE);
+         break;
+
        case MOVX:
          if (movx)
            as_bad (_("multiple movx specifications"));
+         if (movy & 0x2ac)
+           as_bad (_("previous movy requires nopx"));
          if (reg_n < 4 || reg_n > 5)
            as_bad (_("invalid movx address register"));
          if (opcode->nibbles[2] & 8)
@@ -1717,6 +2396,8 @@ assemble_ppi (op_end, opcode)
        case MOVY:
          if (movy)
            as_bad (_("multiple movy specifications"));
+         if (movx & 0x153)
+           as_bad (_("previous movx requires nopy"));
          if (opcode->nibbles[2] & 8)
            {
              /* Bit 3 in nibbles[2] is intended for bit 4 of the opcode,
@@ -1745,11 +2426,31 @@ assemble_ppi (op_end, opcode)
                     | (operand[0].immediate.X_add_number & 127) << 4
                     | reg_n);
          break;
+       case PPI3NC:
+         if (cond)
+           {
+             opcode++;
+             goto try_another_opcode;
+           }
+         /* Fall through.  */
        case PPI3:
          if (field_b)
            as_bad (_("multiple parallel processing specifications"));
          field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
                     + (reg_x << 6) + (reg_y << 4) + reg_n);
+         switch (opcode->nibbles[4])
+           {
+           case HEX_0:
+           case HEX_XX00:
+           case HEX_00YY:
+             break;
+           case HEX_1:
+           case HEX_4:
+             field_b += opcode->nibbles[4] << 4;
+             break;
+           default:
+             abort ();
+           }
          break;
        case PDC:
          if (cond)
@@ -1764,13 +2465,34 @@ assemble_ppi (op_end, opcode)
          field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
                     + cond + (reg_x << 6) + (reg_y << 4) + reg_n);
          cond = 0;
+         switch (opcode->nibbles[4])
+           {
+           case HEX_0:
+           case HEX_XX00:
+           case HEX_00YY:
+             break;
+           case HEX_1:
+           case HEX_4:
+             field_b += opcode->nibbles[4] << 4;
+             break;
+           default:
+             abort ();
+           }
          break;
        case PMUL:
          if (field_b)
            {
-             if ((field_b & 0xef00) != 0xa100)
+             if ((field_b & 0xef00) == 0xa100)
+               field_b -= 0x8100;
+             /* pclr Dz pmuls Se,Sf,Dg */
+             else if ((field_b & 0xff00) == 0x8d00
+                      && (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh4al_dsp_up)))
+               {
+                 valid_arch = SH_MERGE_ARCH_SET (valid_arch, arch_sh4al_dsp_up);
+                 field_b -= 0x8cf0;
+               }
+             else
                as_bad (_("insn cannot be combined with pmuls"));
-             field_b -= 0x8100;
              switch (field_b & 0xf)
                {
                case A_X0_NUM:
@@ -1786,8 +2508,17 @@ assemble_ppi (op_end, opcode)
                  field_b += 3 - A_A1_NUM;
                  break;
                default:
-                 as_bad (_("bad padd / psub pmuls output operand"));
+                 as_bad (_("bad combined pmuls output operand"));
                }
+               /* Generate warning if the destination register for padd / psub
+                  and pmuls is the same ( only for A0 or A1 ).
+                  If the last nibble is 1010 then A0 is used in both
+                  padd / psub and pmuls. If it is 1111 then A1 is used
+                  as destination register in both padd / psub and pmuls.  */
+
+               if ((((field_b | reg_efg) & 0x000F) == 0x000A)
+                   || (((field_b | reg_efg) & 0x000F) == 0x000F))
+                 as_warn (_("destination register is same for parallel insns"));
            }
          field_b += 0x4000 + reg_efg;
          break;
@@ -1855,20 +2586,79 @@ assemble_ppi (op_end, opcode)
    the frags/bytes it assembles to.  */
 
 void
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   unsigned char *op_end;
   sh_operand_info operand[3];
   sh_opcode_info *opcode;
-  unsigned int size;
+  unsigned int size = 0;
+  char *initial_str = str;
+
+#ifdef HAVE_SH64
+  if (sh64_isa_mode == sh64_isa_shmedia)
+    {
+      shmedia_md_assemble (str);
+      return;
+    }
+  else
+    {
+      /* If we've seen pseudo-directives, make sure any emitted data or
+        frags are marked as data.  */
+      if (!seen_insn)
+       {
+         sh64_update_contents_mark (TRUE);
+         sh64_set_contents_type (CRT_SH5_ISA16);
+       }
+
+      seen_insn = TRUE;
+    }
+#endif /* HAVE_SH64 */
 
   opcode = find_cooked_opcode (&str);
   op_end = str;
 
   if (opcode == NULL)
     {
-      as_bad (_("unknown opcode"));
+      /* The opcode is not in the hash table.
+        This means we definately have an assembly failure,
+        but the instruction may be valid in another CPU variant.
+        In this case emit something better than 'unknown opcode'.
+        Search the full table in sh-opc.h to check. */
+
+      char *name = initial_str;
+      int name_length = 0;
+      const sh_opcode_info *op;
+      int found = 0;
+
+      /* identify opcode in string */
+      while (isspace (*name))
+       {
+         name++;
+       }
+      while (!isspace (name[name_length]))
+       {
+         name_length++;
+       }
+
+      /* search for opcode in full list */
+      for (op = sh_table; op->name; op++)
+       {
+         if (strncasecmp (op->name, name, name_length) == 0
+             && op->name[name_length] == '\0')
+           {
+             found = 1;
+             break;
+           }
+       }
+
+      if ( found )
+       {
+         as_bad (_("opcode not valid for this cpu variant"));
+       }
+      else
+       {
+         as_bad (_("unknown opcode"));
+       }
       return;
     }
 
@@ -1891,6 +2681,12 @@ md_assemble (str)
       if (opcode->arg[0] == A_BDISP12
          || opcode->arg[0] == A_BDISP8)
        {
+         /* Since we skip get_specific here, we have to check & update
+            valid_arch now.  */
+         if (SH_MERGE_ARCH_SET_VALID (valid_arch, opcode->arch))
+           valid_arch = SH_MERGE_ARCH_SET (valid_arch, opcode->arch);
+         else
+           as_bad (_("Delayed branches not available on SH1"));
          parse_exp (op_end + 1, &operand[0]);
          build_relax (opcode, &operand[0]);
        }
@@ -1929,14 +2725,16 @@ md_assemble (str)
        }
     }
 
+#ifdef BFD_ASSEMBLER
   dwarf2_emit_insn (size);
+#endif
 }
 
 /* This routine is called each time a label definition is seen.  It
    emits a BFD_RELOC_SH_LABEL reloc if necessary.  */
 
 void
-sh_frob_label ()
+sh_frob_label (void)
 {
   static fragS *last_label_frag;
   static int last_label_offset;
@@ -1961,7 +2759,7 @@ sh_frob_label ()
    data.  It emits a BFD_RELOC_SH_DATA reloc if necessary.  */
 
 void
-sh_flush_pending_output ()
+sh_flush_pending_output (void)
 {
   if (sh_relax
       && seg_info (now_seg)->tc_segment_info_data.in_code)
@@ -1973,27 +2771,8 @@ sh_flush_pending_output ()
 }
 
 symbolS *
-md_undefined_symbol (name)
-     char *name;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
 {
-#ifdef OBJ_ELF
-  /* Under ELF we need to default _GLOBAL_OFFSET_TABLE.  Otherwise we
-     have no need to default values of symbols.  */
-  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
-    {
-      if (!GOT_symbol)
-       {
-         if (symbol_find (name))
-           as_bad ("GOT already in the symbol table");
-
-         GOT_symbol = symbol_new (name, undefined_section,
-                                  (valueT)0, & zero_address_frag);
-       }
-
-      return GOT_symbol;
-    }
-#endif /* OBJ_ELF */
-
   return 0;
 }
 
@@ -2001,15 +2780,13 @@ md_undefined_symbol (name)
 #ifndef BFD_ASSEMBLER
 
 void
-tc_crawl_symbol_chain (headers)
-     object_headers *headers;
+tc_crawl_symbol_chain (object_headers *headers ATTRIBUTE_UNUSED)
 {
   printf (_("call to tc_crawl_symbol_chain \n"));
 }
 
 void
-tc_headers_hook (headers)
-     object_headers *headers;
+tc_headers_hook (object_headers *headers ATTRIBUTE_UNUSED)
 {
   printf (_("call to tc_headers_hook \n"));
 }
@@ -2027,10 +2804,7 @@ tc_headers_hook (headers)
    returned, or NULL on OK.  */
 
 char *
-md_atof (type, litP, sizeP)
-     int type;
-     char *litP;
-     int *sizeP;
+md_atof (int type, char *litP, int *sizeP)
 {
   int prec;
   LITTLENUM_TYPE words[4];
@@ -2084,8 +2858,7 @@ md_atof (type, litP, sizeP)
    special reloc for the linker.  */
 
 static void
-s_uses (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_uses (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
 
@@ -2106,26 +2879,44 @@ s_uses (ignore)
   demand_empty_rest_of_line ();
 }
 \f
-CONST char *md_shortopts = "";
+const char *md_shortopts = "";
 struct option md_longopts[] =
 {
 #define OPTION_RELAX  (OPTION_MD_BASE)
-#define OPTION_LITTLE (OPTION_MD_BASE + 1)
+#define OPTION_BIG (OPTION_MD_BASE + 1)
+#define OPTION_LITTLE (OPTION_BIG + 1)
 #define OPTION_SMALL (OPTION_LITTLE + 1)
 #define OPTION_DSP (OPTION_SMALL + 1)
+#define OPTION_ISA                    (OPTION_DSP + 1)
+#define OPTION_RENESAS (OPTION_ISA + 1)
 
   {"relax", no_argument, NULL, OPTION_RELAX},
+  {"big", no_argument, NULL, OPTION_BIG},
   {"little", no_argument, NULL, OPTION_LITTLE},
   {"small", no_argument, NULL, OPTION_SMALL},
   {"dsp", no_argument, NULL, OPTION_DSP},
+  {"isa",                    required_argument, NULL, OPTION_ISA},
+  {"renesas", no_argument, NULL, OPTION_RENESAS},
+
+#ifdef HAVE_SH64
+#define OPTION_ABI                    (OPTION_RENESAS + 1)
+#define OPTION_NO_MIX                 (OPTION_ABI + 1)
+#define OPTION_SHCOMPACT_CONST_CRANGE (OPTION_NO_MIX + 1)
+#define OPTION_NO_EXPAND              (OPTION_SHCOMPACT_CONST_CRANGE + 1)
+#define OPTION_PT32                   (OPTION_NO_EXPAND + 1)
+  {"abi",                    required_argument, NULL, OPTION_ABI},
+  {"no-mix",                 no_argument, NULL, OPTION_NO_MIX},
+  {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
+  {"no-expand",              no_argument, NULL, OPTION_NO_EXPAND},
+  {"expand-pt32",            no_argument, NULL, OPTION_PT32},
+#endif /* HAVE_SH64 */
+
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
 
 int
-md_parse_option (c, arg)
-     int c;
-     char *arg ATTRIBUTE_UNUSED;
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
@@ -2133,8 +2924,11 @@ md_parse_option (c, arg)
       sh_relax = 1;
       break;
 
+    case OPTION_BIG:
+      target_big_endian = 1;
+      break;
+
     case OPTION_LITTLE:
-      shl = 1;
       target_big_endian = 0;
       break;
 
@@ -2143,9 +2937,113 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_DSP:
-      sh_dsp = 1;
+      preset_target_arch = arch_sh1_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+      break;
+
+    case OPTION_RENESAS:
+      dont_adjust_reloc_32 = 1;
+      break;
+
+    case OPTION_ISA:
+      if (strcasecmp (arg, "sh4") == 0)
+       preset_target_arch = arch_sh4;
+      else if (strcasecmp (arg, "sh4-nofpu") == 0)
+       preset_target_arch = arch_sh4_nofpu;
+      else if (strcasecmp (arg, "sh4-nommu-nofpu") == 0)
+       preset_target_arch = arch_sh4_nommu_nofpu;
+      else if (strcasecmp (arg, "sh4a") == 0)
+       preset_target_arch = arch_sh4a;
+      else if (strcasecmp (arg, "dsp") == 0)
+       preset_target_arch = arch_sh1_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+      else if (strcasecmp (arg, "fp") == 0)
+       preset_target_arch = arch_sh1_up & ~arch_sh_has_dsp;
+      else if (strcasecmp (arg, "any") == 0)
+       preset_target_arch = arch_sh1_up;
+#ifdef HAVE_SH64
+      else if (strcasecmp (arg, "shmedia") == 0)
+       {
+         if (sh64_isa_mode == sh64_isa_shcompact)
+           as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
+         sh64_isa_mode = sh64_isa_shmedia;
+       }
+      else if (strcasecmp (arg, "shcompact") == 0)
+       {
+         if (sh64_isa_mode == sh64_isa_shmedia)
+           as_bad (_("Invalid combination: --isa=SHmedia with --isa=SHcompact"));
+         if (sh64_abi == sh64_abi_64)
+           as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
+         sh64_isa_mode = sh64_isa_shcompact;
+       }
+#endif /* HAVE_SH64 */
+      else
+       {
+         extern const bfd_arch_info_type bfd_sh_arch;
+         extern unsigned int sh_ef_archset_table[];
+         bfd_arch_info_type *bfd_arch = &bfd_sh_arch;
+         preset_target_arch = 0;
+         for (; bfd_arch; bfd_arch=bfd_arch->next)
+           {
+             int len = strlen(bfd_arch->printable_name);
+             
+             if (bfd_arch->mach == bfd_mach_sh5)
+               continue;
+             
+             if (strncasecmp (bfd_arch->printable_name, arg, len) != 0)
+               continue;
+
+             if (arg[len] == '\0')
+               preset_target_arch =
+                 sh_get_arch_from_bfd_mach (bfd_arch->mach);
+             else if (strcasecmp(&arg[len], "-up") == 0)
+               preset_target_arch =
+                 sh_get_arch_up_from_bfd_mach (bfd_arch->mach);
+             else
+               continue;
+             break;
+           }
+         
+         if (!preset_target_arch)
+           as_bad ("Invalid argument to --isa option: %s", arg);
+       }
+      break;
+
+#ifdef HAVE_SH64
+    case OPTION_ABI:
+      if (strcmp (arg, "32") == 0)
+       {
+         if (sh64_abi == sh64_abi_64)
+           as_bad (_("Invalid combination: --abi=32 with --abi=64"));
+         sh64_abi = sh64_abi_32;
+       }
+      else if (strcmp (arg, "64") == 0)
+       {
+         if (sh64_abi == sh64_abi_32)
+           as_bad (_("Invalid combination: --abi=64 with --abi=32"));
+         if (sh64_isa_mode == sh64_isa_shcompact)
+           as_bad (_("Invalid combination: --isa=SHcompact with --abi=64"));
+         sh64_abi = sh64_abi_64;
+       }
+      else
+       as_bad ("Invalid argument to --abi option: %s", arg);
+      break;
+
+    case OPTION_NO_MIX:
+      sh64_mix = FALSE;
+      break;
+
+    case OPTION_SHCOMPACT_CONST_CRANGE:
+      sh64_shcompact_const_crange = TRUE;
       break;
 
+    case OPTION_NO_EXPAND:
+      sh64_expand = FALSE;
+      break;
+
+    case OPTION_PT32:
+      sh64_pt32 = TRUE;
+      break;
+#endif /* HAVE_SH64 */
+
     default:
       return 0;
     }
@@ -2154,24 +3052,50 @@ md_parse_option (c, arg)
 }
 
 void
-md_show_usage (stream)
-     FILE *stream;
+md_show_usage (FILE *stream)
 {
   fprintf (stream, _("\
 SH options:\n\
 -little                        generate little endian code\n\
+-big                   generate big endian code\n\
 -relax                 alter jump instructions for long displacements\n\
+-renesas               disable optimization with section symbol for\n\
+                       compatibility with Renesas assembler.\n\
 -small                 align sections to 4 byte boundaries, not 16\n\
--dsp                   enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
+-dsp                   enable sh-dsp insns, and disable floating-point ISAs.\n\
+-isa=[any              use most appropriate isa\n\
+    | dsp               same as '-dsp'\n\
+    | fp"));
+  {
+    extern const bfd_arch_info_type bfd_sh_arch;
+    bfd_arch_info_type *bfd_arch = &bfd_sh_arch;
+    for (; bfd_arch; bfd_arch=bfd_arch->next)
+      if (bfd_arch->mach != bfd_mach_sh5)
+       {
+         fprintf (stream, "\n    | %s", bfd_arch->printable_name);
+         fprintf (stream, "\n    | %s-up", bfd_arch->printable_name);
+       }
+  }
+  fprintf (stream, "]\n");
+#ifdef HAVE_SH64
+  fprintf (stream, _("\
+-isa=[shmedia          set as the default instruction set for SH64\n\
+    | SHmedia\n\
+    | shcompact\n\
+    | SHcompact]\n"));
+  fprintf (stream, _("\
+-abi=[32|64]           set size of expanded SHmedia operands and object\n\
+                       file type\n\
+-shcompact-const-crange        emit code-range descriptors for constants in\n\
+                       SHcompact code sections\n\
+-no-mix                        disallow SHmedia code in the same section as\n\
+                       constants and SHcompact code\n\
+-no-expand             do not expand MOVI, PT, PTA or PTB instructions\n\
+-expand-pt32           with -abi=64, expand PT, PTA and PTB instructions\n\
+                       to 32 bits only\n"));
+#endif /* HAVE_SH64 */
 }
 \f
-void
-tc_Nout_fix_to_chars ()
-{
-  printf (_("call to tc_Nout_fix_to_chars \n"));
-  abort ();
-}
-
 /* This struct is used to pass arguments to sh_count_relocs through
    bfd_map_over_sections.  */
 
@@ -2188,10 +3112,7 @@ struct sh_count_relocs
    bfd_map_over_sections.  */
 
 static void
-sh_count_relocs (abfd, sec, data)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT sec;
-     PTR data;
+sh_count_relocs (bfd *abfd ATTRIBUTE_UNUSED, segT sec, void *data)
 {
   struct sh_count_relocs *info = (struct sh_count_relocs *) data;
   segment_info_type *seginfo;
@@ -2217,10 +3138,8 @@ sh_count_relocs (abfd, sec, data)
    BFD_ASSEMBLER, this is called via bfd_map_over_sections.  */
 
 static void
-sh_frob_section (abfd, sec, ignore)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT sec;
-     PTR ignore ATTRIBUTE_UNUSED;
+sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
+                void *ignore ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo;
   fixS *fix;
@@ -2303,13 +3222,13 @@ sh_frob_section (abfd, sec, ignore)
       info.sym = sym;
       info.count = 0;
 #ifdef BFD_ASSEMBLER
-      bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);
+      bfd_map_over_sections (stdoutput, sh_count_relocs, &info);
 #else
       {
        int iscan;
 
        for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
-         sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info);
+         sh_count_relocs ((bfd *) NULL, iscan, &info);
       }
 #endif
 
@@ -2320,8 +3239,8 @@ sh_frob_section (abfd, sec, ignore)
         We have already adjusted the value of sym to include the
         fragment address, so we undo that adjustment here.  */
       subseg_change (sec, 0);
-      fix_new (symbol_get_frag (sym),
-              S_GET_VALUE (sym) - symbol_get_frag (sym)->fr_address,
+      fix_new (fscan->fx_frag,
+              S_GET_VALUE (sym) - fscan->fx_frag->fr_address,
               4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
     }
 }
@@ -2337,35 +3256,37 @@ sh_frob_section (abfd, sec, ignore)
    the stored function address entirely.  */
 
 void
-sh_frob_file ()
+sh_frob_file (void)
 {
+#ifdef HAVE_SH64
+  shmedia_frob_file_before_adjust ();
+#endif
+
   if (! sh_relax)
     return;
 
 #ifdef BFD_ASSEMBLER
-  bfd_map_over_sections (stdoutput, sh_frob_section, (PTR) NULL);
+  bfd_map_over_sections (stdoutput, sh_frob_section, NULL);
 #else
   {
     int iseg;
 
     for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
-      sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL);
+      sh_frob_section ((bfd *) NULL, iseg, NULL);
   }
 #endif
 }
 
 /* Called after relaxing.  Set the correct sizes of the fragments, and
-   create relocs so that md_apply_fix will fill in the correct values.  */
+   create relocs so that md_apply_fix3 will fill in the correct values.  */
 
 void
-md_convert_frag (headers, seg, fragP)
 #ifdef BFD_ASSEMBLER
-     bfd *headers ATTRIBUTE_UNUSED;
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg, fragS *fragP)
 #else
-     object_headers *headers;
+md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
+                fragS *fragP)
 #endif
-     segT seg;
-     fragS *fragP;
 {
   int donerelax = 0;
 
@@ -2391,17 +3312,19 @@ md_convert_frag (headers, seg, fragP)
     case C (UNCOND_JUMP, UNCOND32):
     case C (UNCOND_JUMP, UNDEF_WORD_DISP):
       if (fragP->fr_symbol == NULL)
-       as_bad (_("at 0x%lx, displacement overflows 12-bit field"),
-               (unsigned long) fragP->fr_address);
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement overflows 12-bit field"));
       else if (S_IS_DEFINED (fragP->fr_symbol))
-       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 12-bit field"),
-               (unsigned long) fragP->fr_address,
-               S_GET_NAME (fragP->fr_symbol));
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to defined symbol %s overflows 12-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
       else
-       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 12-bit field"),
-               (unsigned long) fragP->fr_address,
-               S_GET_NAME (fragP->fr_symbol));
-
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to undefined symbol %s overflows 12-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
+      /* Stabilize this frag, so we don't trip an assert.  */
+      fragP->fr_fix += fragP->fr_var;
+      fragP->fr_var = 0;
       break;
 
     case C (COND_JUMP, COND12):
@@ -2428,7 +3351,7 @@ md_convert_frag (headers, seg, fragP)
        /* Toggle the true/false bit of the bcond.  */
        buffer[highbyte] ^= 0x2;
 
-       /* If this is a dalayed branch, we may not put the the bra in the
+       /* If this is a delayed branch, we may not put the bra in the
           slot.  So we change it to a non-delayed branch, like that:
           b! cond slot_label; bra disp; slot_label: slot_insn
           ??? We should try if swapping the conditional branch and
@@ -2474,20 +3397,27 @@ md_convert_frag (headers, seg, fragP)
     case C (COND_JUMP, UNDEF_WORD_DISP):
     case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
       if (fragP->fr_symbol == NULL)
-       as_bad (_("at 0x%lx, displacement overflows 8-bit field"),
-               (unsigned long) fragP->fr_address);
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement overflows 8-bit field"));
       else if (S_IS_DEFINED (fragP->fr_symbol))
-       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 8-bit field "),
-               (unsigned long) fragP->fr_address,
-               S_GET_NAME (fragP->fr_symbol));
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to defined symbol %s overflows 8-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
       else
-       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 8-bit field "),
-               (unsigned long) fragP->fr_address,
-               S_GET_NAME (fragP->fr_symbol));
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to undefined symbol %s overflows 8-bit field "),
+                     S_GET_NAME (fragP->fr_symbol));
+      /* Stabilize this frag, so we don't trip an assert.  */
+      fragP->fr_fix += fragP->fr_var;
+      fragP->fr_var = 0;
       break;
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_convert_frag (headers, seg, fragP, TRUE);
+#else
       abort ();
+#endif
     }
 
   if (donerelax && !sh_relax)
@@ -2499,9 +3429,7 @@ md_convert_frag (headers, seg, fragP)
 }
 
 valueT
-md_section_align (seg, size)
-     segT seg;
-     valueT size;
+md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
 {
 #ifdef BFD_ASSEMBLER
 #ifdef OBJ_ELF
@@ -2517,7 +3445,7 @@ md_section_align (seg, size)
 }
 
 /* This static variable is set by s_uacons to tell sh_cons_align that
-   the expession does not need to be aligned.  */
+   the expression does not need to be aligned.  */
 
 static int sh_no_align_cons = 0;
 
@@ -2526,8 +3454,7 @@ static int sh_no_align_cons = 0;
    to be aligned.  */
 
 static void
-s_uacons (bytes)
-     int bytes;
+s_uacons (int bytes)
 {
   /* Tell sh_cons_align not to align this value.  */
   sh_no_align_cons = 1;
@@ -2541,8 +3468,7 @@ s_uacons (bytes)
    enable this warning?  */
 
 void
-sh_cons_align (nbytes)
-     int nbytes;
+sh_cons_align (int nbytes)
 {
   int nalign;
   char *p;
@@ -2571,7 +3497,7 @@ sh_cons_align (nbytes)
       return;
     }
 
-  p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+  p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
                (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
 
   record_alignment (now_seg, nalign);
@@ -2582,60 +3508,87 @@ sh_cons_align (nbytes)
    also where we check for misaligned data.  */
 
 void
-sh_handle_align (frag)
-     fragS *frag;
+sh_handle_align (fragS *frag)
 {
+  int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+
+  if (frag->fr_type == rs_align_code)
+    {
+      static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+      static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+      char *p = frag->fr_literal + frag->fr_fix;
+
+      if (bytes & 1)
+       {
+         *p++ = 0;
+         bytes--;
+         frag->fr_fix += 1;
+       }
+
+      if (target_big_endian)
+       {
+         memcpy (p, big_nop_pattern, sizeof big_nop_pattern);
+         frag->fr_var = sizeof big_nop_pattern;
+       }
+      else
+       {
+         memcpy (p, little_nop_pattern, sizeof little_nop_pattern);
+         frag->fr_var = sizeof little_nop_pattern;
+       }
+    }
+  else if (frag->fr_type == rs_align_test)
+    {
+      if (bytes != 0)
+       as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+    }
+
   if (sh_relax
-      && frag->fr_type == rs_align
+      && (frag->fr_type == rs_align
+         || frag->fr_type == rs_align_code)
       && frag->fr_address + frag->fr_fix > 0
       && frag->fr_offset > 1
       && now_seg != bss_section)
     fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
             BFD_RELOC_SH_ALIGN);
-
-  if (frag->fr_type == rs_align_code
-      && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
-    as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
 }
 
-/* This macro decides whether a particular reloc is an entry in a
-   switch table.  It is used when relaxing, because the linker needs
-   to know about all such entries so that it can adjust them if
-   necessary.  */
-
-#ifdef BFD_ASSEMBLER
-#define SWITCH_TABLE_CONS(fix) (0)
-#else
-#define SWITCH_TABLE_CONS(fix)                         \
-  ((fix)->fx_r_type == 0                               \
-   && ((fix)->fx_size == 2                             \
-       || (fix)->fx_size == 1                          \
-       || (fix)->fx_size == 4))
-#endif
+/* See whether the relocation should be resolved locally.  */
 
-#define SWITCH_TABLE(fix)                              \
-  ((fix)->fx_addsy != NULL                             \
-   && (fix)->fx_subsy != NULL                          \
-   && S_GET_SEGMENT ((fix)->fx_addsy) == text_section  \
-   && S_GET_SEGMENT ((fix)->fx_subsy) == text_section  \
-   && ((fix)->fx_r_type == BFD_RELOC_32                        \
-       || (fix)->fx_r_type == BFD_RELOC_16             \
-       || (fix)->fx_r_type == BFD_RELOC_8              \
-       || SWITCH_TABLE_CONS (fix)))
+static bfd_boolean
+sh_local_pcrel (fixS *fix)
+{
+  return (! sh_relax
+         && (fix->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+             || fix->fx_r_type == BFD_RELOC_8_PCREL
+             || fix->fx_r_type == BFD_RELOC_SH_SWITCH16
+             || fix->fx_r_type == BFD_RELOC_SH_SWITCH32));
+}
 
 /* See whether we need to force a relocation into the output file.
    This is used to force out switch and PC relative relocations when
    relaxing.  */
 
 int
-sh_force_relocation (fix)
-     fixS *fix;
+sh_force_relocation (fixS *fix)
 {
+  /* These relocations can't make it into a DSO, so no use forcing
+     them for global symbols.  */
+  if (sh_local_pcrel (fix))
+    return 0;
 
-  if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY
-      || fix->fx_r_type == BFD_RELOC_SH_LOOP_START
-      || fix->fx_r_type == BFD_RELOC_SH_LOOP_END)
+  /* Make sure some relocations get emitted.  */
+  if (fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+      || fix->fx_r_type == BFD_RELOC_SH_LOOP_END
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_GD_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LD_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_IE_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LDO_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LE_32
+      || generic_force_reloc (fix))
     return 1;
 
   if (! sh_relax)
@@ -2647,29 +3600,20 @@ sh_force_relocation (fix)
          || fix->fx_r_type == BFD_RELOC_SH_ALIGN
          || fix->fx_r_type == BFD_RELOC_SH_CODE
          || fix->fx_r_type == BFD_RELOC_SH_DATA
+#ifdef HAVE_SH64
+         || fix->fx_r_type == BFD_RELOC_SH_SHMEDIA_CODE
+#endif
          || fix->fx_r_type == BFD_RELOC_SH_LABEL);
 }
 
 #ifdef OBJ_ELF
-boolean
-sh_fix_adjustable (fixP)
-   fixS *fixP;
+bfd_boolean
+sh_fix_adjustable (fixS *fixP)
 {
-
-  if (fixP->fx_addsy == NULL)
-    return 1;
-
-  if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
-      || fixP->fx_r_type == BFD_RELOC_8_PCREL
-      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16
-      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32)
-    return 1;
-
-  if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP)
-      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+  if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+      || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
       || fixP->fx_r_type == BFD_RELOC_RVA)
     return 0;
 
@@ -2682,28 +3626,20 @@ sh_fix_adjustable (fixP)
 }
 
 void
-sh_elf_final_processing ()
+sh_elf_final_processing (void)
 {
   int val;
 
   /* Set file-specific flags to indicate if this code needs
-     a processor with the sh-dsp / sh3e ISA to execute.  */
-  if (valid_arch & arch_sh1)
-    val = EF_SH1;
-  else if (valid_arch & arch_sh2)
-    val = EF_SH2;
-  else if (valid_arch & arch_sh_dsp)
-    val = EF_SH_DSP;
-  else if (valid_arch & arch_sh3)
-    val = EF_SH3;
-  else if (valid_arch & arch_sh3_dsp)
-    val = EF_SH_DSP;
-  else if (valid_arch & arch_sh3e)
-    val = EF_SH3E;
-  else if (valid_arch & arch_sh4)
-    val = EF_SH4;
+     a processor with the sh-dsp / sh2e ISA to execute.  */
+#ifdef HAVE_SH64
+  /* SH5 and above don't know about the valid_arch arch_sh* bits defined
+     in sh-opc.h, so check SH64 mode before checking valid_arch.  */
+  if (sh64_isa_mode != sh64_isa_unspecified)
+    val = EF_SH5;
   else
-    abort ();
+#endif /* HAVE_SH64 */
+    val = sh_find_elf_flags (valid_arch);
 
   elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
   elf_elfheader (stdoutput)->e_flags |= val;
@@ -2712,24 +3648,13 @@ sh_elf_final_processing ()
 
 /* Apply a fixup to the object file.  */
 
-#ifdef BFD_ASSEMBLER
-int
-md_apply_fix (fixP, valp)
-     fixS *fixP;
-     valueT *valp;
-#else
 void
-md_apply_fix (fixP, val)
-     fixS *fixP;
-     long val;
-#endif
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
   int lowbyte = target_big_endian ? 1 : 0;
   int highbyte = target_big_endian ? 0 : 1;
-#ifdef BFD_ASSEMBLER
-  long val = *valp;
-#endif
+  long val = (long) *valP;
   long max, min;
   int shift;
 
@@ -2755,11 +3680,11 @@ md_apply_fix (fixP, val)
             we need.  FIXME.  */
        case BFD_RELOC_16:
          bfd_set_error (bfd_error_bad_value);
-         return false;
+         return;
 
        case BFD_RELOC_8:
          bfd_set_error (bfd_error_bad_value);
-         return false;
+         return;
        }
     }
 
@@ -2781,7 +3706,10 @@ md_apply_fix (fixP, val)
     val -= S_GET_VALUE  (fixP->fx_addsy);
 #endif
 
-#ifndef BFD_ASSEMBLER
+#ifdef BFD_ASSEMBLER
+  if (SWITCH_TABLE (fixP))
+    val -= S_GET_VALUE  (fixP->fx_subsy);
+#else
   if (fixP->fx_r_type == 0)
     {
       if (fixP->fx_size == 2)
@@ -2842,7 +3770,7 @@ md_apply_fix (fixP, val)
     case BFD_RELOC_SH_PCRELIMM8BY4:
       /* The lower two bits of the PC are cleared before the
          displacement is added in.  We can assume that the destination
-         is on a 4 byte bounday.  If this instruction is also on a 4
+         is on a 4 byte boundary.  If this instruction is also on a 4
          byte boundary, then we want
           (target - here) / 4
         and target - here is a multiple of 4.
@@ -2909,17 +3837,18 @@ md_apply_fix (fixP, val)
     case BFD_RELOC_VTABLE_INHERIT:
     case BFD_RELOC_VTABLE_ENTRY:
       fixP->fx_done = 0;
-#ifdef BFD_ASSEMBLER
-      return 0;
-#else
       return;
-#endif
 
 #ifdef OBJ_ELF
     case BFD_RELOC_32_PLT_PCREL:
       /* Make the jump instruction point to the address of the operand.  At
         runtime we merely add the offset to the actual PLT entry.  */
-      *valp = 0xfffffffc;
+      * valP = 0xfffffffc;
+      val = fixP->fx_offset;
+      if (fixP->fx_subsy)
+       val -= S_GET_VALUE (fixP->fx_subsy);
+      fixP->fx_addnumber = val;
+      md_number_to_chars (buf, val, 4);
       break;
 
     case BFD_RELOC_SH_GOTPC:
@@ -2939,21 +3868,37 @@ md_apply_fix (fixP, val)
          earlier versions of the PIC patches, the pcrel_adjust field
          was used to store the correction, but since the expression is
          not pcrel, I felt it would be confusing to do it this way.  */
-      *valp -= 1;
+      * valP -= 1;
       md_number_to_chars (buf, val, 4);
       break;
 
+    case BFD_RELOC_SH_TLS_GD_32:
+    case BFD_RELOC_SH_TLS_LD_32:
+    case BFD_RELOC_SH_TLS_IE_32:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* Fallthrough */
     case BFD_RELOC_32_GOT_PCREL:
-      *valp = 0; /* Fully resolved at runtime.  No addend.  */
+    case BFD_RELOC_SH_GOTPLT32:
+      * valP = 0; /* Fully resolved at runtime.  No addend.  */
       md_number_to_chars (buf, 0, 4);
       break;
 
+    case BFD_RELOC_SH_TLS_LDO_32:
+    case BFD_RELOC_SH_TLS_LE_32:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* Fallthrough */
     case BFD_RELOC_32_GOTOFF:
+      md_number_to_chars (buf, val, 4);
       break;
 #endif
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_apply_fix3 (fixP, valP);
+      return;
+#else
       abort ();
+#endif
     }
 
   if (shift != 0)
@@ -2969,138 +3914,140 @@ md_apply_fix (fixP, val)
   if (max != 0 && (val < min || val > max))
     as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
 
-#ifdef BFD_ASSEMBLER
-  return 0;
-#endif
+  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+    fixP->fx_done = 1;
 }
 
 /* Called just before address relaxation.  Return the length
    by which a fragment must grow to reach it's destination.  */
 
 int
-md_estimate_size_before_relax (fragP, segment_type)
-     register fragS *fragP;
-     register segT segment_type;
+md_estimate_size_before_relax (fragS *fragP, segT segment_type)
 {
+  int what;
+
   switch (fragP->fr_subtype)
     {
+    default:
+#ifdef HAVE_SH64
+      return shmedia_md_estimate_size_before_relax (fragP, segment_type);
+#else
+      abort ();
+#endif
+
+
     case C (UNCOND_JUMP, UNDEF_DISP):
       /* Used to be a branch to somewhere which was unknown.  */
       if (!fragP->fr_symbol)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
-         fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
        }
       else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
-         fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
        }
       else
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP);
-         fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
-         return md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
        }
       break;
 
-    default:
-      abort ();
     case C (COND_JUMP, UNDEF_DISP):
     case C (COND_JUMP_DELAY, UNDEF_DISP):
+      what = GET_WHAT (fragP->fr_subtype);
       /* Used to be a branch to somewhere which was unknown.  */
       if (fragP->fr_symbol
          && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
        {
-         int what = GET_WHAT (fragP->fr_subtype);
          /* Got a symbol and it's defined in this segment, become byte
             sized - maybe it will fix up.  */
          fragP->fr_subtype = C (what, COND8);
-         fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
       else if (fragP->fr_symbol)
        {
-         int what = GET_WHAT (fragP->fr_subtype);
          /* Its got a segment, but its not ours, so it will always be long.  */
          fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
-         fragP->fr_var = md_relax_table[C (what, COND32)].rlx_length;
-         return md_relax_table[C (what, COND32)].rlx_length;
        }
       else
        {
-         int what = GET_WHAT (fragP->fr_subtype);
          /* We know the abs value.  */
          fragP->fr_subtype = C (what, COND8);
-         fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
+      break;
 
+    case C (UNCOND_JUMP, UNCOND12):
+    case C (UNCOND_JUMP, UNCOND32):
+    case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP, COND8):
+    case C (COND_JUMP, COND12):
+    case C (COND_JUMP, COND32):
+    case C (COND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP_DELAY, COND8):
+    case C (COND_JUMP_DELAY, COND12):
+    case C (COND_JUMP_DELAY, COND32):
+    case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+      /* When relaxing a section for the second time, we don't need to
+        do anything besides return the current size.  */
       break;
     }
+
+  fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
   return fragP->fr_var;
 }
 
 /* Put number into target byte order.  */
 
 void
-md_number_to_chars (ptr, use, nbytes)
-     char *ptr;
-     valueT use;
-     int nbytes;
+md_number_to_chars (char *ptr, valueT use, int nbytes)
 {
+#ifdef HAVE_SH64
+  /* We might need to set the contents type to data.  */
+  sh64_flag_output ();
+#endif
+
   if (! target_big_endian)
     number_to_chars_littleendian (ptr, use, nbytes);
   else
     number_to_chars_bigendian (ptr, use, nbytes);
 }
 
+/* This version is used in obj-coff.c when not using BFD_ASSEMBLER.
+   eg for the sh-hms target.  */
+
 long
-md_pcrel_from (fixP)
-     fixS *fixP;
+md_pcrel_from (fixS *fixP)
 {
   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
 }
 
+long
+md_pcrel_from_section (fixS *fixP, segT sec)
+{
+  if (! sh_local_pcrel (fixP)
+      && fixP->fx_addsy != (symbolS *) NULL
+      && (generic_force_reloc (fixP)
+         || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+    {
+      /* The symbol is undefined (or is defined but not in this section,
+        or we're not sure about it being the final definition).  Let the
+        linker figure it out.  We need to adjust the subtraction of a
+        symbol to the position of the relocated data, though.  */
+      return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
+    }
+
+  return md_pcrel_from (fixP);
+}
+
 #ifdef OBJ_COFF
 
 int
-tc_coff_sizemachdep (frag)
-     fragS *frag;
+tc_coff_sizemachdep (fragS *frag)
 {
   return md_relax_table[frag->fr_subtype].rlx_length;
 }
 
 #endif /* OBJ_COFF */
 
-/* When we align the .text section, insert the correct NOP pattern.  */
-
-int
-sh_do_align (n, fill, len, max)
-     int n;
-     const char *fill;
-     int len ATTRIBUTE_UNUSED;
-     int max;
-{
-  if (fill == NULL
-      && subseg_text_p (now_seg)
-      && n > 1)
-    {
-      static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
-      static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
-
-      /* First align to a 2 byte boundary, in case there is an odd
-         .byte.  */
-      frag_align (1, 0, 0);
-      if (target_big_endian)
-       frag_align_pattern (n, big_nop_pattern, sizeof big_nop_pattern, max);
-      else
-       frag_align_pattern (n, little_nop_pattern, sizeof little_nop_pattern,
-                           max);
-      return 1;
-    }
-
-  return 0;
-}
-
 #ifndef BFD_ASSEMBLER
 #ifdef OBJ_COFF
 
@@ -3143,11 +4090,8 @@ static const struct reloc_map coff_reloc_map[] =
    but does some minor tweaking.  */
 
 void
-sh_coff_reloc_mangle (seg, fix, intr, paddr)
-     segment_info_type *seg;
-     fixS *fix;
-     struct internal_reloc *intr;
-     unsigned int paddr;
+sh_coff_reloc_mangle (segment_info_type *seg, fixS *fix,
+                     struct internal_reloc *intr, unsigned int paddr)
 {
   symbolS *symbol_ptr = fix->fx_addsy;
   symbolS *dot;
@@ -3209,7 +4153,7 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
     {
       /* We can't store the offset in the object file, since this
         reloc does not take up any space, so we store it in r_offset.
-        The fx_addnumber field was set in md_apply_fix.  */
+        The fx_addnumber field was set in md_apply_fix3.  */
       intr->r_offset = fix->fx_addnumber;
     }
   else if (fix->fx_r_type == BFD_RELOC_SH_COUNT)
@@ -3258,9 +4202,7 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
 /* Create a reloc.  */
 
 arelent *
-tc_gen_reloc (section, fixp)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixp;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 {
   arelent *rel;
   bfd_reloc_code_real_type r_type;
@@ -3274,7 +4216,8 @@ tc_gen_reloc (section, fixp)
 
   if (SWITCH_TABLE (fixp))
     {
-      rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+      *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+      rel->addend = 0;
       if (r_type == BFD_RELOC_16)
        r_type = BFD_RELOC_SH_SWITCH16;
       else if (r_type == BFD_RELOC_8)
@@ -3301,6 +4244,10 @@ tc_gen_reloc (section, fixp)
       rel->addend = 0;
       rel->address = rel->addend = fixp->fx_offset;
     }
+#ifdef HAVE_SH64
+  else if (shmedia_init_reloc (rel, fixp))
+    ;
+#endif
   else if (fixp->fx_pcrel)
     rel->addend = fixp->fx_addnumber;
   else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
@@ -3309,6 +4256,7 @@ tc_gen_reloc (section, fixp)
     rel->addend = 0;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
   if (rel->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
@@ -3318,8 +4266,152 @@ tc_gen_reloc (section, fixp)
       rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
       assert (rel->howto != NULL);
     }
+#ifdef OBJ_ELF
+  else if (rel->howto->type == R_SH_IND12W)
+    rel->addend += fixp->fx_offset - 4;
+#endif
 
   return rel;
 }
 
+#ifdef OBJ_ELF
+inline static char *
+sh_end_of_match (char *cont, char *what)
+{
+  int len = strlen (what);
+
+  if (strncasecmp (cont, what, strlen (what)) == 0
+      && ! is_part_of_name (cont[len]))
+    return cont + len;
+
+  return NULL;
+}
+
+int
+sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+{
+  char *next = input_line_pointer;
+  char *next_end;
+  int reloc_type;
+  segT segment;
+
+  exprP->X_op_symbol = NULL;
+
+  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+    {
+      if (! GOT_symbol)
+       GOT_symbol = symbol_find_or_make (name);
+
+      exprP->X_add_symbol = GOT_symbol;
+    no_suffix:
+      /* If we have an absolute symbol or a reg, then we know its
+            value now.  */
+      segment = S_GET_SEGMENT (exprP->X_add_symbol);
+      if (segment == absolute_section)
+       {
+         exprP->X_op = O_constant;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else if (segment == reg_section)
+       {
+         exprP->X_op = O_register;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else
+       {
+         exprP->X_op = O_symbol;
+         exprP->X_add_number = 0;
+       }
+
+      return 1;
+    }
+
+  exprP->X_add_symbol = symbol_find_or_make (name);
+
+  if (*nextcharP != '@')
+    goto no_suffix;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTOFF")))
+    reloc_type = BFD_RELOC_32_GOTOFF;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTPLT")))
+    reloc_type = BFD_RELOC_SH_GOTPLT32;
+  else if ((next_end = sh_end_of_match (next + 1, "GOT")))
+    reloc_type = BFD_RELOC_32_GOT_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "PLT")))
+    reloc_type = BFD_RELOC_32_PLT_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "TLSGD")))
+    reloc_type = BFD_RELOC_SH_TLS_GD_32;
+  else if ((next_end = sh_end_of_match (next + 1, "TLSLDM")))
+    reloc_type = BFD_RELOC_SH_TLS_LD_32;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTTPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_IE_32;
+  else if ((next_end = sh_end_of_match (next + 1, "TPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_LE_32;
+  else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_LDO_32;
+  else
+    goto no_suffix;
+
+  *input_line_pointer = *nextcharP;
+  input_line_pointer = next_end;
+  *nextcharP = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  exprP->X_op = O_PIC_reloc;
+  exprP->X_add_number = 0;
+  exprP->X_md = reloc_type;
+
+  return 1;
+}
+#endif
+
+void
+sh_cfi_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa (15, 0);
+}
+
+int
+sh_regname_to_dw2regnum (const char *regname)
+{
+  unsigned int regnum = -1;
+  unsigned int i;
+  const char *p;
+  char *q;
+  static struct { char *name; int dw2regnum; } regnames[] =
+    {
+      { "pr", 17 }, { "t", 18 }, { "gbr", 19 }, { "mach", 20 },
+      { "macl", 21 }, { "fpul", 23 }
+    };
+
+  for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+    if (strcmp (regnames[i].name, regname) == 0)
+      return regnames[i].dw2regnum;
+
+  if (regname[0] == 'r')
+    {
+      p = regname + 1;
+      regnum = strtoul (p, &q, 10);
+      if (p == q || *q || regnum >= 16)
+       return -1;
+    }
+  else if (regname[0] == 'f' && regname[1] == 'r')
+    {
+      p = regname + 2;
+      regnum = strtoul (p, &q, 10);
+      if (p == q || *q || regnum >= 16)
+       return -1;
+      regnum += 25;
+    }
+  else if (regname[0] == 'x' && regname[1] == 'd')
+    {
+      p = regname + 2;
+      regnum = strtoul (p, &q, 10);
+      if (p == q || *q || regnum >= 8)
+       return -1;
+      regnum += 87;
+    }
+  return regnum;
+}
 #endif /* BFD_ASSEMBLER */
This page took 0.061232 seconds and 4 git commands to generate.