]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-sh.c
This commit was manufactured by cvs2svn to create branch 'binutils-
[thirdparty/binutils-gdb.git] / gas / config / tc-sh.c
index b235cdda83b6feeb4410d13fccc79ad76faa3e69..4db1a0913602aaf18d1390cc315f6db0fbdef649 100644 (file)
@@ -1,12 +1,13 @@
-/* tc-sh.c -- Assemble code for the Hitachi Super-H
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 /* Written By Steve Chamberlain <sac@cygnus.com>  */
 
-#include <stdio.h>
 #include "as.h"
-#include "bfd.h"
 #include "subsegs.h"
 #define DEFINE_TABLE
 #include "opcodes/sh-opc.h"
@@ -35,6 +34,7 @@
 #endif
 
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 
 typedef struct
   {
@@ -48,42 +48,17 @@ 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));
-
-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 little PARAMS ((int));
-static void big PARAMS ((int));
-static bfd_reloc_code_real_type sh_elf_suffix
-  PARAMS ((char **str_p, expressionS *, expressionS *new_exp_p));
-static int parse_reg PARAMS ((char *, int *, int *));
-static symbolS *dot PARAMS ((void));
-static char *parse_exp PARAMS ((char *, sh_operand_info *));
-static char *parse_at PARAMS ((char *, sh_operand_info *));
-static void get_operand PARAMS ((char **, sh_operand_info *));
-static char *get_operands
-  PARAMS ((sh_opcode_info *, char *, sh_operand_info *));
-static sh_opcode_info *get_specific
-  PARAMS ((sh_opcode_info *, sh_operand_info *));
-static void insert PARAMS ((char *, int, int, sh_operand_info *));
-static void build_relax PARAMS ((sh_opcode_info *, sh_operand_info *));
-static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
-static unsigned int build_Mytes
-  PARAMS ((sh_opcode_info *, sh_operand_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
 
 static void
-big (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+big (int ignore ATTRIBUTE_UNUSED)
 {
   if (! target_big_endian)
     as_bad (_("directive .big encountered when option -big required"));
@@ -93,8 +68,7 @@ big (ignore)
 }
 
 static void
-little (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+little (int ignore ATTRIBUTE_UNUSED)
 {
   if (target_big_endian)
     as_bad (_("directive .little encountered when option -little required"));
@@ -134,28 +108,47 @@ const pseudo_typeS md_pseudo_table[] =
   {"2byte", s_uacons, 2},
   {"4byte", s_uacons, 4},
   {"8byte", s_uacons, 8},
-#ifdef BFD_ASSEMBLER
-  {"file", dwarf2_directive_file, 0 },
-  {"loc", dwarf2_directive_loc, 0 },
-#endif
+#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}
 };
 
-/*int md_reloc_size; */
-
 int sh_relax;          /* set if -relax seen */
 
 /* Whether -small was seen.  */
 
 int sh_small;
 
-/* Whether -dsp was seen.  */
+/* Flag to generate relocations against symbol values for local symbols.  */
+
+static int dont_adjust_reloc_32;
+
+/* Flag to indicate that '$' is allowed as a register prefix.  */
+
+static int allow_dollar_register_prefix;
 
-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;
+
+#ifdef OBJ_ELF
+/* Whether --fdpic was given.  */
+static int sh_fdpic;
+#endif
 
 const char EXP_CHARS[] = "eE";
 
@@ -169,12 +162,38 @@ 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
@@ -184,6 +203,24 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #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
@@ -214,6 +251,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)] = {
@@ -254,6 +370,118 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { 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
@@ -262,90 +490,341 @@ 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;
-
-#define MAP(str,reloc) { str, sizeof (str)-1, reloc }
-
-  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 }
-  };
-
-  if (*str++ != '@')
-    return BFD_RELOC_UNUSED;
-
-  for (ch = *str, str2 = ident;
-       (str2 < ident + sizeof (ident) - 1
-       && (ISALNUM (ch) || ch == '@'));
-       ch = *++str)
-    *str2++ = TOLOWER (ch);
-
-  *str2 = '\0';
-  len = str2 - ident;
-
-  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 (! 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;
+
+  /* This is here for backward-compatibility only.  GCC used to generated:
+
+       f@PLT + . - (.LPCS# + 2)
+
+     but we'd rather be able to handle this as a PIC-related reference
+     plus/minus a symbol.  However, gas' parser gives us:
+
+       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)
+    {
+      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;
+
+  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;
+    }
+
+  if (exp->X_op == O_PIC_reloc)
+    {
+      switch (*r_type_p)
+       {
+       case BFD_RELOC_NONE:
+       case BFD_RELOC_UNUSED:
+         *r_type_p = exp->X_md;
+         break;
+
+       case BFD_RELOC_SH_DISP20:
+         switch (exp->X_md)
+           {
+           case BFD_RELOC_32_GOT_PCREL:
+             *r_type_p = BFD_RELOC_SH_GOT20;
+             break;
+
+           case BFD_RELOC_32_GOTOFF:
+             *r_type_p = BFD_RELOC_SH_GOTOFF20;
+             break;
+
+           case BFD_RELOC_SH_GOTFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
+             break;
+
+           case BFD_RELOC_SH_GOTOFFFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
+             break;
+
+           default:
+             abort ();
+           }
+         break;
+
+#ifdef HAVE_SH64
+       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;
+#endif
+
+       default:
+         abort ();
+       }
+      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;
+
+      case 2:
+       r_type = BFD_RELOC_16;
+       break;
+
+      case 4:
+       r_type = BFD_RELOC_32;
+       break;
+
+      case 8:
+       r_type = BFD_RELOC_64;
+       break;
 
-       *str_p = str;
-       return ptr->reloc;
+      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 ())
     {
@@ -353,82 +832,14 @@ 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++ == ',');
 
@@ -440,40 +851,128 @@ sh_elf_cons (nbytes)
   else
     demand_empty_rest_of_line ();
 }
-#endif /* OBJ_ELF */
 
+/* The regular frag_offset_fixed_p doesn't work for rs_align_test
+   frags.  */
+
+static bfd_boolean
+align_test_frag_offset_fixed_p (const fragS *frag1, const fragS *frag2,
+                               bfd_vma *offset)
+{
+  const fragS *frag;
+  bfd_vma off;
+
+  /* Start with offset initialised to difference between the two frags.
+     Prior to assigning frag addresses this will be zero.  */
+  off = frag1->fr_address - frag2->fr_address;
+  if (frag1 == frag2)
+    {
+      *offset = off;
+      return TRUE;
+    }
+
+  /* Maybe frag2 is after frag1.  */
+  frag = frag1;
+  while (frag->fr_type == rs_fill
+        || frag->fr_type == rs_align_test)
+    {
+      if (frag->fr_type == rs_fill)
+       off += frag->fr_fix + frag->fr_offset * frag->fr_var;
+      else
+       off += frag->fr_fix;
+      frag = frag->fr_next;
+      if (frag == NULL)
+       break;
+      if (frag == frag2)
+       {
+         *offset = off;
+         return TRUE;
+       }
+    }
+
+  /* Maybe frag1 is after frag2.  */
+  off = frag1->fr_address - frag2->fr_address;
+  frag = frag2;
+  while (frag->fr_type == rs_fill
+        || frag->fr_type == rs_align_test)
+    {
+      if (frag->fr_type == rs_fill)
+       off -= frag->fr_fix + frag->fr_offset * frag->fr_var;
+      else
+       off -= frag->fr_fix;
+      frag = frag->fr_next;
+      if (frag == NULL)
+       break;
+      if (frag == frag1)
+       {
+         *offset = off;
+         return TRUE;
+       }
+    }
+
+  return FALSE;
+}
+
+/* Optimize a difference of symbols which have rs_align_test frag if
+   possible.  */
+
+int
+sh_optimize_expr (expressionS *l, operatorT op, expressionS *r)
+{
+  bfd_vma frag_off;
+
+  if (op == O_subtract
+      && l->X_op == O_symbol
+      && r->X_op == O_symbol
+      && S_GET_SEGMENT (l->X_add_symbol) == S_GET_SEGMENT (r->X_add_symbol)
+      && (SEG_NORMAL (S_GET_SEGMENT (l->X_add_symbol))
+         || r->X_add_symbol == l->X_add_symbol)
+      && align_test_frag_offset_fixed_p (symbol_get_frag (l->X_add_symbol),
+                                        symbol_get_frag (r->X_add_symbol),
+                                        &frag_off))
+    {
+      l->X_add_number -= r->X_add_number;
+      l->X_add_number -= frag_off / OCTETS_PER_BYTE;
+      l->X_add_number += (S_GET_VALUE (l->X_add_symbol)
+                         - S_GET_VALUE (r->X_add_symbol));
+      l->X_op = O_constant;
+      l->X_add_symbol = 0;
+      return 1;
+    }
+  return 0;
+}
+#endif /* OBJ_ELF */
 \f
 /* This function is called once, at assembler startup time.  This should
    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;
+  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_sh_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;
-       }
     }
 }
 
@@ -487,11 +986,8 @@ static int reg_b;
 
 /* 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;
+static unsigned int
+parse_reg_without_prefix (char *src, int *mode, int *reg)
 {
   char l0 = TOLOWER (src[0]);
   char l1 = l0 ? TOLOWER (src[1]) : 0;
@@ -597,7 +1093,7 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[3]))
+  if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       if (l1 == 's')
        {
@@ -717,6 +1213,12 @@ parse_reg (src, mode, reg)
       return 3;
     }
 
+  if (l0 == 't' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
+    {
+      *mode = A_TBR;
+      return 3;
+    }
   if (l0 == 'm' && l1 == 'a' && TOLOWER (src[2]) == 'c'
       && ! IDENT_CHAR ((unsigned char) src[4]))
     {
@@ -732,7 +1234,7 @@ parse_reg (src, mode, reg)
        }
     }
   if (l0 == 'm' && l1 == 'o' && TOLOWER (src[2]) == 'd'
-      && ! IDENT_CHAR ((unsigned char) src[4]))
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_MOD;
       return 3;
@@ -840,36 +1342,50 @@ parse_reg (src, mode, reg)
   return 0;
 }
 
-static symbolS *
-dot ()
+/* Like parse_reg_without_prefix, but this version supports
+   $-prefixed register names if enabled by the user.  */
+
+static unsigned int
+parse_reg (char *src, int *mode, int *reg)
 {
-  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);
+  unsigned int prefix;
+  unsigned int consumed;
+
+  if (src[0] == '$')
+    {
+      if (allow_dollar_register_prefix)
+       {
+         src ++;
+         prefix = 1;
+       }
+      else
+       return 0;
+    }
+  else
+    prefix = 0;
+  
+  consumed = parse_reg_without_prefix (src, mode, reg);
+
+  if (consumed == 0)
+    return 0;
+
+  return consumed + prefix;
 }
 
 static char *
-parse_exp (s, op)
-     char *s;
-     sh_operand_info *op;
+parse_exp (char *s, sh_operand_info *op)
 {
   char *save;
-  char *new;
+  char *new_pointer;
 
   save = input_line_pointer;
   input_line_pointer = s;
   expression (&op->immediate);
   if (op->immediate.X_op == O_absent)
     as_bad (_("missing operand"));
-  new = input_line_pointer;
+  new_pointer = input_line_pointer;
   input_line_pointer = save;
-  return new;
+  return new_pointer;
 }
 
 /* The many forms of operand:
@@ -892,14 +1408,20 @@ 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;
   src++;
-  if (src[0] == '-')
+  if (src[0] == '@')
+    {
+      src = parse_at (src, op);
+      if (op->type == A_DISP_TBR)
+       op->type = A_DISP2_TBR;
+      else
+       as_bad (_("illegal double indirection"));
+    }
+  else if (src[0] == '-')
     {
       /* Must be predecrement.  */
       src++;
@@ -914,7 +1436,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)
@@ -925,29 +1447,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
+           {
+             len = 0;
            }
-         else if (mode == A_REG_N)
+         if (len)
            {
-             op->type = A_IND_R0_REG_N;
+             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)
            {
@@ -959,15 +1494,39 @@ parse_at (src, op)
                {
                  op->type = A_DISP_GBR;
                }
+             else if (mode == A_TBR)
+               {
+                 op->type = A_DISP_TBR;
+               }
              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->type = A_DISP_PC;
+                     as_warn (_("Deprecated syntax."));
+                   }
+                 else
                    {
-                     op->immediate.X_add_symbol = dot();
-                     op->immediate.X_op = O_symbol;
+                     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
                {
@@ -989,39 +1548,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;
@@ -1057,10 +1616,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])
@@ -1118,9 +1674,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;
@@ -1129,7 +1683,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.  */
@@ -1147,11 +1701,15 @@ 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_DISP2_TBR:
            case A_MACH:
            case A_PR:
            case A_MACL:
@@ -1184,8 +1742,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)
@@ -1198,6 +1754,7 @@ get_specific (opcode, operands)
              reg_n = user->reg;
              break;
            case A_GBR:
+           case A_TBR:
            case A_SR:
            case A_VBR:
            case A_DSR:
@@ -1218,17 +1775,264 @@ get_specific (opcode, operands)
              reg_b = user->reg;
              break;
 
-           case A_REG_M:
-           case A_INC_M:
-           case A_DEC_M:
-           case A_IND_M:
-           case A_IND_R0_REG_M:
-           case A_DISP_REG_M:
-           case DSP_REG_M:
-             /* Opcode needs rn */
-             if (user->type != arg - A_REG_M + A_REG_N)
+           case A_INC_R15:
+             if (user->type != A_INC_N)
+               goto fail;
+             if (user->reg != 15)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case A_DEC_R15:
+             if (user->type != A_DEC_N)
+               goto fail;
+             if (user->reg != 15)
+               goto fail;
+             reg_n = user->reg;
+             break;
+
+           case A_REG_M:
+           case A_INC_M:
+           case A_DEC_M:
+           case A_IND_M:
+           case A_IND_R0_REG_M:
+           case A_DISP_REG_M:
+           case DSP_REG_M:
+             /* Opcode needs rn */
+             if (user->type != arg - A_REG_M + A_REG_N)
+               goto fail;
+             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;
-             reg_m = user->reg;
+             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:
@@ -1388,10 +2192,40 @@ get_specific (opcode, operands)
              printf (_("unhandled %d\n"), arg);
              goto fail;
            }
+         if (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh2a_nofpu_up)
+             && (   arg == A_DISP_REG_M
+                 || arg == A_DISP_REG_N))
+           {
+             /* Check a few key IMM* fields for overflow.  */
+             int opf;
+             long val = user->immediate.X_add_number;
+
+             for (opf = 0; opf < 4; opf ++)
+               switch (this_try->nibbles[opf])
+                 {
+                 case IMM0_4:
+                 case IMM1_4:
+                   if (val < 0 || val > 15)
+                     goto fail;
+                   break;
+                 case IMM0_4BY2:
+                 case IMM1_4BY2:
+                   if (val < 0 || val > 15 * 2)
+                     goto fail;
+                   break;
+                 case IMM0_4BY4:
+                 case IMM1_4BY4:
+                   if (val < 0 || val > 15 * 4)
+                     goto fail;
+                   break;
+                 default:
+                   break;
+                 }
+           }
        }
-      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:
       ;
@@ -1401,11 +2235,7 @@ get_specific (opcode, operands)
 }
 
 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,
@@ -1416,9 +2246,17 @@ insert (where, how, pcrel, op)
 }
 
 static void
-build_relax (opcode, op)
-     sh_opcode_info *opcode;
-     sh_operand_info *op;
+insert4 (char * where, int how, int pcrel, sh_operand_info * op)
+{
+  fix_new_exp (frag_now,
+              where - frag_now->fr_literal,
+              4,
+              & op->immediate,
+              pcrel,
+              how);
+}
+static void
+build_relax (sh_opcode_info *opcode, sh_operand_info *op)
 {
   int high_byte = target_big_endian ? 0 : 1;
   char *p;
@@ -1452,9 +2290,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;
@@ -1502,47 +2338,126 @@ 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];
-  char *output = frag_more (2);
+  int indx;
+  char nbuf[8];
+  char *output;
   unsigned int size = 2;
   int low_byte = target_big_endian ? 1 : 0;
+  int max_index = 4;
+  bfd_reloc_code_real_type r_type;
+#ifdef OBJ_ELF
+  int unhandled_pic = 0;
+#endif
+
   nbuf[0] = 0;
   nbuf[1] = 0;
   nbuf[2] = 0;
   nbuf[3] = 0;
+  nbuf[4] = 0;
+  nbuf[5] = 0;
+  nbuf[6] = 0;
+  nbuf[7] = 0;
+
+#ifdef OBJ_ELF
+  for (indx = 0; indx < 3; indx++)
+    if (opcode->arg[indx] == A_IMM
+       && operand[indx].type == A_IMM
+       && (operand[indx].immediate.X_op == O_PIC_reloc
+           || sh_PIC_related_p (operand[indx].immediate.X_add_symbol)
+           || sh_PIC_related_p (operand[indx].immediate.X_op_symbol)))
+      unhandled_pic = 1;
+#endif
+
+  if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+    {
+      output = frag_more (4);
+      size = 4;
+      max_index = 8;
+    }
+  else
+    output = frag_more (2);
 
-  for (index = 0; index < 4; index++)
+  for (indx = 0; indx < max_index; indx++)
     {
-      sh_nibble_type i = opcode->nibbles[index];
+      sh_nibble_type i = opcode->nibbles[indx];
       if (i < 16)
        {
-         nbuf[index] = i;
+         nbuf[indx] = i;
        }
       else
        {
          switch (i)
            {
            case REG_N:
-             nbuf[index] = reg_n;
+           case REG_N_D:
+             nbuf[indx] = reg_n;
              break;
            case REG_M:
-             nbuf[index] = reg_m;
+             nbuf[indx] = reg_m;
              break;
            case SDT_REG_N:
              if (reg_n < 2 || reg_n > 5)
                as_bad (_("Invalid register: 'r%d'"), reg_n);
-             nbuf[index] = (reg_n & 3) | 4;
+             nbuf[indx] = (reg_n & 3) | 4;
              break;
            case REG_NM:
-             nbuf[index] = reg_n | (reg_m >> 2);
+             nbuf[indx] = reg_n | (reg_m >> 2);
              break;
            case REG_B:
-             nbuf[index] = reg_b | 0x08;
+             nbuf[indx] = reg_b | 0x08;
+             break;
+           case REG_N_B01:
+             nbuf[indx] = reg_n | 0x01;
+             break;
+           case IMM0_3s:
+             nbuf[indx] |= 0x08;
+           case IMM0_3c:
+             insert (output + low_byte, BFD_RELOC_SH_IMM3, 0, operand);
+             break;
+           case IMM0_3Us:
+             nbuf[indx] |= 0x80;
+           case IMM0_3Uc:
+             insert (output + low_byte, BFD_RELOC_SH_IMM3U, 0, operand);
+             break;
+           case DISP0_12:
+             insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand);
+             break;
+           case DISP0_12BY2:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand);
+             break;
+           case DISP0_12BY4:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand);
+             break;
+           case DISP0_12BY8:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand);
+             break;
+           case DISP1_12:
+             insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand+1);
+             break;
+           case DISP1_12BY2:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand+1);
+             break;
+           case DISP1_12BY4:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand+1);
+             break;
+           case DISP1_12BY8:
+             insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand+1);
+             break;
+           case IMM0_20_4:
+             break;
+           case IMM0_20:
+             r_type = BFD_RELOC_SH_DISP20;
+#ifdef OBJ_ELF
+             if (sh_check_fixup (&operand->immediate, &r_type))
+               as_bad (_("Invalid PIC expression."));
+             unhandled_pic = 0;
+#endif
+             insert4 (output, r_type, 0, operand);
+             break;
+           case IMM0_20BY8:
+             insert4 (output, BFD_RELOC_SH_DISP20BY8, 0, operand);
              break;
            case IMM0_4BY4:
              insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
@@ -1581,14 +2496,16 @@ 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);
-             nbuf[index] = opcode->nibbles[3];
+             nbuf[indx] = opcode->nibbles[3];
              operand += 2;
              break;
            default:
@@ -1596,6 +2513,10 @@ build_Mytes (opcode, operand)
            }
        }
     }
+#ifdef OBJ_ELF
+  if (unhandled_pic)
+    as_bad (_("misplaced PIC operand"));
+#endif
   if (!target_big_endian)
     {
       output[1] = (nbuf[0] << 4) | (nbuf[1]);
@@ -1606,6 +2527,19 @@ build_Mytes (opcode, operand)
       output[0] = (nbuf[0] << 4) | (nbuf[1]);
       output[1] = (nbuf[2] << 4) | (nbuf[3]);
     }
+  if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+    {
+      if (!target_big_endian)
+       {
+         output[3] = (nbuf[4] << 4) | (nbuf[5]);
+         output[2] = (nbuf[6] << 4) | (nbuf[7]);
+       }
+      else
+       {
+         output[2] = (nbuf[4] << 4) | (nbuf[5]);
+         output[3] = (nbuf[6] << 4) | (nbuf[7]);
+       }
+    }
   return size;
 }
 
@@ -1613,14 +2547,13 @@ 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;
   unsigned char *op_end;
   char name[20];
-  int nlen = 0;
+  unsigned int nlen = 0;
 
   /* Drop leading whitespace.  */
   while (*str == ' ')
@@ -1630,9 +2563,9 @@ find_cooked_opcode (str_p)
      The pre-processor will eliminate whitespace in front of
      any '@' after the first argument; we may be called from
      assemble_ppi, so the opcode might be terminated by an '@'.  */
-  for (op_start = op_end = (unsigned char *) (str);
+  for (op_start = op_end = (unsigned char *) str;
        *op_end
-       && nlen < 20
+       && nlen < sizeof (name) - 1
        && !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@';
        op_end++)
     {
@@ -1649,7 +2582,7 @@ find_cooked_opcode (str_p)
     }
 
   name[nlen] = 0;
-  *str_p = op_end;
+  *str_p = (char *) op_end;
 
   if (nlen == 0)
     as_bad (_("can't find opcode "));
@@ -1661,9 +2594,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;
@@ -1673,17 +2604,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)
        {
@@ -1714,9 +2647,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)
@@ -1738,6 +2705,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,
@@ -1766,11 +2735,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)
@@ -1785,13 +2774,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:
@@ -1807,8 +2817,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;
@@ -1876,20 +2895,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;
+  char *op_end;
   sh_operand_info operand[3];
   sh_opcode_info *opcode;
   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 definitely 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;
     }
 
@@ -1912,8 +2990,17 @@ 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]);
+
+         /* All branches are currently 16 bit.  */
+         size = 2;
        }
       else
        {
@@ -1950,16 +3037,14 @@ 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 (symbolS *sym)
 {
   static fragS *last_label_frag;
   static int last_label_offset;
@@ -1978,13 +3063,15 @@ sh_frob_label ()
          last_label_offset = offset;
        }
     }
+
+  dwarf2_emit_label (sym);
 }
 
 /* This routine is called when the assembler is about to output some
    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)
@@ -1996,109 +3083,17 @@ 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;
 }
 
-#ifdef OBJ_COFF
-#ifndef BFD_ASSEMBLER
-
-void
-tc_crawl_symbol_chain (headers)
-     object_headers *headers;
-{
-  printf (_("call to tc_crawl_symbol_chain \n"));
-}
-
-void
-tc_headers_hook (headers)
-     object_headers *headers;
-{
-  printf (_("call to tc_headers_hook \n"));
-}
-
-#endif
-#endif
-
 /* Various routines to kill one day.  */
-/* Equal to MAX_PRECISION in atof-ieee.c.  */
-#define MAX_LITTLENUMS 6
-
-/* Turn a string in input_line_pointer into a floating point constant
-   of type TYPE, and store the appropriate bytes in *LITP.  The number
-   of LITTLENUMS emitted is stored in *SIZEP .  An error message is
-   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];
-  char *t;
-  int i;
-
-  switch (type)
-    {
-    case 'f':
-      prec = 2;
-      break;
-
-    case 'd':
-      prec = 4;
-      break;
-
-    default:
-      *sizeP = 0;
-      return _("bad call to md_atof");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-
-  *sizeP = prec * 2;
-
-  if (! target_big_endian)
-    {
-      for (i = prec - 1; i >= 0; i--)
-       {
-         md_number_to_chars (litP, (valueT) words[i], 2);
-         litP += 2;
-       }
-    }
-  else
-    {
-      for (i = 0; i < prec; i++)
-       {
-         md_number_to_chars (litP, (valueT) words[i], 2);
-         litP += 2;
-       }
-    }
-
-  return NULL;
+  return ieee_md_atof (type, litP, sizeP, target_big_endian);
 }
 
 /* Handle the .uses pseudo-op.  This pseudo-op is used just before a
@@ -2107,8 +3102,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;
 
@@ -2129,50 +3123,197 @@ s_uses (ignore)
   demand_empty_rest_of_line ();
 }
 \f
-CONST char *md_shortopts = "";
-struct option md_longopts[] =
+enum options
 {
-#define OPTION_RELAX  (OPTION_MD_BASE)
-#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)
+  OPTION_RELAX = OPTION_MD_BASE,
+  OPTION_BIG,
+  OPTION_LITTLE,
+  OPTION_SMALL,
+  OPTION_DSP,
+  OPTION_ISA,
+  OPTION_RENESAS,
+  OPTION_ALLOW_REG_PREFIX,
+#ifdef HAVE_SH64
+  OPTION_ABI,
+  OPTION_NO_MIX,
+  OPTION_SHCOMPACT_CONST_CRANGE,
+  OPTION_NO_EXPAND,
+  OPTION_PT32,
+#endif
+  OPTION_H_TICK_HEX,
+#ifdef OBJ_ELF
+  OPTION_FDPIC,
+#endif
+  OPTION_DUMMY  /* Not used.  This is just here to make it easy to add and subtract options from this enum.  */
+};
 
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
   {"relax", no_argument, NULL, OPTION_RELAX},
   {"big", no_argument, NULL, OPTION_BIG},
   {"little", no_argument, NULL, OPTION_LITTLE},
+  /* The next two switches are here because the
+     generic parts of the linker testsuite uses them.  */
+  {"EB", no_argument, NULL, OPTION_BIG},
+  {"EL", 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},
+  {"allow-reg-prefix", no_argument, NULL, OPTION_ALLOW_REG_PREFIX},
+
+#ifdef HAVE_SH64
+  {"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 */
+  { "h-tick-hex", no_argument,       NULL, OPTION_H_TICK_HEX  },
+
+#ifdef OBJ_ELF
+  {"fdpic", no_argument, NULL, OPTION_FDPIC},
+#endif
+
   {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;
-{
-  switch (c)
-    {
-    case OPTION_RELAX:
-      sh_relax = 1;
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+  switch (c)
+    {
+    case OPTION_RELAX:
+      sh_relax = 1;
+      break;
+
+    case OPTION_BIG:
+      target_big_endian = 1;
+      break;
+
+    case OPTION_LITTLE:
+      target_big_endian = 0;
+      break;
+
+    case OPTION_SMALL:
+      sh_small = 1;
+      break;
+
+    case OPTION_DSP:
+      preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+      break;
+
+    case OPTION_RENESAS:
+      dont_adjust_reloc_32 = 1;
+      break;
+
+    case OPTION_ALLOW_REG_PREFIX:
+      allow_dollar_register_prefix = 1;
+      break;
+
+    case OPTION_ISA:
+      if (strcasecmp (arg, "dsp") == 0)
+       preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+      else if (strcasecmp (arg, "fp") == 0)
+       preset_target_arch = arch_sh_up & ~arch_sh_has_dsp;
+      else if (strcasecmp (arg, "any") == 0)
+       preset_target_arch = arch_sh_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;
+         bfd_arch_info_type const *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_BIG:
-      target_big_endian = 1;
+    case OPTION_NO_EXPAND:
+      sh64_expand = FALSE;
       break;
 
-    case OPTION_LITTLE:
-      target_big_endian = 0;
+    case OPTION_PT32:
+      sh64_pt32 = TRUE;
       break;
+#endif /* HAVE_SH64 */
 
-    case OPTION_SMALL:
-      sh_small = 1;
+    case OPTION_H_TICK_HEX:
+      enable_h_tick_hex = 1;
       break;
 
-    case OPTION_DSP:
-      sh_dsp = 1;
+#ifdef OBJ_ELF
+    case OPTION_FDPIC:
+      sh_fdpic = TRUE;
       break;
+#endif /* OBJ_ELF */
 
     default:
       return 0;
@@ -2182,16 +3323,54 @@ 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\
--small                 align sections to 4 byte boundaries, not 16\n\
--dsp                   enable sh-dsp insns, and disable sh3e / sh4 insns.\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 floating-point ISAs.\n\
+--allow-reg-prefix     allow '$' as a register name prefix.\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 const *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 */
+#ifdef OBJ_ELF
+  fprintf (stream, _("\
+--fdpic                        generate an FDPIC object file\n"));
+#endif /* OBJ_ELF */
 }
 \f
 /* This struct is used to pass arguments to sh_count_relocs through
@@ -2206,14 +3385,10 @@ struct sh_count_relocs
 };
 
 /* Count the number of fixups in a section which refer to a particular
-   symbol.  When using BFD_ASSEMBLER, this is called via
-   bfd_map_over_sections.  */
+   symbol.  This is called via 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;
@@ -2235,14 +3410,12 @@ sh_count_relocs (abfd, sec, data)
     }
 }
 
-/* Handle the count relocs for a particular section.  When using
-   BFD_ASSEMBLER, this is called via bfd_map_over_sections.  */
+/* Handle the count relocs for a particular section.
+   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;
@@ -2251,6 +3424,21 @@ sh_frob_section (abfd, sec, ignore)
   if (seginfo == NULL)
     return;
 
+  for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+    {
+      symbolS *sym;
+
+      sym = fix->fx_addsy;
+      /* Check for a local_symbol.  */
+      if (sym && sym->bsym == NULL)
+       {
+         struct local_symbol *ls = (struct local_symbol *)sym;
+         /* See if it's been converted.  If so, canonicalize.  */
+         if (local_symbol_converted_p (ls))
+           fix->fx_addsy = local_symbol_get_real_symbol (ls);
+       }
+    }
+
   for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
     {
       symbolS *sym;
@@ -2268,9 +3456,6 @@ sh_frob_section (abfd, sec, ignore)
          || fix->fx_subsy != NULL
          || fix->fx_addnumber != 0
          || S_GET_SEGMENT (sym) != sec
-#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
-         || S_GET_STORAGE_CLASS (sym) == C_EXT
-#endif
          || S_IS_EXTERNAL (sym))
        {
          as_warn_where (fix->fx_file, fix->fx_line,
@@ -2310,9 +3495,6 @@ sh_frob_section (abfd, sec, ignore)
          || fscan->fx_subsy != NULL
          || fscan->fx_addnumber != 0
          || S_GET_SEGMENT (sym) != sec
-#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
-         || S_GET_STORAGE_CLASS (sym) == C_EXT
-#endif
          || S_IS_EXTERNAL (sym))
        {
          as_warn_where (fix->fx_file, fix->fx_line,
@@ -2324,16 +3506,7 @@ sh_frob_section (abfd, sec, ignore)
         counting the number of times we find a reference to sym.  */
       info.sym = sym;
       info.count = 0;
-#ifdef BFD_ASSEMBLER
-      bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);
-#else
-      {
-       int iscan;
-
-       for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
-         sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info);
-      }
-#endif
+      bfd_map_over_sections (stdoutput, sh_count_relocs, &info);
 
       if (info.count < 1)
        abort ();
@@ -2359,35 +3532,23 @@ 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);
-#else
-  {
-    int iseg;
-
-    for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
-      sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL);
-  }
-#endif
+  bfd_map_over_sections (stdoutput, sh_frob_section, NULL);
 }
 
 /* Called after relaxing.  Set the correct sizes of the fragments, and
    create relocs so that md_apply_fix will fill in the correct values.  */
 
 void
-md_convert_frag (headers, seg, fragP)
-#ifdef BFD_ASSEMBLER
-     bfd *headers ATTRIBUTE_UNUSED;
-#else
-     object_headers *headers;
-#endif
-     segT seg;
-     fragS *fragP;
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg, fragS *fragP)
 {
   int donerelax = 0;
 
@@ -2452,7 +3613,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 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
@@ -2460,12 +3621,7 @@ md_convert_frag (headers, seg, fragP)
 
        /* Build a relocation to six / four bytes farther on.  */
        subseg_change (seg, 0);
-       fix_new (fragP, fragP->fr_fix, 2,
-#ifdef BFD_ASSEMBLER
-                section_symbol (seg),
-#else
-                seg_info (seg)->dot,
-#endif
+       fix_new (fragP, fragP->fr_fix, 2, section_symbol (seg),
                 fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6),
                 1, BFD_RELOC_SH_PCDISP8BY2);
 
@@ -2514,7 +3670,11 @@ md_convert_frag (headers, seg, fragP)
       break;
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_convert_frag (headers, seg, fragP, TRUE);
+#else
       abort ();
+#endif
     }
 
   if (donerelax && !sh_relax)
@@ -2526,25 +3686,18 @@ md_convert_frag (headers, seg, fragP)
 }
 
 valueT
-md_section_align (seg, size)
-     segT seg ATTRIBUTE_UNUSED;
-     valueT size;
+md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
 {
-#ifdef BFD_ASSEMBLER
 #ifdef OBJ_ELF
   return size;
 #else /* ! OBJ_ELF */
   return ((size + (1 << bfd_get_section_alignment (stdoutput, seg)) - 1)
          & (-1 << bfd_get_section_alignment (stdoutput, seg)));
 #endif /* ! OBJ_ELF */
-#else /* ! BFD_ASSEMBLER */
-  return ((size + (1 << section_alignment[(int) seg]) - 1)
-         & (-1 << section_alignment[(int) seg]));
-#endif /* ! BFD_ASSEMBLER */
 }
 
 /* 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;
 
@@ -2553,8 +3706,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;
@@ -2568,11 +3720,9 @@ s_uacons (bytes)
    enable this warning?  */
 
 void
-sh_cons_align (nbytes)
-     int nbytes;
+sh_cons_align (int nbytes)
 {
   int nalign;
-  char *p;
 
   if (sh_no_align_cons)
     {
@@ -2598,8 +3748,8 @@ sh_cons_align (nbytes)
       return;
     }
 
-  p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
-               (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+  frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+           (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
 
   record_alignment (now_seg, nalign);
 }
@@ -2609,8 +3759,7 @@ 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;
 
@@ -2642,7 +3791,7 @@ sh_handle_align (frag)
   else if (frag->fr_type == rs_align_test)
     {
       if (bytes != 0)
-       as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+       as_bad_where (frag->fr_file, frag->fr_line, _("misaligned data"));
     }
 
   if (sh_relax
@@ -2655,44 +3804,42 @@ sh_handle_align (frag)
             BFD_RELOC_SH_ALIGN);
 }
 
-/* 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.  */
+/* See whether the relocation should be resolved locally.  */
 
-#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
-
-#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)
@@ -2704,29 +3851,26 @@ 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_GOT20
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC20
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC20
+      || fixP->fx_r_type == BFD_RELOC_SH_FUNCDESC
+      || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
       || fixP->fx_r_type == BFD_RELOC_RVA)
     return 0;
 
@@ -2739,63 +3883,94 @@ 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 ();
+#elif defined TARGET_SYMBIAN
+    if (1)
+      {
+       extern int sh_symbian_find_elf_flags (unsigned int);
+
+       val = sh_symbian_find_elf_flags (valid_arch);
+      }
+    else
+#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;
+
+  if (sh_fdpic)
+    elf_elfheader (stdoutput)->e_flags |= EF_SH_FDPIC;
 }
 #endif
 
+#ifdef TE_UCLINUX
+/* Return the target format for uClinux.  */
+
+const char *
+sh_uclinux_target_format (void)
+{
+  if (sh_fdpic)
+    return (!target_big_endian ? "elf32-sh-fdpic" : "elf32-shbig-fdpic");
+  else
+    return (!target_big_endian ? "elf32-shl" : "elf32-sh");
+}
+#endif
+
+/* Apply fixup FIXP to SIZE-byte field BUF given that VAL is its
+   assembly-time value.  If we're generating a reloc for FIXP,
+   see whether the addend should be stored in-place or whether
+   it should be in an ELF r_addend field.  */
+
+static void
+apply_full_field_fix (fixS *fixP, char *buf, bfd_vma val, int size)
+{
+  reloc_howto_type *howto;
+
+  if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
+    {
+      howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+      if (howto && !howto->partial_inplace)
+       {
+         fixP->fx_addnumber = val;
+         return;
+       }
+    }
+  md_number_to_chars (buf, val, size);
+}
+
 /* 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_fix (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;
 
-#ifdef BFD_ASSEMBLER
   /* A difference between two symbols, the second of which is in the
      current section, is transformed in a PC-relative relocation to
      the other symbol.  We have to adjust the relocation type here.  */
   if (fixP->fx_pcrel)
     {
+#ifndef HAVE_SH64
+      /* Safeguard; this must not occur for non-sh64 configurations.  */
+      gas_assert (fixP->fx_r_type != BFD_RELOC_64);
+#endif
+
       switch (fixP->fx_r_type)
        {
        default:
@@ -2812,11 +3987,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;
        }
     }
 
@@ -2836,26 +4011,65 @@ md_apply_fix (fixP, val)
       && fixP->fx_addsy != NULL
       && S_IS_WEAK (fixP->fx_addsy))
     val -= S_GET_VALUE  (fixP->fx_addsy);
-#endif
 
-#ifndef BFD_ASSEMBLER
-  if (fixP->fx_r_type == 0)
-    {
-      if (fixP->fx_size == 2)
-       fixP->fx_r_type = BFD_RELOC_16;
-      else if (fixP->fx_size == 4)
-       fixP->fx_r_type = BFD_RELOC_32;
-      else if (fixP->fx_size == 1)
-       fixP->fx_r_type = BFD_RELOC_8;
-      else
-       abort ();
-    }
-#endif
+  if (SWITCH_TABLE (fixP))
+    val -= S_GET_VALUE  (fixP->fx_subsy);
 
   max = min = 0;
   shift = 0;
   switch (fixP->fx_r_type)
     {
+    case BFD_RELOC_SH_IMM3:
+      max = 0x7;
+      * buf = (* buf & 0xf8) | (val & 0x7);
+      break;
+    case BFD_RELOC_SH_IMM3U:
+      max = 0x7;
+      * buf = (* buf & 0x8f) | ((val & 0x7) << 4);
+      break;
+    case BFD_RELOC_SH_DISP12:
+      max = 0xfff;
+      buf[lowbyte] = val & 0xff;
+      buf[highbyte] |= (val >> 8) & 0x0f;
+      break;
+    case BFD_RELOC_SH_DISP12BY2:
+      max = 0xfff;
+      shift = 1;
+      buf[lowbyte] = (val >> 1) & 0xff;
+      buf[highbyte] |= (val >> 9) & 0x0f;
+      break;
+    case BFD_RELOC_SH_DISP12BY4:
+      max = 0xfff;
+      shift = 2;
+      buf[lowbyte] = (val >> 2) & 0xff;
+      buf[highbyte] |= (val >> 10) & 0x0f;
+      break;
+    case BFD_RELOC_SH_DISP12BY8:
+      max = 0xfff;
+      shift = 3;
+      buf[lowbyte] = (val >> 3) & 0xff;
+      buf[highbyte] |= (val >> 11) & 0x0f;
+      break;
+    case BFD_RELOC_SH_DISP20:
+      if (! target_big_endian)
+       abort();
+      max = 0x7ffff;
+      min = -0x80000;
+      buf[1] = (buf[1] & 0x0f) | ((val >> 12) & 0xf0);
+      buf[2] = (val >> 8) & 0xff;
+      buf[3] = val & 0xff;
+      break;
+    case BFD_RELOC_SH_DISP20BY8:
+      if (!target_big_endian)
+       abort();
+      max = 0x7ffff;
+      min = -0x80000;
+      shift = 8;
+      buf[1] = (buf[1] & 0x0f) | ((val >> 20) & 0xf0);
+      buf[2] = (val >> 16) & 0xff;
+      buf[3] = (val >> 8) & 0xff;
+      break;
+
     case BFD_RELOC_SH_IMM4:
       max = 0xf;
       *buf = (*buf & 0xf0) | (val & 0xf);
@@ -2897,9 +4111,26 @@ md_apply_fix (fixP, val)
       break;
 
     case BFD_RELOC_SH_PCRELIMM8BY4:
+      /* If we are dealing with a known destination ... */
+      if ((fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+         && (fixP->fx_subsy == NULL || S_IS_DEFINED (fixP->fx_addsy)))
+      {
+       /* Don't silently move the destination due to misalignment.
+          The absolute address is the fragment base plus the offset into
+          the fragment plus the pc relative offset to the label.  */
+       if ((fixP->fx_frag->fr_address + fixP->fx_where + val) & 3)
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("offset to unaligned destination"));
+
+       /* The displacement cannot be zero or backward even if aligned.
+          Allow -2 because val has already been adjusted somewhere.  */
+       if (val < -2)
+         as_bad_where (fixP->fx_file, fixP->fx_line, _("negative offset"));
+      }
+
       /* 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.
@@ -2938,17 +4169,23 @@ md_apply_fix (fixP, val)
       buf[highbyte] |= (val >> 8) & 0xf;
       break;
 
+#ifndef HAVE_SH64
+    case BFD_RELOC_64:
+      apply_full_field_fix (fixP, buf, *valP, 8);
+      break;
+#endif
+
     case BFD_RELOC_32:
     case BFD_RELOC_32_PCREL:
-      md_number_to_chars (buf, val, 4);
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 
     case BFD_RELOC_16:
-      md_number_to_chars (buf, val, 2);
+      apply_full_field_fix (fixP, buf, val, 2);
       break;
 
     case BFD_RELOC_SH_USES:
-      /* Pass the value into sh_coff_reloc_mangle.  */
+      /* Pass the value into sh_reloc().  */
       fixP->fx_addnumber = val;
       break;
 
@@ -2966,17 +4203,17 @@ 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);
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 
     case BFD_RELOC_SH_GOTPC:
@@ -2996,21 +4233,44 @@ 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;
-      md_number_to_chars (buf, val, 4);
+      * valP -= 1;
+      apply_full_field_fix (fixP, 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.  */
-      md_number_to_chars (buf, 0, 4);
+    case BFD_RELOC_SH_GOT20:
+    case BFD_RELOC_SH_GOTPLT32:
+    case BFD_RELOC_SH_GOTFUNCDESC:
+    case BFD_RELOC_SH_GOTFUNCDESC20:
+    case BFD_RELOC_SH_GOTOFFFUNCDESC:
+    case BFD_RELOC_SH_GOTOFFFUNCDESC20:
+    case BFD_RELOC_SH_FUNCDESC:
+      * valP = 0; /* Fully resolved at runtime.  No addend.  */
+      apply_full_field_fix (fixP, 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:
+    case BFD_RELOC_SH_GOTOFF20:
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 #endif
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_apply_fix (fixP, valP);
+      return;
+#else
       abort ();
+#endif
     }
 
   if (shift != 0)
@@ -3023,28 +4283,38 @@ md_apply_fix (fixP, val)
        val = ((val >> shift)
               | ((long) -1 & ~ ((long) -1 >> shift)));
     }
+
+  /* Extend sign for 64-bit host.  */
+  val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
   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
+  else if (max != 0)
+    /* Stop the generic code from trying to overlow check the value as well.
+       It may not have the correct value anyway, as we do not store val back
+       into *valP.  */
+    fixP->fx_no_overflow = 1;
+
+  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.  */
@@ -3108,26 +4378,33 @@ md_estimate_size_before_relax (fragP, segment_type)
 /* 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 eg. for the sh-hms target.  */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
 long
-md_pcrel_from_section (fixP, sec)
-     fixS *fixP;
-     segT sec;
+md_pcrel_from_section (fixS *fixP, segT sec)
 {
-  if (fixP->fx_addsy != (symbolS *) NULL
-      && (! S_IS_DEFINED (fixP->fx_addsy)
-         || S_IS_EXTERN (fixP->fx_addsy)
-         || S_IS_WEAK (fixP->fx_addsy)
+  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,
@@ -3137,180 +4414,13 @@ md_pcrel_from_section (fixP, sec)
       return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
     }
 
-  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
-}
-
-#ifdef OBJ_COFF
-
-int
-tc_coff_sizemachdep (frag)
-     fragS *frag;
-{
-  return md_relax_table[frag->fr_subtype].rlx_length;
-}
-
-#endif /* OBJ_COFF */
-
-#ifndef BFD_ASSEMBLER
-#ifdef OBJ_COFF
-
-/* Map BFD relocs to SH COFF relocs.  */
-
-struct reloc_map
-{
-  bfd_reloc_code_real_type bfd_reloc;
-  int sh_reloc;
-};
-
-static const struct reloc_map coff_reloc_map[] =
-{
-  { BFD_RELOC_32, R_SH_IMM32 },
-  { BFD_RELOC_16, R_SH_IMM16 },
-  { BFD_RELOC_8, R_SH_IMM8 },
-  { BFD_RELOC_SH_PCDISP8BY2, R_SH_PCDISP8BY2 },
-  { BFD_RELOC_SH_PCDISP12BY2, R_SH_PCDISP },
-  { BFD_RELOC_SH_IMM4, R_SH_IMM4 },
-  { BFD_RELOC_SH_IMM4BY2, R_SH_IMM4BY2 },
-  { BFD_RELOC_SH_IMM4BY4, R_SH_IMM4BY4 },
-  { BFD_RELOC_SH_IMM8, R_SH_IMM8 },
-  { BFD_RELOC_SH_IMM8BY2, R_SH_IMM8BY2 },
-  { BFD_RELOC_SH_IMM8BY4, R_SH_IMM8BY4 },
-  { BFD_RELOC_SH_PCRELIMM8BY2, R_SH_PCRELIMM8BY2 },
-  { BFD_RELOC_SH_PCRELIMM8BY4, R_SH_PCRELIMM8BY4 },
-  { BFD_RELOC_8_PCREL, R_SH_SWITCH8 },
-  { BFD_RELOC_SH_SWITCH16, R_SH_SWITCH16 },
-  { BFD_RELOC_SH_SWITCH32, R_SH_SWITCH32 },
-  { BFD_RELOC_SH_USES, R_SH_USES },
-  { BFD_RELOC_SH_COUNT, R_SH_COUNT },
-  { BFD_RELOC_SH_ALIGN, R_SH_ALIGN },
-  { BFD_RELOC_SH_CODE, R_SH_CODE },
-  { BFD_RELOC_SH_DATA, R_SH_DATA },
-  { BFD_RELOC_SH_LABEL, R_SH_LABEL },
-  { BFD_RELOC_UNUSED, 0 }
-};
-
-/* Adjust a reloc for the SH.  This is similar to the generic code,
-   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;
-{
-  symbolS *symbol_ptr = fix->fx_addsy;
-  symbolS *dot;
-
-  intr->r_vaddr = paddr + fix->fx_frag->fr_address + fix->fx_where;
-
-  if (! SWITCH_TABLE (fix))
-    {
-      const struct reloc_map *rm;
-
-      for (rm = coff_reloc_map; rm->bfd_reloc != BFD_RELOC_UNUSED; rm++)
-       if (rm->bfd_reloc == (bfd_reloc_code_real_type) fix->fx_r_type)
-         break;
-      if (rm->bfd_reloc == BFD_RELOC_UNUSED)
-       as_bad_where (fix->fx_file, fix->fx_line,
-                     _("Can not represent %s relocation in this object file format"),
-                     bfd_get_reloc_code_name (fix->fx_r_type));
-      intr->r_type = rm->sh_reloc;
-      intr->r_offset = 0;
-    }
-  else
-    {
-      know (sh_relax);
-
-      if (fix->fx_r_type == BFD_RELOC_16)
-       intr->r_type = R_SH_SWITCH16;
-      else if (fix->fx_r_type == BFD_RELOC_8)
-       intr->r_type = R_SH_SWITCH8;
-      else if (fix->fx_r_type == BFD_RELOC_32)
-       intr->r_type = R_SH_SWITCH32;
-      else
-       abort ();
-
-      /* For a switch reloc, we set r_offset to the difference between
-         the reloc address and the subtrahend.  When the linker is
-         doing relaxing, it can use the determine the starting and
-         ending points of the switch difference expression.  */
-      intr->r_offset = intr->r_vaddr - S_GET_VALUE (fix->fx_subsy);
-    }
-
-  /* PC relative relocs are always against the current section.  */
-  if (symbol_ptr == NULL)
-    {
-      switch (fix->fx_r_type)
-       {
-       case BFD_RELOC_SH_PCRELIMM8BY2:
-       case BFD_RELOC_SH_PCRELIMM8BY4:
-       case BFD_RELOC_SH_PCDISP8BY2:
-       case BFD_RELOC_SH_PCDISP12BY2:
-       case BFD_RELOC_SH_USES:
-         symbol_ptr = seg->dot;
-         break;
-       default:
-         break;
-       }
-    }
-
-  if (fix->fx_r_type == BFD_RELOC_SH_USES)
-    {
-      /* 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.  */
-      intr->r_offset = fix->fx_addnumber;
-    }
-  else if (fix->fx_r_type == BFD_RELOC_SH_COUNT)
-    {
-      /* We can't store the count in the object file, since this reloc
-         does not take up any space, so we store it in r_offset.  The
-         fx_offset field was set when the fixup was created in
-         sh_coff_frob_file.  */
-      intr->r_offset = fix->fx_offset;
-      /* This reloc is always absolute.  */
-      symbol_ptr = NULL;
-    }
-  else if (fix->fx_r_type == BFD_RELOC_SH_ALIGN)
-    {
-      /* Store the alignment in the r_offset field.  */
-      intr->r_offset = fix->fx_offset;
-      /* This reloc is always absolute.  */
-      symbol_ptr = NULL;
-    }
-  else if (fix->fx_r_type == BFD_RELOC_SH_CODE
-          || fix->fx_r_type == BFD_RELOC_SH_DATA
-          || fix->fx_r_type == BFD_RELOC_SH_LABEL)
-    {
-      /* These relocs are always absolute.  */
-      symbol_ptr = NULL;
-    }
-
-  /* Turn the segment of the symbol into an offset.  */
-  if (symbol_ptr != NULL)
-    {
-      dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
-      if (dot != NULL)
-       intr->r_symndx = dot->sy_number;
-      else
-       intr->r_symndx = symbol_ptr->sy_number;
-    }
-  else
-    intr->r_symndx = -1;
+  return md_pcrel_from (fixP);
 }
 
-#endif /* OBJ_COFF */
-#endif /* ! BFD_ASSEMBLER */
-
-#ifdef BFD_ASSEMBLER
-
 /* 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;
@@ -3324,7 +4434,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)
@@ -3351,14 +4462,15 @@ tc_gen_reloc (section, fixp)
       rel->addend = 0;
       rel->address = rel->addend = fixp->fx_offset;
     }
-  else if (fixp->fx_pcrel)
-    rel->addend = fixp->fx_addnumber;
-  else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
-    rel->addend = fixp->fx_addnumber;
+#ifdef HAVE_SH64
+  else if (shmedia_init_reloc (rel, fixp))
+    ;
+#endif
   else
-    rel->addend = 0;
+    rel->addend = fixp->fx_addnumber;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
   if (rel->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
@@ -3366,10 +4478,164 @@ tc_gen_reloc (section, fixp)
                    bfd_get_reloc_code_name (r_type));
       /* Set howto to a garbage value so that we can keep going.  */
       rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
-      assert (rel->howto != NULL);
+      gas_assert (rel->howto != NULL);
     }
+#ifdef OBJ_ELF
+  else if (rel->howto->type == R_SH_IND12W)
+    rel->addend += fixp->fx_offset - 4;
+#endif
 
   return rel;
 }
 
-#endif /* BFD_ASSEMBLER */
+#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,
+              enum expr_mode mode,
+              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 (mode != expr_defer && 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 (mode != expr_defer && 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 if ((next_end = sh_end_of_match (next + 1, "PCREL")))
+    reloc_type = BFD_RELOC_32_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTFUNCDESC")))
+    reloc_type = BFD_RELOC_SH_GOTFUNCDESC;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTOFFFUNCDESC")))
+    reloc_type = BFD_RELOC_SH_GOTOFFFUNCDESC;
+  else if ((next_end = sh_end_of_match (next + 1, "FUNCDESC")))
+    reloc_type = BFD_RELOC_SH_FUNCDESC;
+  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;
+}
+
+void
+sh_cfi_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa (15, 0);
+}
+
+int
+sh_regname_to_dw2regnum (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 /* OBJ_ELF */