]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-sh.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / config / tc-sh.c
index 2b8f918fdb9867019de8864d508e98e7673af532..78c3fca955a939b52ecf22edd89877fb5c7d46fe 100644 (file)
@@ -1,12 +1,11 @@
 /* tc-sh.c -- Assemble code for the Renesas / SuperH SH
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-   Free Software Foundation, Inc.
+   Copyright (C) 1993-2024 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"
 #include "safe-ctype.h"
-#include "struc-symbol.h"
 
 #ifdef OBJ_ELF
 #include "elf/sh.h"
 #endif
 
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 
 typedef struct
   {
@@ -108,36 +105,35 @@ const pseudo_typeS md_pseudo_table[] =
   {"2byte", s_uacons, 2},
   {"4byte", s_uacons, 4},
   {"8byte", s_uacons, 8},
-#ifdef HAVE_SH64
-  {"mode", s_sh64_mode, 0 },
-
-  /* Have the old name too.  */
-  {"isa", s_sh64_mode, 0 },
-
-  /* Assert that the right ABI is used.  */
-  {"abi", s_sh64_abi, 0 },
-
-  { "vtable_inherit", sh64_vtable_inherit, 0 },
-  { "vtable_entry", sh64_vtable_entry, 0 },
-#endif /* HAVE_SH64 */
   {0, 0, 0}
 };
 
-/*int md_reloc_size; */
-
 int sh_relax;          /* set if -relax seen */
 
 /* Whether -small was seen.  */
 
 int sh_small;
 
-/* preset architecture set, if given; zero otherwise.  */
+/* 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;
+
+/* Preset architecture set, if given; zero otherwise.  */
 
-static int preset_target_arch;
+static unsigned int preset_target_arch;
 
 /* The bit mask of architectures that could
    accommodate the insns seen so far.  */
-static int valid_arch;
+static unsigned int valid_arch;
+
+#ifdef OBJ_ELF
+/* Whether --fdpic was given.  */
+static int sh_fdpic;
+#endif
 
 const char EXP_CHARS[] = "eE";
 
@@ -158,31 +154,8 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #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
@@ -192,24 +165,6 @@ 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
@@ -240,85 +195,6 @@ 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)] = {
@@ -360,126 +236,15 @@ const relax_typeS md_relax_table[C (END, 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
 
-static struct hash_control *opcode_hash_control;       /* Opcode mnemonics */
+static htab_t opcode_hash_control;     /* Opcode mnemonics */
 
 \f
 #ifdef OBJ_ELF
-/* Determinet whether the symbol needs any kind of PIC relocation.  */
+/* Determine whether the symbol needs any kind of PIC relocation.  */
 
 inline static int
 sh_PIC_related_p (symbolS *sym)
@@ -492,11 +257,6 @@ sh_PIC_related_p (symbolS *sym)
   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
@@ -558,47 +318,11 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 
   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;
@@ -606,7 +330,6 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 
   if (exp->X_op == O_PIC_reloc)
     {
-#ifdef HAVE_SH64
       switch (*r_type_p)
        {
        case BFD_RELOC_NONE:
@@ -614,95 +337,23 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
          *r_type_p = exp->X_md;
          break;
 
-       case BFD_RELOC_SH_IMM_LOW16:
-         switch (exp->X_md)
-           {
-           case BFD_RELOC_32_GOTOFF:
-             *r_type_p = BFD_RELOC_SH_GOTOFF_LOW16;
-             break;
-
-           case BFD_RELOC_SH_GOTPLT32:
-             *r_type_p = BFD_RELOC_SH_GOTPLT_LOW16;
-             break;
-
-           case BFD_RELOC_32_GOT_PCREL:
-             *r_type_p = BFD_RELOC_SH_GOT_LOW16;
-             break;
-
-           case BFD_RELOC_32_PLT_PCREL:
-             *r_type_p = BFD_RELOC_SH_PLT_LOW16;
-             break;
-
-           default:
-             abort ();
-           }
-         break;
-
-       case BFD_RELOC_SH_IMM_MEDLOW16:
-         switch (exp->X_md)
-           {
-           case BFD_RELOC_32_GOTOFF:
-             *r_type_p = BFD_RELOC_SH_GOTOFF_MEDLOW16;
-             break;
-
-           case BFD_RELOC_SH_GOTPLT32:
-             *r_type_p = BFD_RELOC_SH_GOTPLT_MEDLOW16;
-             break;
-
-           case BFD_RELOC_32_GOT_PCREL:
-             *r_type_p = BFD_RELOC_SH_GOT_MEDLOW16;
-             break;
-
-           case BFD_RELOC_32_PLT_PCREL:
-             *r_type_p = BFD_RELOC_SH_PLT_MEDLOW16;
-             break;
-
-           default:
-             abort ();
-           }
-         break;
-
-       case BFD_RELOC_SH_IMM_MEDHI16:
+       case BFD_RELOC_SH_DISP20:
          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;
+             *r_type_p = BFD_RELOC_SH_GOT20;
              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;
+             *r_type_p = BFD_RELOC_SH_GOTOFF20;
              break;
 
-           case BFD_RELOC_32_GOT_PCREL:
-             *r_type_p = BFD_RELOC_SH_GOT_HI16;
+           case BFD_RELOC_SH_GOTFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
              break;
 
-           case BFD_RELOC_32_PLT_PCREL:
-             *r_type_p = BFD_RELOC_SH_PLT_HI16;
+           case BFD_RELOC_SH_GOTOFFFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
              break;
 
            default:
@@ -713,9 +364,6 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
        default:
          abort ();
        }
-#else
-      *r_type_p = exp->X_md;
-#endif
       if (exp == main_exp)
        exp->X_op = O_symbol;
       else
@@ -734,9 +382,10 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 /* 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)
+sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp,
+                bfd_reloc_code_real_type r_type)
 {
-  bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+  r_type = BFD_RELOC_UNUSED;
 
   if (sh_check_fixup (exp, &r_type))
     as_bad (_("Invalid PIC expression."));
@@ -756,11 +405,9 @@ sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
        r_type = BFD_RELOC_32;
        break;
 
-#ifdef HAVE_SH64
       case 8:
        r_type = BFD_RELOC_64;
        break;
-#endif
 
       default:
        goto error;
@@ -781,20 +428,10 @@ sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
 /* Clobbers input_line_pointer, checks end-of-line.  */
 /* NBYTES 1=.byte, 2=.word, 4=.long */
 static void
-sh_elf_cons (register int nbytes)
+sh_elf_cons (int nbytes)
 {
   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 ())
     {
       demand_empty_rest_of_line ();
@@ -820,8 +457,98 @@ sh_elf_cons (register int 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 bool
+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))
+    {
+      offsetT symval_diff = S_GET_VALUE (l->X_add_symbol)
+                           - S_GET_VALUE (r->X_add_symbol);
+      subtract_from_result (l, r->X_add_number, r->X_extrabit);
+      subtract_from_result (l, frag_off / OCTETS_PER_BYTE, 0);
+      add_to_result (l, symval_diff, symval_diff < 0);
+      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.  */
@@ -830,28 +557,24 @@ void
 md_begin (void)
 {
   const sh_opcode_info *opcode;
-  char *prev_name = "";
-  int target_arch;
+  const char *prev_name = "";
+  unsigned int target_arch;
 
   target_arch
-    = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_dsp_up;
+    = 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 ();
+  opcode_hash_control = str_htab_create ();
 
   /* Insert unique names into hash table.  */
   for (opcode = sh_table; opcode->name; opcode++)
     {
       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);
+         str_hash_insert (opcode_hash_control, opcode->name, opcode, 0);
        }
     }
 }
@@ -866,8 +589,8 @@ static int reg_b;
 
 /* Try to parse a reg name.  Return the number of chars consumed.  */
 
-static int
-parse_reg (char *src, int *mode, int *reg)
+static unsigned int
+parse_reg_without_prefix (char *src, sh_arg_type *mode, int *reg)
 {
   char l0 = TOLOWER (src[0]);
   char l1 = l0 ? TOLOWER (src[1]) : 0;
@@ -1093,6 +816,12 @@ parse_reg (char *src, int *mode, int *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]))
     {
@@ -1216,26 +945,50 @@ parse_reg (char *src, int *mode, int *reg)
   return 0;
 }
 
+/* Like parse_reg_without_prefix, but this version supports
+   $-prefixed register names if enabled by the user.  */
+
+static unsigned int
+parse_reg (char *src, sh_arg_type *mode, int *reg)
+{
+  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 (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"));
-#ifdef OBJ_ELF
-  else if (op->immediate.X_op == O_PIC_reloc
-          || sh_PIC_related_p (op->immediate.X_add_symbol)
-          || sh_PIC_related_p (op->immediate.X_op_symbol))
-    as_bad (_("misplaced PIC operand"));
-#endif
-  new = input_line_pointer;
+  new_pointer = input_line_pointer;
   input_line_pointer = save;
-  return new;
+  return new_pointer;
 }
 
 /* The many forms of operand:
@@ -1261,9 +1014,17 @@ static char *
 parse_at (char *src, sh_operand_info *op)
 {
   int len;
-  int mode;
+  sh_arg_type 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++;
@@ -1336,6 +1097,10 @@ parse_at (char *src, sh_operand_info *op)
                {
                  op->type = A_DISP_GBR;
                }
+             else if (mode == A_TBR)
+               {
+                 op->type = A_DISP_TBR;
+               }
              else if (mode == A_PC)
                {
                  /* We want @(expr, pc) to uniformly address . + expr,
@@ -1421,7 +1186,7 @@ static void
 get_operand (char **ptr, sh_operand_info *op)
 {
   char *src = *ptr;
-  int mode = -1;
+  sh_arg_type mode = (sh_arg_type) -1;
   unsigned int len;
 
   if (src[0] == '#')
@@ -1457,6 +1222,10 @@ static char *
 get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
 {
   char *ptr = args;
+
+  operand[0].type = 0;
+  operand[1].type = 0;
+  operand[2].type = 0;
   if (info->arg[0])
     {
       /* The pre-processor will eliminate whitespace in front of '@'
@@ -1469,9 +1238,7 @@ get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
       if (info->arg[1])
        {
          if (*ptr == ',')
-           {
-             ptr++;
-           }
+           ptr++;
          get_operand (&ptr, operand + 1);
          /* ??? Hack: psha/pshl have a varying operand number depending on
             the type of the first operand.  We handle this by having the
@@ -1482,28 +1249,11 @@ get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
          if (info->arg[2] && operand[0].type != A_IMM)
            {
              if (*ptr == ',')
-               {
-                 ptr++;
-               }
+               ptr++;
              get_operand (&ptr, operand + 2);
            }
-         else
-           {
-             operand[2].type = 0;
-           }
-       }
-      else
-       {
-         operand[1].type = 0;
-         operand[2].type = 0;
        }
     }
-  else
-    {
-      operand[0].type = 0;
-      operand[1].type = 0;
-      operand[2].type = 0;
-    }
   return ptr;
 }
 
@@ -1515,7 +1265,7 @@ static sh_opcode_info *
 get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
 {
   sh_opcode_info *this_try = opcode;
-  char *name = opcode->name;
+  const char *name = opcode->name;
   int n = 0;
 
   while (opcode->name)
@@ -1547,6 +1297,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
            case A_BDISP12:
            case A_BDISP8:
            case A_DISP_GBR:
+           case A_DISP2_TBR:
            case A_MACH:
            case A_PR:
            case A_MACL:
@@ -1591,6 +1342,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
              reg_n = user->reg;
              break;
            case A_GBR:
+           case A_TBR:
            case A_SR:
            case A_VBR:
            case A_DSR:
@@ -1611,6 +1363,22 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
              reg_b = user->reg;
              break;
 
+           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:
@@ -1631,7 +1399,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AS_INC_N:
              if (user->type != A_INC_N)
                goto fail;
@@ -1639,7 +1407,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AS_IND_N:
              if (user->type != A_IND_N)
                goto fail;
@@ -1647,7 +1415,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AS_PMOD_N:
              if (user->type != AX_PMOD_N)
                goto fail;
@@ -1655,7 +1423,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AX_INC_N:
              if (user->type != A_INC_N)
                goto fail;
@@ -1663,7 +1431,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AX_IND_N:
              if (user->type != A_IND_N)
                goto fail;
@@ -1671,7 +1439,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AX_PMOD_N:
              if (user->type != AX_PMOD_N)
                goto fail;
@@ -1679,7 +1447,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AXY_INC_N:
              if (user->type != A_INC_N)
                goto fail;
@@ -1688,7 +1456,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AXY_IND_N:
              if (user->type != A_IND_N)
                goto fail;
@@ -1697,7 +1465,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AXY_PMOD_N:
              if (user->type != AX_PMOD_N)
                goto fail;
@@ -1706,7 +1474,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AY_INC_N:
              if (user->type != A_INC_N)
                goto fail;
@@ -1714,7 +1482,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AY_IND_N:
              if (user->type != A_IND_N)
                goto fail;
@@ -1722,7 +1490,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AY_PMOD_N:
              if (user->type != AY_PMOD_N)
                goto fail;
@@ -1739,7 +1507,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AYX_IND_N:
              if (user->type != A_IND_N)
                goto fail;
@@ -1748,7 +1516,7 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
                goto fail;
              reg_n = user->reg;
              break;
-             
+
            case AYX_PMOD_N:
              if (user->type != AY_PMOD_N)
                goto fail;
@@ -2012,10 +1780,40 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *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:
       ;
@@ -2025,7 +1823,8 @@ get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
 }
 
 static void
-insert (char *where, int how, int pcrel, sh_operand_info *op)
+insert (char *where, bfd_reloc_code_real_type how, int pcrel,
+               sh_operand_info *op)
 {
   fix_new_exp (frag_now,
               where - frag_now->fr_literal,
@@ -2035,6 +1834,17 @@ insert (char *where, int how, int pcrel, sh_operand_info *op)
               how);
 }
 
+static void
+insert4 (char * where, bfd_reloc_code_real_type 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)
 {
@@ -2072,7 +1882,6 @@ build_relax (sh_opcode_info *opcode, sh_operand_info *op)
 static char *
 insert_loop_bounds (char *output, sh_operand_info *operand)
 {
-  char *name;
   symbolS *end_sym;
 
   /* Since the low byte of the opcode will be overwritten by the reloc, we
@@ -2085,6 +1894,8 @@ insert_loop_bounds (char *output, sh_operand_info *operand)
   if (sh_relax)
     {
       static int count = 0;
+      char name[11];
+      expressionS *symval;
 
       /* If the last loop insn is a two-byte-insn, it is in danger of being
         swapped with the insn after it.  To prevent this, create a new
@@ -2093,16 +1904,16 @@ insert_loop_bounds (char *output, sh_operand_info *operand)
         right in the middle, but four byte insns are not swapped anyways.  */
       /* A REPEAT takes 6 bytes.  The SH has a 32 bit address space.
         Hence a 9 digit number should be enough to count all REPEATs.  */
-      name = alloca (11);
       sprintf (name, "_R%x", count++ & 0x3fffffff);
-      end_sym = symbol_new (name, undefined_section, 0, &zero_address_frag);
+      end_sym = symbol_new (name, undefined_section, &zero_address_frag, 0);
       /* Make this a local symbol.  */
 #ifdef OBJ_COFF
       SF_SET_LOCAL (end_sym);
 #endif /* OBJ_COFF */
       symbol_table_insert (end_sym);
-      end_sym->sy_value = operand[1].immediate;
-      end_sym->sy_value.X_add_number += 2;
+      symval = symbol_get_value_expression (end_sym);
+      *symval = operand[1].immediate;
+      symval->X_add_number += 2;
       fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL);
     }
 
@@ -2120,46 +1931,129 @@ insert_loop_bounds (char *output, sh_operand_info *operand)
 static unsigned int
 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
 
-  for (index = 0; index < 4; index++)
+  if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
     {
-      sh_nibble_type i = opcode->nibbles[index];
+      output = frag_more (4);
+      size = 4;
+      max_index = 8;
+    }
+  else
+    output = frag_more (2);
+
+  for (indx = 0; indx < max_index; indx++)
+    {
+      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 IMM0_4BY4:
-             insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
+           case REG_N_B01:
+             nbuf[indx] = reg_n | 0x01;
+             break;
+           case IMM0_3s:
+             nbuf[indx] |= 0x08;
+             /* Fall through.  */
+           case IMM0_3c:
+             insert (output + low_byte, BFD_RELOC_SH_IMM3, 0, operand);
+             break;
+           case IMM0_3Us:
+             nbuf[indx] |= 0x80;
+             /* Fall through.  */
+           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);
              break;
            case IMM0_4BY2:
              insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand);
@@ -2182,7 +2076,8 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
            case IMM0_8BY2:
              insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand);
              break;
-           case IMM0_8:
+           case IMM0_8U:
+           case IMM0_8S:
              insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand);
              break;
            case IMM1_8BY4:
@@ -2204,7 +2099,7 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
              break;
            case REPEAT:
              output = insert_loop_bounds (output, operand);
-             nbuf[index] = opcode->nibbles[3];
+             nbuf[indx] = opcode->nibbles[3];
              operand += 2;
              break;
            default:
@@ -2212,6 +2107,10 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
            }
        }
     }
+#ifdef OBJ_ELF
+  if (unhandled_pic)
+    as_bad (_("misplaced PIC operand"));
+#endif
   if (!target_big_endian)
     {
       output[1] = (nbuf[0] << 4) | (nbuf[1]);
@@ -2222,6 +2121,19 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *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;
 }
 
@@ -2235,7 +2147,7 @@ find_cooked_opcode (char **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 == ' ')
@@ -2245,9 +2157,9 @@ find_cooked_opcode (char **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++)
     {
@@ -2264,12 +2176,12 @@ find_cooked_opcode (char **str_p)
     }
 
   name[nlen] = 0;
-  *str_p = op_end;
+  *str_p = (char *) op_end;
 
   if (nlen == 0)
     as_bad (_("can't find opcode "));
 
-  return (sh_opcode_info *) hash_find (opcode_hash_control, name);
+  return (sh_opcode_info *) str_hash_find (opcode_hash_control, name);
 }
 
 /* Assemble a parallel processing insn.  */
@@ -2278,12 +2190,12 @@ find_cooked_opcode (char **str_p)
 static unsigned int
 assemble_ppi (char *op_end, sh_opcode_info *opcode)
 {
-  int movx = 0;
-  int movy = 0;
-  int cond = 0;
-  int field_b = 0;
+  unsigned int movx = 0;
+  unsigned int movy = 0;
+  unsigned int cond = 0;
+  unsigned int field_b = 0;
   char *output;
-  int move_code;
+  unsigned int move_code;
   unsigned int size;
 
   for (;;)
@@ -2477,9 +2389,9 @@ assemble_ppi (char *op_end, sh_opcode_info *opcode)
                field_b -= 0x8100;
              /* pclr Dz pmuls Se,Sf,Dg */
              else if ((field_b & 0xff00) == 0x8d00
-                      && (valid_arch & arch_sh4al_dsp_up))
+                      && (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh4al_dsp_up)))
                {
-                 valid_arch &= arch_sh4al_dsp_up;
+                 valid_arch = SH_MERGE_ARCH_SET (valid_arch, arch_sh4al_dsp_up);
                  field_b -= 0x8cf0;
                }
              else
@@ -2537,7 +2449,7 @@ assemble_ppi (char *op_end, sh_opcode_info *opcode)
   if (field_b)
     {
       /* Parallel processing insn.  */
-      unsigned long ppi_code = (movx | movy | 0xf800) << 16 | field_b;
+      unsigned int ppi_code = (movx | movy | 0xf800) << 16 | field_b;
 
       output = frag_more (4);
       size = 4;
@@ -2579,37 +2491,51 @@ assemble_ppi (char *op_end, sh_opcode_info *opcode)
 void
 md_assemble (char *str)
 {
-  unsigned char *op_end;
+  char *op_end;
   sh_operand_info operand[3];
   sh_opcode_info *opcode;
   unsigned int size = 0;
-
-#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 */
+  char *initial_str = str;
 
   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;
+      bool found = false;
+
+      /* Identify opcode in string.  */
+      while (ISSPACE (*name))
+       name++;
+
+      while (name[name_length] != '\0' && !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 = true;
+             break;
+           }
+       }
+
+      if (found)
+       as_bad (_("opcode not valid for this cpu variant"));
+      else
+       as_bad (_("unknown opcode"));
+
       return;
     }
 
@@ -2634,12 +2560,15 @@ md_assemble (char *str)
        {
          /* Since we skip get_specific here, we have to check & update
             valid_arch now.  */
-         if (valid_arch & opcode->arch)
-           valid_arch &= opcode->arch;
+         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
        {
@@ -2676,16 +2605,14 @@ md_assemble (char *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 (void)
+sh_frob_label (symbolS *sym)
 {
   static fragS *last_label_frag;
   static int last_label_offset;
@@ -2704,6 +2631,8 @@ sh_frob_label (void)
          last_label_offset = offset;
        }
     }
+
+  dwarf2_emit_label (sym);
 }
 
 /* This routine is called when the assembler is about to output some
@@ -2727,80 +2656,12 @@ md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
   return 0;
 }
 
-#ifdef OBJ_COFF
-#ifndef BFD_ASSEMBLER
-
-void
-tc_crawl_symbol_chain (object_headers *headers ATTRIBUTE_UNUSED)
-{
-  printf (_("call to tc_crawl_symbol_chain \n"));
-}
-
-void
-tc_headers_hook (object_headers *headers ATTRIBUTE_UNUSED)
-{
-  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 *
+const char *
 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
@@ -2830,41 +2691,51 @@ s_uses (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 \f
+enum options
+{
+  OPTION_RELAX = OPTION_MD_BASE,
+  OPTION_BIG,
+  OPTION_LITTLE,
+  OPTION_SMALL,
+  OPTION_DSP,
+  OPTION_ISA,
+  OPTION_RENESAS,
+  OPTION_ALLOW_REG_PREFIX,
+  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[] =
 {
-#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)
-#define OPTION_ISA                    (OPTION_DSP + 1)
-
   {"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},
-#ifdef HAVE_SH64
-#define OPTION_ABI                    (OPTION_ISA + 1)
-#define OPTION_NO_MIX                 (OPTION_ABI + 1)
-#define OPTION_SHCOMPACT_CONST_CRANGE (OPTION_NO_MIX + 1)
-#define OPTION_NO_EXPAND              (OPTION_SHCOMPACT_CONST_CRANGE + 1)
-#define OPTION_PT32                   (OPTION_NO_EXPAND + 1)
-  {"abi",                    required_argument, NULL, OPTION_ABI},
-  {"no-mix",                 no_argument, NULL, OPTION_NO_MIX},
-  {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
-  {"no-expand",              no_argument, NULL, OPTION_NO_EXPAND},
-  {"expand-pt32",            no_argument, NULL, OPTION_PT32},
-#endif /* HAVE_SH64 */
+  {"isa", required_argument, NULL, OPTION_ISA},
+  {"renesas", no_argument, NULL, OPTION_RENESAS},
+  {"allow-reg-prefix", no_argument, NULL, OPTION_ALLOW_REG_PREFIX},
+
+  { "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 (int c, char *arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
@@ -2885,76 +2756,62 @@ md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
       break;
 
     case OPTION_DSP:
-      preset_target_arch = arch_sh1_up & ~arch_sh2e_up;
+      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, "sh4") == 0)
-       preset_target_arch = arch_sh4;
-      else if (strcasecmp (arg, "sh4a") == 0)
-       preset_target_arch = arch_sh4a;
-      else if (strcasecmp (arg, "dsp") == 0)
-       preset_target_arch = arch_sh1_up & ~arch_sh2e_up;
+      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_sh2e_up;
+       preset_target_arch = arch_sh_up & ~arch_sh_has_dsp;
       else if (strcasecmp (arg, "any") == 0)
-       preset_target_arch = arch_sh1_up;
-#ifdef HAVE_SH64
-      else if (strcasecmp (arg, "shmedia") == 0)
-       {
-         if (sh64_isa_mode == sh64_isa_shcompact)
-           as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
-         sh64_isa_mode = sh64_isa_shmedia;
-       }
-      else if (strcasecmp (arg, "shcompact") == 0)
-       {
-         if (sh64_isa_mode == sh64_isa_shmedia)
-           as_bad (_("Invalid combination: --isa=SHmedia with --isa=SHcompact"));
-         if (sh64_abi == sh64_abi_64)
-           as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
-         sh64_isa_mode = sh64_isa_shcompact;
-       }
-#endif /* HAVE_SH64 */
+       preset_target_arch = arch_sh_up;
       else
-       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;
+         extern const bfd_arch_info_type bfd_sh_arch;
+         bfd_arch_info_type const *bfd_arch = &bfd_sh_arch;
 
-    case OPTION_NO_MIX:
-      sh64_mix = FALSE;
-      break;
+         preset_target_arch = 0;
+         for (; bfd_arch; bfd_arch=bfd_arch->next)
+           {
+             int len = strlen(bfd_arch->printable_name);
+
+             if (strncasecmp (bfd_arch->printable_name, arg, len) != 0)
+               continue;
 
-    case OPTION_SHCOMPACT_CONST_CRANGE:
-      sh64_shcompact_const_crange = TRUE;
+             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;
 
-    case OPTION_NO_EXPAND:
-      sh64_expand = FALSE;
+    case OPTION_H_TICK_HEX:
+      enable_h_tick_hex = 1;
       break;
 
-    case OPTION_PT32:
-      sh64_pt32 = TRUE;
+#ifdef OBJ_ELF
+    case OPTION_FDPIC:
+      sh_fdpic = true;
       break;
-#endif /* HAVE_SH64 */
+#endif /* OBJ_ELF */
 
     default:
       return 0;
@@ -2968,37 +2825,32 @@ 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 floating-point ISAs.\n"));
-  fprintf (stream, _("\
--isa=[sh4\n\
-    | sh4a\n\
-    | dsp              same as '-dsp'\n\
-    | fp\n"
-#ifdef HAVE_SH64
-"\
-    | shmedia          set default instruction set for SH64\n\
-    | SHmedia\n\
-    | shcompact\n\
-    | SHcompact\n"
-#endif
-"\
-    | any]\n"));
-#ifdef HAVE_SH64
+--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)
+      {
+       fprintf (stream, "\n    | %s", bfd_arch->printable_name);
+       fprintf (stream, "\n    | %s-up", bfd_arch->printable_name);
+      }
+  }
+  fprintf (stream, "]\n");
+#ifdef OBJ_ELF
   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"));
-#endif /* HAVE_SH64 */
+--fdpic                        generate an FDPIC object file\n"));
+#endif /* OBJ_ELF */
 }
 \f
 /* This struct is used to pass arguments to sh_count_relocs through
@@ -3013,8 +2865,7 @@ 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 (bfd *abfd ATTRIBUTE_UNUSED, segT sec, void *data)
@@ -3039,8 +2890,8 @@ sh_count_relocs (bfd *abfd ATTRIBUTE_UNUSED, segT sec, void *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 (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
@@ -3070,9 +2921,6 @@ sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
          || 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,
@@ -3112,9 +2960,6 @@ sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
          || 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,
@@ -3126,16 +2971,7 @@ sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
         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, &info);
-#else
-      {
-       int iscan;
-
-       for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
-         sh_count_relocs ((bfd *) NULL, iscan, &info);
-      }
-#endif
 
       if (info.count < 1)
        abort ();
@@ -3163,35 +2999,17 @@ sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
 void
 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, NULL);
-#else
-  {
-    int iseg;
-
-    for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
-      sh_frob_section ((bfd *) NULL, iseg, NULL);
-  }
-#endif
 }
 
 /* Called after relaxing.  Set the correct sizes of the fragments, and
-   create relocs so that md_apply_fix3 will fill in the correct values.  */
+   create relocs so that md_apply_fix will fill in the correct values.  */
 
 void
-#ifdef BFD_ASSEMBLER
 md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg, fragS *fragP)
-#else
-md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
-                fragS *fragP)
-#endif
 {
   int donerelax = 0;
 
@@ -3248,7 +3066,7 @@ md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
         differently from ones without delay slots.  */
       {
        unsigned char *buffer =
-         (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+         (unsigned char *) (fragP->fr_fix + &fragP->fr_literal[0]);
        int highbyte = target_big_endian ? 0 : 1;
        int lowbyte = target_big_endian ? 1 : 0;
        int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12);
@@ -3264,12 +3082,7 @@ md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
 
        /* 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);
 
@@ -3318,11 +3131,7 @@ md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
       break;
 
     default:
-#ifdef HAVE_SH64
-      shmedia_md_convert_frag (headers, seg, fragP, TRUE);
-#else
       abort ();
-#endif
     }
 
   if (donerelax && !sh_relax)
@@ -3336,17 +3145,12 @@ md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
 valueT
 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)));
+  return ((size + (1 << bfd_section_alignment (seg)) - 1)
+         & -(1 << bfd_section_alignment (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
@@ -3376,7 +3180,6 @@ void
 sh_cons_align (int nbytes)
 {
   int nalign;
-  char *p;
 
   if (sh_no_align_cons)
     {
@@ -3402,8 +3205,8 @@ sh_cons_align (int 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);
 }
@@ -3445,7 +3248,7 @@ sh_handle_align (fragS *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
@@ -3460,7 +3263,7 @@ sh_handle_align (fragS *frag)
 
 /* See whether the relocation should be resolved locally.  */
 
-static bfd_boolean
+static bool
 sh_local_pcrel (fixS *fix)
 {
   return (! sh_relax
@@ -3505,19 +3308,23 @@ sh_force_relocation (fixS *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
-bfd_boolean
+bool
 sh_fix_adjustable (fixS *fixP)
 {
   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;
 
@@ -3536,49 +3343,55 @@ sh_elf_final_processing (void)
 
   /* Set file-specific flags to indicate if this code needs
      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
-#endif /* HAVE_SH64 */
-  if (valid_arch & arch_sh1)
-    val = EF_SH1;
-  else if (valid_arch & arch_sh2)
-    val = EF_SH2;
-  else if (valid_arch & arch_sh2e)
-    val = EF_SH2E;
-  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_SH3_DSP;
-  else if (valid_arch & arch_sh3e)
-    val = EF_SH3E;
-  else if (valid_arch & arch_sh4_nofpu)
-    val = EF_SH4_NOFPU;
-  else if (valid_arch & arch_sh4)
-    val = EF_SH4;
-  else if (valid_arch & arch_sh4a_nofpu)
-    val = EF_SH4A_NOFPU;
-  else if (valid_arch & arch_sh4a)
-    val = EF_SH4A;
-  else if (valid_arch & arch_sh4al_dsp)
-    val = EF_SH4AL_DSP;
-  else
-    abort ();
+  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.  */
 
 void
-md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+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;
@@ -3587,7 +3400,6 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
   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.  */
@@ -3633,29 +3445,65 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       && fixP->fx_addsy != NULL
       && S_IS_WEAK (fixP->fx_addsy))
     val -= S_GET_VALUE  (fixP->fx_addsy);
-#endif
 
-#ifdef BFD_ASSEMBLER
   if (SWITCH_TABLE (fixP))
     val -= S_GET_VALUE  (fixP->fx_subsy);
-#else
-  if (fixP->fx_r_type == 0)
-    {
-      if (fixP->fx_size == 2)
-       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
 
   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);
@@ -3697,6 +3545,23 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       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 boundary.  If this instruction is also on a 4
@@ -3740,15 +3605,15 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 
     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;
 
@@ -3776,8 +3641,7 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       val = fixP->fx_offset;
       if (fixP->fx_subsy)
        val -= S_GET_VALUE (fixP->fx_subsy);
-      fixP->fx_addnumber = val;
-      md_number_to_chars (buf, val, 4);
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 
     case BFD_RELOC_SH_GOTPC:
@@ -3798,7 +3662,7 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          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);
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 
     case BFD_RELOC_SH_TLS_GD_32:
@@ -3807,9 +3671,15 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* Fallthrough */
     case BFD_RELOC_32_GOT_PCREL:
+    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.  */
-      md_number_to_chars (buf, 0, 4);
+      apply_full_field_fix (fixP, buf, 0, 4);
       break;
 
     case BFD_RELOC_SH_TLS_LDO_32:
@@ -3817,17 +3687,13 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* Fallthrough */
     case BFD_RELOC_32_GOTOFF:
-      md_number_to_chars (buf, val, 4);
+    case BFD_RELOC_SH_GOTOFF20:
+      apply_full_field_fix (fixP, buf, val, 4);
       break;
 #endif
 
     default:
-#ifdef HAVE_SH64
-      shmedia_md_apply_fix3 (fixP, valP);
-      return;
-#else
       abort ();
-#endif
     }
 
   if (shift != 0)
@@ -3840,8 +3706,16 @@ md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        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"));
+  else if (max != 0)
+    /* Stop the generic code from trying to overflow 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;
@@ -3858,12 +3732,7 @@ md_estimate_size_before_relax (fragS *fragP, segT segment_type)
   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.  */
@@ -3894,7 +3763,7 @@ md_estimate_size_before_relax (fragS *fragP, segT segment_type)
        }
       else if (fragP->fr_symbol)
        {
-         /* Its got a segment, but its not ours, so it will always be long.  */
+         /* It's got a segment, but it's not ours, so it will always be long.  */
          fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
        }
       else
@@ -3929,19 +3798,13 @@ md_estimate_size_before_relax (fragS *fragP, segT segment_type)
 void
 md_number_to_chars (char *ptr, valueT use, int nbytes)
 {
-#ifdef HAVE_SH64
-  /* We might need to set the contents type to data.  */
-  sh64_flag_output ();
-#endif
-
   if (! target_big_endian)
     number_to_chars_littleendian (ptr, use, nbytes);
   else
     number_to_chars_bigendian (ptr, use, nbytes);
 }
 
-/* This version is used in obj-coff.c when not using BFD_ASSEMBLER.
-   eg for the sh-hms target.  */
+/* This version is used in obj-coff.c eg. for the sh-hms target.  */
 
 long
 md_pcrel_from (fixS *fixP)
@@ -3967,167 +3830,6 @@ md_pcrel_from_section (fixS *fixP, segT sec)
   return md_pcrel_from (fixP);
 }
 
-#ifdef OBJ_COFF
-
-int
-tc_coff_sizemachdep (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 (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_fix3.  */
-      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;
-}
-
-#endif /* OBJ_COFF */
-#endif /* ! BFD_ASSEMBLER */
-
-#ifdef BFD_ASSEMBLER
-
 /* Create a reloc.  */
 
 arelent *
@@ -4136,8 +3838,8 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
   arelent *rel;
   bfd_reloc_code_real_type r_type;
 
-  rel = (arelent *) xmalloc (sizeof (arelent));
-  rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  rel = XNEW (arelent);
+  rel->sym_ptr_ptr = XNEW (asymbol *);
   *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
@@ -4146,7 +3848,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
   if (SWITCH_TABLE (fixp))
     {
       *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
-      rel->addend = 0;
+      rel->addend = rel->address - S_GET_VALUE(fixp->fx_subsy);
       if (r_type == BFD_RELOC_16)
        r_type = BFD_RELOC_SH_SWITCH16;
       else if (r_type == BFD_RELOC_8)
@@ -4173,22 +3875,11 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
       rel->addend = 0;
       rel->address = rel->addend = fixp->fx_offset;
     }
-#ifdef HAVE_SH64
-  else if (shmedia_init_reloc (rel, fixp))
-    ;
-#endif
-  else if (fixp->fx_pcrel)
-    rel->addend = fixp->fx_addnumber;
-  else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
-    rel->addend = fixp->fx_addnumber;
   else
-    rel->addend = 0;
+    rel->addend = fixp->fx_addnumber;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
-#ifdef OBJ_ELF
-  if (rel->howto->type == R_SH_IND12W)
-      rel->addend += fixp->fx_offset - 4;
-#endif
+
   if (rel->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
@@ -4196,15 +3887,19 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *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;
 }
 
 #ifdef OBJ_ELF
 inline static char *
-sh_end_of_match (char *cont, char *what)
+sh_end_of_match (char *cont, const char *what)
 {
   int len = strlen (what);
 
@@ -4216,7 +3911,10 @@ sh_end_of_match (char *cont, char *what)
 }
 
 int
-sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
+sh_parse_name (char const *name,
+              expressionS *exprP,
+              enum expr_mode mode,
+              char *nextcharP)
 {
   char *next = input_line_pointer;
   char *next_end;
@@ -4233,15 +3931,15 @@ sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
       exprP->X_add_symbol = GOT_symbol;
     no_suffix:
       /* If we have an absolute symbol or a reg, then we know its
-            value now.  */
+        value now.  */
       segment = S_GET_SEGMENT (exprP->X_add_symbol);
-      if (segment == absolute_section)
+      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 (segment == reg_section)
+      else if (mode != expr_defer && segment == reg_section)
        {
          exprP->X_op = O_register;
          exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
@@ -4278,6 +3976,14 @@ sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
     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;
 
@@ -4292,5 +3998,53 @@ sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
 
   return 1;
 }
-#endif
-#endif /* BFD_ASSEMBLER */
+
+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 { const 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 */