* dw2gencfi.c (cfi_pseudo_table): Add cfi_gnu_window_save.
[binutils-gdb.git] / gas / config / tc-sh.c
index 8bb72bd146ab8ad9e526d1ae7ea8040a3ee8a283..d147df6df5b20bd315b5c72fbbaf3d603bce7d44 100644 (file)
@@ -1,5 +1,5 @@
-/* tc-sh.c -- Assemble code for the Hitachi Super-H
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+/* 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.
 
    This file is part of GAS, the GNU Assembler.
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -71,6 +71,7 @@ static void build_relax PARAMS ((sh_opcode_info *, sh_operand_info *));
 static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
 static unsigned int build_Mytes
   PARAMS ((sh_opcode_info *, sh_operand_info *));
 static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
 static unsigned int build_Mytes
   PARAMS ((sh_opcode_info *, sh_operand_info *));
+static bfd_boolean sh_local_pcrel PARAMS ((fixS *fix));
 
 #ifdef OBJ_ELF
 static void sh_elf_cons PARAMS ((int));
 
 #ifdef OBJ_ELF
 static void sh_elf_cons PARAMS ((int));
@@ -135,10 +136,6 @@ const pseudo_typeS md_pseudo_table[] =
   {"2byte", s_uacons, 2},
   {"4byte", s_uacons, 4},
   {"8byte", s_uacons, 8},
   {"2byte", s_uacons, 2},
   {"4byte", s_uacons, 4},
   {"8byte", s_uacons, 8},
-#ifdef BFD_ASSEMBLER
-  {"file", dwarf2_directive_file, 0 },
-  {"loc", dwarf2_directive_loc, 0 },
-#endif
 #ifdef HAVE_SH64
   {"mode", s_sh64_mode, 0 },
 
 #ifdef HAVE_SH64
   {"mode", s_sh64_mode, 0 },
 
@@ -162,9 +159,9 @@ int sh_relax;               /* set if -relax seen */
 
 int sh_small;
 
 
 int sh_small;
 
-/* Whether -dsp was seen.  */
+/* preset architecture set, if given; zero otherwise.  */
 
 
-static int sh_dsp;
+static int preset_target_arch;
 
 /* The bit mask of architectures that could
    accomodate the insns seen so far.  */
 
 /* The bit mask of architectures that could
    accomodate the insns seen so far.  */
@@ -825,7 +822,7 @@ sh_elf_cons (nbytes)
 #ifdef HAVE_SH64
 
   /* Update existing range to include a previous insn, if there was one.  */
 #ifdef HAVE_SH64
 
   /* Update existing range to include a previous insn, if there was one.  */
-  sh64_update_contents_mark (true);
+  sh64_update_contents_mark (TRUE);
 
   /* We need to make sure the contents type is set to data.  */
   sh64_flag_output ();
 
   /* We need to make sure the contents type is set to data.  */
   sh64_flag_output ();
@@ -862,11 +859,12 @@ sh_elf_cons (nbytes)
 void
 md_begin ()
 {
 void
 md_begin ()
 {
-  sh_opcode_info *opcode;
+  const sh_opcode_info *opcode;
   char *prev_name = "";
   int target_arch;
 
   char *prev_name = "";
   int target_arch;
 
-  target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
+  target_arch
+    = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_dsp_up;
   valid_arch = target_arch;
 
 #ifdef HAVE_SH64
   valid_arch = target_arch;
 
 #ifdef HAVE_SH64
@@ -878,19 +876,13 @@ md_begin ()
   /* Insert unique names into hash table.  */
   for (opcode = sh_table; opcode->name; opcode++)
     {
   /* Insert unique names into hash table.  */
   for (opcode = sh_table; opcode->name; opcode++)
     {
-      if (strcmp (prev_name, opcode->name))
+      if (strcmp (prev_name, opcode->name) != 0)
        {
          if (! (opcode->arch & target_arch))
            continue;
          prev_name = opcode->name;
          hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
        }
        {
          if (! (opcode->arch & target_arch))
            continue;
          prev_name = opcode->name;
          hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
        }
-      else
-       {
-         /* Make all the opcodes with the same name point to the same
-            string.  */
-         opcode->name = prev_name;
-       }
     }
 }
 
     }
 }
 
@@ -1383,10 +1375,33 @@ parse_at (src, op)
                }
              else if (mode == A_PC)
                {
                }
              else if (mode == A_PC)
                {
-                 op->type = A_DISP_PC_ABS;
-                 /* Such operands don't get corrected for PC==.+4, so
-                    make the correction here.  */
-                 op->immediate.X_add_number -= 4;
+                 /* We want @(expr, pc) to uniformly address . + expr,
+                    no matter if expr is a constant, or a more complex
+                    expression, e.g. sym-. or sym1-sym2.
+                    However, we also used to accept @(sym,pc)
+                    as adressing sym, i.e. meaning the same as plain sym.
+                    Some existing code does use the @(sym,pc) syntax, so
+                    we give it the old semantics for now, but warn about
+                    its use, so that users have some time to fix their code.
+
+                    Note that due to this backward compatibility hack,
+                    we'll get unexpected results when @(offset, pc) is used,
+                    and offset is a symbol that is set later to an an address
+                    difference, or an external symbol that is set to an
+                    address difference in another source file, so we want to
+                    eventually remove it.  */
+                 if (op->immediate.X_op == O_symbol)
+                   {
+                     op->type = A_DISP_PC;
+                     as_warn (_("Deprecated syntax."));
+                   }
+                 else
+                   {
+                     op->type = A_DISP_PC_ABS;
+                     /* Such operands don't get corrected for PC==.+4, so
+                        make the correction here.  */
+                     op->immediate.X_add_number -= 4;
+                   }
                }
              else
                {
                }
              else
                {
@@ -1550,7 +1565,7 @@ get_specific (opcode, operands)
   while (opcode->name)
     {
       this_try = opcode++;
   while (opcode->name)
     {
       this_try = opcode++;
-      if (this_try->name != name)
+      if ((this_try->name != name) && (strcmp (this_try->name, name) != 0))
        {
          /* We've looked so far down the table that we've run out of
             opcodes with the same name.  */
        {
          /* We've looked so far down the table that we've run out of
             opcodes with the same name.  */
@@ -1566,27 +1581,6 @@ get_specific (opcode, operands)
          sh_operand_info *user = operands + n;
          sh_arg_type arg = this_try->arg[n];
 
          sh_operand_info *user = operands + n;
          sh_arg_type arg = this_try->arg[n];
 
-         /* If this is a parallel insn check to see if both
-            parts have the same destination register.  */
-         if ((n == 2) && (this_try->nibbles[0] == PPI))
-           {
-             static boolean bIsPPI = false;
-             static int nLastDestReg;
-
-             if (!bIsPPI)
-               {
-                 bIsPPI = true;
-                 nLastDestReg = user->reg;
-               }
-             else /* Second insn.  */
-               {
-                 if (nLastDestReg == user->reg)
-                   as_warn (_("destination register is same for parallel insns"));
-
-                 bIsPPI = false;
-               }
-           }
-
          switch (arg)
            {
            case A_DISP_PC:
          switch (arg)
            {
            case A_DISP_PC:
@@ -2120,15 +2114,16 @@ assemble_ppi (op_end, opcode)
   int move_code;
   unsigned int size;
 
   int move_code;
   unsigned int size;
 
-  /* Some insn ignore one or more register fields, e.g. psts machl,a0.
-     Make sure we encode a defined insn pattern.  */
-  reg_x = 0;
-  reg_y = 0;
-
   for (;;)
     {
       sh_operand_info operand[3];
 
   for (;;)
     {
       sh_operand_info operand[3];
 
+      /* Some insn ignore one or more register fields, e.g. psts machl,a0.
+        Make sure we encode a defined insn pattern.  */
+      reg_x = 0;
+      reg_y = 0;
+      reg_n = 0;
+
       if (opcode->arg[0] != A_END)
        op_end = get_operands (opcode, op_end, operand);
       opcode = get_specific (opcode, operand);
       if (opcode->arg[0] != A_END)
        op_end = get_operands (opcode, op_end, operand);
       opcode = get_specific (opcode, operand);
@@ -2256,6 +2251,15 @@ assemble_ppi (op_end, opcode)
                default:
                  as_bad (_("bad padd / psub pmuls output operand"));
                }
                default:
                  as_bad (_("bad padd / psub pmuls output operand"));
                }
+               /* Generate warning if the destination register for padd / psub
+                  and pmuls is the same ( only for A0 or A1 ).
+                  If the last nibble is 1010 then A0 is used in both
+                  padd / psub and pmuls. If it is 1111 then A1 is used
+                  as destination register in both padd / psub and pmuls.  */
+
+               if ((((field_b | reg_efg) & 0x000F) == 0x000A)
+                   || (((field_b | reg_efg) & 0x000F) == 0x000F))
+                 as_warn (_("destination register is same for parallel insns"));
            }
          field_b += 0x4000 + reg_efg;
          break;
            }
          field_b += 0x4000 + reg_efg;
          break;
@@ -2341,13 +2345,13 @@ md_assemble (str)
     {
       /* If we've seen pseudo-directives, make sure any emitted data or
         frags are marked as data.  */
     {
       /* If we've seen pseudo-directives, make sure any emitted data or
         frags are marked as data.  */
-      if (seen_insn == false)
+      if (!seen_insn)
        {
        {
-         sh64_update_contents_mark (true);
+         sh64_update_contents_mark (TRUE);
          sh64_set_contents_type (CRT_SH5_ISA16);
        }
 
          sh64_set_contents_type (CRT_SH5_ISA16);
        }
 
-      seen_insn = true;
+      seen_insn = TRUE;
     }
 #endif /* HAVE_SH64 */
 
     }
 #endif /* HAVE_SH64 */
 
@@ -2379,6 +2383,12 @@ md_assemble (str)
       if (opcode->arg[0] == A_BDISP12
          || opcode->arg[0] == A_BDISP8)
        {
       if (opcode->arg[0] == A_BDISP12
          || opcode->arg[0] == A_BDISP8)
        {
+         /* Since we skip get_specific here, we have to check & update
+            valid_arch now.  */
+         if (valid_arch & opcode->arch)
+           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]);
        }
          parse_exp (op_end + 1, &operand[0]);
          build_relax (opcode, &operand[0]);
        }
@@ -2474,14 +2484,14 @@ md_undefined_symbol (name)
 
 void
 tc_crawl_symbol_chain (headers)
 
 void
 tc_crawl_symbol_chain (headers)
-     object_headers *headers;
+     object_headers *headers ATTRIBUTE_UNUSED;
 {
   printf (_("call to tc_crawl_symbol_chain \n"));
 }
 
 void
 tc_headers_hook (headers)
 {
   printf (_("call to tc_crawl_symbol_chain \n"));
 }
 
 void
 tc_headers_hook (headers)
-     object_headers *headers;
+     object_headers *headers ATTRIBUTE_UNUSED;
 {
   printf (_("call to tc_headers_hook \n"));
 }
 {
   printf (_("call to tc_headers_hook \n"));
 }
@@ -2578,7 +2588,7 @@ s_uses (ignore)
   demand_empty_rest_of_line ();
 }
 \f
   demand_empty_rest_of_line ();
 }
 \f
-CONST char *md_shortopts = "";
+const char *md_shortopts = "";
 struct option md_longopts[] =
 {
 #define OPTION_RELAX  (OPTION_MD_BASE)
 struct option md_longopts[] =
 {
 #define OPTION_RELAX  (OPTION_MD_BASE)
@@ -2586,20 +2596,20 @@ struct option md_longopts[] =
 #define OPTION_LITTLE (OPTION_BIG + 1)
 #define OPTION_SMALL (OPTION_LITTLE + 1)
 #define OPTION_DSP (OPTION_SMALL + 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},
   {"small", no_argument, NULL, OPTION_SMALL},
   {"dsp", no_argument, NULL, OPTION_DSP},
 
   {"relax", no_argument, NULL, OPTION_RELAX},
   {"big", no_argument, NULL, OPTION_BIG},
   {"little", no_argument, NULL, OPTION_LITTLE},
   {"small", no_argument, NULL, OPTION_SMALL},
   {"dsp", no_argument, NULL, OPTION_DSP},
+  {"isa",                    required_argument, NULL, OPTION_ISA},
 #ifdef HAVE_SH64
 #ifdef HAVE_SH64
-#define OPTION_ISA                    (OPTION_DSP + 1)
 #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)
 #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)
-  {"isa",                    required_argument, NULL, OPTION_ISA},
   {"abi",                    required_argument, NULL, OPTION_ABI},
   {"no-mix",                 no_argument, NULL, OPTION_NO_MIX},
   {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
   {"abi",                    required_argument, NULL, OPTION_ABI},
   {"no-mix",                 no_argument, NULL, OPTION_NO_MIX},
   {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
@@ -2635,12 +2645,16 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_DSP:
       break;
 
     case OPTION_DSP:
-      sh_dsp = 1;
+      preset_target_arch = arch_sh1_up & ~arch_sh3e_up;
       break;
 
       break;
 
-#ifdef HAVE_SH64
     case OPTION_ISA:
     case OPTION_ISA:
-      if (strcasecmp (arg, "shmedia") == 0)
+      if (strcasecmp (arg, "sh4") == 0)
+       preset_target_arch = arch_sh4;
+      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"));
        {
          if (sh64_isa_mode == sh64_isa_shcompact)
            as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
@@ -2654,10 +2668,12 @@ md_parse_option (c, arg)
            as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
          sh64_isa_mode = sh64_isa_shcompact;
        }
            as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
          sh64_isa_mode = sh64_isa_shcompact;
        }
+#endif /* HAVE_SH64 */
       else
        as_bad ("Invalid argument to --isa option: %s", arg);
       break;
 
       else
        as_bad ("Invalid argument to --isa option: %s", arg);
       break;
 
+#ifdef HAVE_SH64
     case OPTION_ABI:
       if (strcmp (arg, "32") == 0)
        {
     case OPTION_ABI:
       if (strcmp (arg, "32") == 0)
        {
@@ -2678,19 +2694,19 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_NO_MIX:
       break;
 
     case OPTION_NO_MIX:
-      sh64_mix = false;
+      sh64_mix = FALSE;
       break;
 
     case OPTION_SHCOMPACT_CONST_CRANGE:
       break;
 
     case OPTION_SHCOMPACT_CONST_CRANGE:
-      sh64_shcompact_const_crange = true;
+      sh64_shcompact_const_crange = TRUE;
       break;
 
     case OPTION_NO_EXPAND:
       break;
 
     case OPTION_NO_EXPAND:
-      sh64_expand = false;
+      sh64_expand = FALSE;
       break;
 
     case OPTION_PT32:
       break;
 
     case OPTION_PT32:
-      sh64_pt32 = true;
+      sh64_pt32 = TRUE;
       break;
 #endif /* HAVE_SH64 */
 
       break;
 #endif /* HAVE_SH64 */
 
@@ -2711,7 +2727,7 @@ SH options:\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\
 -big                   generate big endian code\n\
 -relax                 alter jump instructions for long displacements\n\
 -small                 align sections to 4 byte boundaries, not 16\n\
--dsp                   enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
+-dsp                   enable sh-dsp insns, and disable sh2e/sh3e/sh4 insns.\n"));
 #ifdef HAVE_SH64
   fprintf (stream, _("\
 -isa=[shmedia          set default instruction set for SH64\n\
 #ifdef HAVE_SH64
   fprintf (stream, _("\
 -isa=[shmedia          set default instruction set for SH64\n\
@@ -2924,7 +2940,7 @@ md_convert_frag (headers, seg, fragP)
 #ifdef BFD_ASSEMBLER
      bfd *headers ATTRIBUTE_UNUSED;
 #else
 #ifdef BFD_ASSEMBLER
      bfd *headers ATTRIBUTE_UNUSED;
 #else
-     object_headers *headers;
+     object_headers *headers ATTRIBUTE_UNUSED;
 #endif
      segT seg;
      fragS *fragP;
 #endif
      segT seg;
      fragS *fragP;
@@ -3055,7 +3071,7 @@ md_convert_frag (headers, seg, fragP)
 
     default:
 #ifdef HAVE_SH64
 
     default:
 #ifdef HAVE_SH64
-      shmedia_md_convert_frag (headers, seg, fragP, true);
+      shmedia_md_convert_frag (headers, seg, fragP, TRUE);
 #else
       abort ();
 #endif
 #else
       abort ();
 #endif
@@ -3199,30 +3215,21 @@ sh_handle_align (frag)
             BFD_RELOC_SH_ALIGN);
 }
 
             BFD_RELOC_SH_ALIGN);
 }
 
-/* This macro decides whether a particular reloc is an entry in a
-   switch table.  It is used when relaxing, because the linker needs
-   to know about all such entries so that it can adjust them if
-   necessary.  */
+/* See whether the relocation should be resolved locally.  */
 
 
-#ifdef BFD_ASSEMBLER
-#define SWITCH_TABLE_CONS(fix) (0)
-#else
-#define SWITCH_TABLE_CONS(fix)                         \
-  ((fix)->fx_r_type == 0                               \
-   && ((fix)->fx_size == 2                             \
-       || (fix)->fx_size == 1                          \
-       || (fix)->fx_size == 4))
-#endif
-
-#define SWITCH_TABLE(fix)                              \
-  ((fix)->fx_addsy != NULL                             \
-   && (fix)->fx_subsy != NULL                          \
-   && S_GET_SEGMENT ((fix)->fx_addsy) == text_section  \
-   && S_GET_SEGMENT ((fix)->fx_subsy) == text_section  \
-   && ((fix)->fx_r_type == BFD_RELOC_32                        \
-       || (fix)->fx_r_type == BFD_RELOC_16             \
-       || (fix)->fx_r_type == BFD_RELOC_8              \
-       || SWITCH_TABLE_CONS (fix)))
+static bfd_boolean
+sh_local_pcrel (fix)
+     fixS *fix;
+{
+  return (! sh_relax
+         && (fix->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+             || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+             || fix->fx_r_type == BFD_RELOC_8_PCREL
+             || fix->fx_r_type == BFD_RELOC_SH_SWITCH16
+             || fix->fx_r_type == BFD_RELOC_SH_SWITCH32));
+}
 
 /* See whether we need to force a relocation into the output file.
    This is used to force out switch and PC relative relocations when
 
 /* See whether we need to force a relocation into the output file.
    This is used to force out switch and PC relative relocations when
@@ -3232,11 +3239,20 @@ int
 sh_force_relocation (fix)
      fixS *fix;
 {
 sh_force_relocation (fix)
      fixS *fix;
 {
+  /* These relocations can't make it into a DSO, so no use forcing
+     them for global symbols.  */
+  if (sh_local_pcrel (fix))
+    return 0;
 
 
-  if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY
-      || fix->fx_r_type == BFD_RELOC_SH_LOOP_START
-      || fix->fx_r_type == BFD_RELOC_SH_LOOP_END)
+  /* Make sure some relocations get emitted.  */
+  if (fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+      || fix->fx_r_type == BFD_RELOC_SH_LOOP_END
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_GD_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LD_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_IE_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LDO_32
+      || fix->fx_r_type == BFD_RELOC_SH_TLS_LE_32
+      || generic_force_reloc (fix))
     return 1;
 
   if (! sh_relax)
     return 1;
 
   if (! sh_relax)
@@ -3255,24 +3271,13 @@ sh_force_relocation (fix)
 }
 
 #ifdef OBJ_ELF
 }
 
 #ifdef OBJ_ELF
-boolean
+bfd_boolean
 sh_fix_adjustable (fixP)
    fixS *fixP;
 {
 sh_fix_adjustable (fixP)
    fixS *fixP;
 {
-
-  if (fixP->fx_addsy == NULL)
-    return 1;
-
-  if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
-      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
-      || fixP->fx_r_type == BFD_RELOC_8_PCREL
-      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16
-      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32)
-    return 1;
-
-  if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP)
+  if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
       || fixP->fx_r_type == BFD_RELOC_RVA)
     return 0;
 
       || fixP->fx_r_type == BFD_RELOC_RVA)
     return 0;
 
@@ -3302,6 +3307,8 @@ sh_elf_final_processing ()
     val = EF_SH1;
   else if (valid_arch & arch_sh2)
     val = EF_SH2;
     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)
   else if (valid_arch & arch_sh_dsp)
     val = EF_SH_DSP;
   else if (valid_arch & arch_sh3)
@@ -3383,7 +3390,10 @@ md_apply_fix3 (fixP, valP, seg)
     val -= S_GET_VALUE  (fixP->fx_addsy);
 #endif
 
     val -= S_GET_VALUE  (fixP->fx_addsy);
 #endif
 
-#ifndef BFD_ASSEMBLER
+#ifdef BFD_ASSEMBLER
+  if (SWITCH_TABLE (fixP))
+    val -= S_GET_VALUE  (fixP->fx_subsy);
+#else
   if (fixP->fx_r_type == 0)
     {
       if (fixP->fx_size == 2)
   if (fixP->fx_r_type == 0)
     {
       if (fixP->fx_size == 2)
@@ -3518,9 +3528,10 @@ md_apply_fix3 (fixP, valP, seg)
       /* Make the jump instruction point to the address of the operand.  At
         runtime we merely add the offset to the actual PLT entry.  */
       * valP = 0xfffffffc;
       /* Make the jump instruction point to the address of the operand.  At
         runtime we merely add the offset to the actual PLT entry.  */
       * valP = 0xfffffffc;
-      val = fixP->fx_addnumber;
+      val = fixP->fx_offset;
       if (fixP->fx_subsy)
        val -= S_GET_VALUE (fixP->fx_subsy);
       if (fixP->fx_subsy)
        val -= S_GET_VALUE (fixP->fx_subsy);
+      fixP->fx_addnumber = val;
       md_number_to_chars (buf, val, 4);
       break;
 
       md_number_to_chars (buf, val, 4);
       break;
 
@@ -3545,12 +3556,21 @@ md_apply_fix3 (fixP, valP, seg)
       md_number_to_chars (buf, val, 4);
       break;
 
       md_number_to_chars (buf, val, 4);
       break;
 
+    case BFD_RELOC_SH_TLS_GD_32:
+    case BFD_RELOC_SH_TLS_LD_32:
+    case BFD_RELOC_SH_TLS_IE_32:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* Fallthrough */
     case BFD_RELOC_32_GOT_PCREL:
     case BFD_RELOC_SH_GOTPLT32:
       * valP = 0; /* Fully resolved at runtime.  No addend.  */
       md_number_to_chars (buf, 0, 4);
       break;
 
     case BFD_RELOC_32_GOT_PCREL:
     case BFD_RELOC_SH_GOTPLT32:
       * valP = 0; /* Fully resolved at runtime.  No addend.  */
       md_number_to_chars (buf, 0, 4);
       break;
 
+    case BFD_RELOC_SH_TLS_LDO_32:
+    case BFD_RELOC_SH_TLS_LE_32:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* Fallthrough */
     case BFD_RELOC_32_GOTOFF:
       md_number_to_chars (buf, val, 4);
       break;
     case BFD_RELOC_32_GOTOFF:
       md_number_to_chars (buf, val, 4);
       break;
@@ -3680,15 +3700,24 @@ md_number_to_chars (ptr, use, nbytes)
     number_to_chars_bigendian (ptr, use, nbytes);
 }
 
     number_to_chars_bigendian (ptr, use, nbytes);
 }
 
+/* This version is used in obj-coff.c when not using BFD_ASSEMBLER.
+   eg for the sh-hms target.  */
+
+long
+md_pcrel_from (fixP)
+     fixS *fixP;
+{
+  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
 long
 md_pcrel_from_section (fixP, sec)
      fixS *fixP;
      segT sec;
 {
 long
 md_pcrel_from_section (fixP, sec)
      fixS *fixP;
      segT sec;
 {
-  if (fixP->fx_addsy != (symbolS *) NULL
-      && (! S_IS_DEFINED (fixP->fx_addsy)
-         || S_IS_EXTERN (fixP->fx_addsy)
-         || S_IS_WEAK (fixP->fx_addsy)
+  if (! sh_local_pcrel (fixP)
+      && fixP->fx_addsy != (symbolS *) NULL
+      && (generic_force_reloc (fixP)
          || S_GET_SEGMENT (fixP->fx_addsy) != sec))
     {
       /* The symbol is undefined (or is defined but not in this section,
          || S_GET_SEGMENT (fixP->fx_addsy) != sec))
     {
       /* The symbol is undefined (or is defined but not in this section,
@@ -3698,7 +3727,7 @@ md_pcrel_from_section (fixP, sec)
       return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
     }
 
       return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
     }
 
-  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+  return md_pcrel_from (fixP);
 }
 
 #ifdef OBJ_COFF
 }
 
 #ifdef OBJ_COFF
@@ -3881,18 +3910,12 @@ tc_gen_reloc (section, fixp)
   *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
   *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
-  if (fixp->fx_subsy
-      && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
-    {
-      fixp->fx_addnumber -= S_GET_VALUE (fixp->fx_subsy);
-      fixp->fx_subsy = 0;
-    }
-
   r_type = fixp->fx_r_type;
 
   if (SWITCH_TABLE (fixp))
     {
   r_type = fixp->fx_r_type;
 
   if (SWITCH_TABLE (fixp))
     {
-      rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+      *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+      rel->addend = 0;
       if (r_type == BFD_RELOC_16)
        r_type = BFD_RELOC_SH_SWITCH16;
       else if (r_type == BFD_RELOC_8)
       if (r_type == BFD_RELOC_16)
        r_type = BFD_RELOC_SH_SWITCH16;
       else if (r_type == BFD_RELOC_8)
@@ -3931,7 +3954,11 @@ tc_gen_reloc (section, fixp)
     rel->addend = 0;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
     rel->addend = 0;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
-  if (rel->howto == NULL || fixp->fx_subsy)
+#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,
                    _("Cannot represent relocation type %s"),
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("Cannot represent relocation type %s"),
@@ -4014,6 +4041,16 @@ sh_parse_name (name, exprP, nextcharP)
     reloc_type = BFD_RELOC_32_GOT_PCREL;
   else if ((next_end = sh_end_of_match (next + 1, "PLT")))
     reloc_type = BFD_RELOC_32_PLT_PCREL;
     reloc_type = BFD_RELOC_32_GOT_PCREL;
   else if ((next_end = sh_end_of_match (next + 1, "PLT")))
     reloc_type = BFD_RELOC_32_PLT_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "TLSGD")))
+    reloc_type = BFD_RELOC_SH_TLS_GD_32;
+  else if ((next_end = sh_end_of_match (next + 1, "TLSLDM")))
+    reloc_type = BFD_RELOC_SH_TLS_LD_32;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTTPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_IE_32;
+  else if ((next_end = sh_end_of_match (next + 1, "TPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_LE_32;
+  else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
+    reloc_type = BFD_RELOC_SH_TLS_LDO_32;
   else
     goto no_suffix;
 
   else
     goto no_suffix;