]> 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 76f669be776536cc673438890c955fa6e4e2f156..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, 2004  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"
@@ -109,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;
 
-static int preset_target_arch;
+/* Flag to indicate that '$' is allowed as a register prefix.  */
+
+static int allow_dollar_register_prefix;
+
+/* Preset architecture set, if given; zero otherwise.  */
+
+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";
 
@@ -159,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
@@ -193,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
@@ -241,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)] = {
@@ -361,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)
@@ -493,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
@@ -559,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;
@@ -607,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:
@@ -615,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;
-             break;
-
-           case BFD_RELOC_32_PLT_PCREL:
-             *r_type_p = BFD_RELOC_SH_PLT_MEDHI16;
+             *r_type_p = BFD_RELOC_SH_GOT20;
              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;
+             *r_type_p = BFD_RELOC_SH_GOTOFF20;
              break;
 
-           case BFD_RELOC_SH_GOTPLT32:
-             *r_type_p = BFD_RELOC_SH_GOTPLT_HI16;
+           case BFD_RELOC_SH_GOTFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
              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;
+           case BFD_RELOC_SH_GOTOFFFUNCDESC:
+             *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
              break;
 
            default:
@@ -714,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
@@ -735,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."));
@@ -757,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;
@@ -782,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 ();
@@ -821,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.  */
@@ -831,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);
        }
     }
 }
@@ -867,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;
@@ -1094,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]))
     {
@@ -1217,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:
@@ -1262,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++;
@@ -1337,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,
@@ -1422,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] == '#')
@@ -1458,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 '@'
@@ -1470,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
@@ -1483,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;
 }
 
@@ -1516,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)
@@ -1548,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:
@@ -1592,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:
@@ -1612,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:
@@ -2013,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:
       ;
@@ -2026,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,
@@ -2036,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)
 {
@@ -2073,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
@@ -2086,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
@@ -2094,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);
     }
 
@@ -2121,22 +1931,51 @@ 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
        {
@@ -2144,24 +1983,74 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
            {
            case REG_N:
            case REG_N_D:
-             nbuf[index] = reg_n;
+             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[index] = reg_n | 0x01;
+             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);
@@ -2187,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:
@@ -2209,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:
@@ -2217,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]);
@@ -2227,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;
 }
 
@@ -2240,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 == ' ')
@@ -2250,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++)
     {
@@ -2269,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.  */
@@ -2283,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 (;;)
@@ -2482,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
@@ -2542,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;
@@ -2584,39 +2491,19 @@ 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;
   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)
     {
       /* The opcode is not in the hash table.
-        This means we definately have an assembly failure,
+        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. */
@@ -2624,36 +2511,31 @@ md_assemble (char *str)
       char *name = initial_str;
       int name_length = 0;
       const sh_opcode_info *op;
-      int found = 0;
+      bool found = false;
 
-      /* identify opcode in string */
-      while (isspace (*name))
-       {
-         name++;
-       }
-      while (!isspace (name[name_length]))
-       {
-         name_length++;
-       }
+      /* Identify opcode in string.  */
+      while (ISSPACE (*name))
+       name++;
 
-      /* search for opcode in full list */
+      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)
+         if (strncasecmp (op->name, name, name_length) == 0
+             && op->name[name_length] == '\0')
            {
-             found = 1;
+             found = true;
              break;
            }
        }
 
-      if ( found )
-       {
-         as_bad (_("opcode not valid for this cpu variant"));
-       }
+      if (found)
+       as_bad (_("opcode not valid for this cpu variant"));
       else
-       {
-         as_bad (_("unknown opcode"));
-       }
+       as_bad (_("unknown opcode"));
+
       return;
     }
 
@@ -2678,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
        {
@@ -2720,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;
@@ -2748,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
@@ -2771,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
@@ -2874,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)
     {
@@ -2929,80 +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, "sh4-nofpu") == 0)
-       preset_target_arch = arch_sh4_nofpu;
-      else if (strcasecmp (arg, "sh4-nommu-nofpu") == 0)
-       preset_target_arch = arch_sh4_nommu_nofpu;
-      else if (strcasecmp (arg, "sh4a") == 0)
-       preset_target_arch = arch_sh4a;
-      else if (strcasecmp (arg, "dsp") == 0)
-       preset_target_arch = arch_sh1_up & ~arch_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;
+
+             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;
+           }
 
-    case OPTION_SHCOMPACT_CONST_CRANGE:
-      sh64_shcompact_const_crange = TRUE;
+         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;
@@ -3016,35 +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\
--isa=[sh4\n\
-    | sh4-nofpu                sh4 with fpu disabled\n\
-    | sh4-nommu-nofpu   sh4 with no MMU or FPU\n\
-    | sh4a\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\n\
-    | any]             use most appropriate isa\n"));
-#ifdef HAVE_SH64
-  fprintf (stream, _("\
--isa=[shmedia          set as the default instruction set for SH64\n\
-    | SHmedia\n\
-    | shcompact\n\
-    | SHcompact]\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\n"));
-#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
@@ -3059,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)
@@ -3085,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,
@@ -3116,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,
@@ -3158,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,
@@ -3172,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 ();
@@ -3209,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;
 
@@ -3294,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);
@@ -3310,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);
 
@@ -3364,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)
@@ -3382,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
@@ -3422,7 +3180,6 @@ void
 sh_cons_align (int nbytes)
 {
   int nalign;
-  char *p;
 
   if (sh_no_align_cons)
     {
@@ -3448,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);
 }
@@ -3491,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
@@ -3506,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
@@ -3551,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;
 
@@ -3582,51 +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_nommu_nofpu)
-    val = EF_SH4_NOMMU_NOFPU;
-  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;
@@ -3635,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.  */
@@ -3681,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);
@@ -3745,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
@@ -3788,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;
 
@@ -3824,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:
@@ -3846,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:
@@ -3855,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:
@@ -3865,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)
@@ -3888,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;
@@ -3906,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.  */
@@ -3942,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
@@ -3977,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)
@@ -4015,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 *
@@ -4184,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;
 
@@ -4194,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)
@@ -4221,16 +3875,8 @@ 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);
 
@@ -4241,7 +3887,7 @@ 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)
@@ -4253,7 +3899,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 
 #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);
 
@@ -4265,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;
@@ -4282,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);
@@ -4327,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;
 
@@ -4341,7 +3998,6 @@ sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
 
   return 1;
 }
-#endif
 
 void
 sh_cfi_frame_initial_instructions (void)
@@ -4350,13 +4006,13 @@ sh_cfi_frame_initial_instructions (void)
 }
 
 int
-sh_regname_to_dw2regnum (const char *regname)
+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[] =
+  static struct { const char *name; int dw2regnum; } regnames[] =
     {
       { "pr", 17 }, { "t", 18 }, { "gbr", 19 }, { "mach", 20 },
       { "macl", 21 }, { "fpul", 23 }
@@ -4391,4 +4047,4 @@ sh_regname_to_dw2regnum (const char *regname)
     }
   return regnum;
 }
-#endif /* BFD_ASSEMBLER */
+#endif /* OBJ_ELF */