Prepare for multi-pass relaxation.
[deliverable/binutils-gdb.git] / gas / config / tc-sh.c
index 777d76cf248000c89d56359e1bb668e47047aaea..4ae801f69b534b18da6f7f8aeacad2f26095673c 100644 (file)
@@ -1,5 +1,6 @@
 /* tc-sh.c -- Assemble code for the Hitachi Super-H
-   Copyright (C) 1993, 94, 95, 96, 97, 98, 1999 Free Software Foundation.
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+   Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    the Free Software Foundation, 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-/*
-   Written By Steve Chamberlain
-   sac@cygnus.com
- */
+/* Written By Steve Chamberlain <sac@cygnus.com>  */
 
 #include <stdio.h>
 #include "as.h"
 #define DEFINE_TABLE
 #include "opcodes/sh-opc.h"
 #include <ctype.h>
+#include "struc-symbol.h"
 
 #ifdef OBJ_ELF
 #include "elf/sh.h"
 #endif
 
+#include "dwarf2dbg.h"
+
 const char comment_chars[] = "!";
 const char line_separator_chars[] = ";";
 const char line_comment_chars[] = "!#";
@@ -48,13 +49,19 @@ void cons ();
 void s_align_bytes ();
 static void s_uacons PARAMS ((int));
 static sh_opcode_info *find_cooked_opcode PARAMS ((char **));
-static void assemble_ppi PARAMS ((char *, sh_opcode_info *));
+static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *));
+
+#ifdef OBJ_ELF
+static void sh_elf_cons PARAMS ((int));
+
+symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
 
 int shl = 0;
 
 static void
 little (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   shl = 1;
   target_big_endian = 0;
@@ -64,13 +71,19 @@ little (ignore)
    has to support.  The fields are:
    pseudo-op name without dot
    function to call to execute this pseudo-op
-   Integer arg to pass to the function
- */
+   Integer arg to pass to the function.  */
 
 const pseudo_typeS md_pseudo_table[] =
 {
+#ifdef OBJ_ELF
+  {"long", sh_elf_cons, 4},
+  {"int", sh_elf_cons, 4},
+  {"word", sh_elf_cons, 2},
+  {"short", sh_elf_cons, 2},
+#else
   {"int", cons, 4},
   {"word", cons, 2},
+#endif /* OBJ_ELF */
   {"form", listing_psize, 0},
   {"little", little, 0},
   {"heading", listing_title, 0},
@@ -80,6 +93,12 @@ const pseudo_typeS md_pseudo_table[] =
   {"uses", s_uses, 0},
   {"uaword", s_uacons, 2},
   {"ualong", s_uacons, 4},
+  {"uaquad", s_uacons, 8},
+  {"2byte", s_uacons, 2},
+  {"4byte", s_uacons, 4},
+  {"8byte", s_uacons, 8},
+  {"file", dwarf2_directive_file, 0 },
+  {"loc", dwarf2_directive_loc, 0 },
   {0, 0, 0}
 };
 
@@ -101,18 +120,17 @@ static int valid_arch;
 
 const char EXP_CHARS[] = "eE";
 
-/* Chars that mean this number is a floating point constant */
+/* Chars that mean this number is a floating point constant */
 /* As in 0f12.456 */
 /* or    0d1.2345e12 */
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 #define C(a,b) ENCODE_RELAX(a,b)
 
-#define JREG 14                        /* Register used as a temp when relaxing */
 #define ENCODE_RELAX(what,length) (((what) << 4) + (length))
 #define GET_WHAT(x) ((x>>4))
 
-/* These are the three types of relaxable instrction */
+/* These are the three types of relaxable instrction */
 #define COND_JUMP 1
 #define COND_JUMP_DELAY 2
 #define UNCOND_JUMP  3
@@ -159,45 +177,232 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define UNCOND32_M -(1<<30)
 #define UNCOND32_LENGTH 14
 
+#define EMPTY { 0, 0, 0, 0 }
+
 const relax_typeS md_relax_table[C (END, 0)] = {
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
-  { 0 },
+  EMPTY,
   /* C (COND_JUMP, COND8) */
   { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP, COND12) },
   /* C (COND_JUMP, COND12) */
   { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
   /* C (COND_JUMP, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  { 0 }, { 0 }, { 0 }, { 0 },
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
-  { 0 },
+  EMPTY,
   /* C (COND_JUMP_DELAY, COND8) */
   { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP_DELAY, COND12) },
   /* C (COND_JUMP_DELAY, COND12) */
   { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
   /* C (COND_JUMP_DELAY, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  { 0 }, { 0 }, { 0 }, { 0 },
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
-  { 0 },
+  EMPTY,
   /* C (UNCOND_JUMP, UNCOND12) */
   { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
   /* C (UNCOND_JUMP, UNCOND32) */
   { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
-  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 };
 
+#undef EMPTY
+
 static struct hash_control *opcode_hash_control;       /* Opcode mnemonics */
 
-/*
-   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
- */
+\f
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation.
+   If we have additional arithmetic expression, then we fill in new_exp_p.  */
+static bfd_reloc_code_real_type
+sh_elf_suffix (str_p, exp_p, new_exp_p)
+     char **str_p;
+     expressionS *exp_p, *new_exp_p;
+{
+  struct map_bfd {
+    char *string;
+    int length;
+    bfd_reloc_code_real_type reloc;
+  };
+
+  char ident[20];
+  char *str = *str_p;
+  char *str2;
+  int ch;
+  int len;
+  struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof (str)-1, reloc }
+
+  static struct map_bfd mapping[] = {
+    MAP ("got",                BFD_RELOC_32_GOT_PCREL),
+    MAP ("plt",                BFD_RELOC_32_PLT_PCREL),
+    MAP ("gotoff",     BFD_RELOC_32_GOTOFF),
+    { (char *)0,       0,      BFD_RELOC_UNUSED }
+  };
+
+  if (*str++ != '@')
+    return BFD_RELOC_UNUSED;
+
+  for (ch = *str, str2 = ident;
+       (str2 < ident + sizeof (ident) - 1
+       && (isalnum (ch) || ch == '@'));
+       ch = *++str)
+    {
+      *str2++ = (islower (ch)) ? ch : tolower (ch);
+    }
+
+  *str2 = '\0';
+  len = str2 - ident;
+
+  ch = ident[0];
+  for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+    if (ch == ptr->string[0]
+       && len == ptr->length
+       && memcmp (ident, ptr->string, ptr->length) == 0)
+      {
+       /* Now check for identifier@suffix+constant */
+       if (*str == '-' || *str == '+')
+         {
+           char *orig_line = input_line_pointer;
+
+           input_line_pointer = str;
+           expression (new_exp_p);
+           if (new_exp_p->X_op == O_constant)
+             {
+               exp_p->X_add_number += new_exp_p->X_add_number;
+               str = input_line_pointer;
+             }
+           if (new_exp_p->X_op == O_subtract)
+             str = input_line_pointer;
+
+           if (&input_line_pointer != str_p)
+             input_line_pointer = orig_line;
+         }
+
+       *str_p = str;
+       return ptr->reloc;
+      }
+
+  return BFD_RELOC_UNUSED;
+}
+
+/* The regular cons() function, that reads constants, doesn't support
+   suffixes such as @GOT, @GOTOFF and @PLT, that generate
+   machine-specific relocation types.  So we must define it here.  */
+/* Clobbers input_line_pointer, checks end-of-line.  */
+static void
+sh_elf_cons (nbytes)
+     register int nbytes;      /* 1=.byte, 2=.word, 4=.long */
+{
+  expressionS exp, new_exp;
+  bfd_reloc_code_real_type reloc;
+  const char *name;
+
+  if (is_it_end_of_statement ())
+    {
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  do
+    {
+      expression (&exp);
+      new_exp.X_op = O_absent;
+      new_exp.X_add_symbol = new_exp.X_op_symbol = NULL;
+      /* If the _GLOBAL_OFFSET_TABLE_ symbol hasn't been found yet,
+        use the name of the symbol to tell whether it's the
+        _GLOBAL_OFFSET_TABLE_.  If it has, comparing the symbols is
+        sufficient.  */
+      if (! GOT_symbol && exp.X_add_symbol)
+       name = S_GET_NAME (exp.X_add_symbol);
+      else
+       name = NULL;
+      /* Check whether this expression involves the
+        _GLOBAL_OFFSET_TABLE_ symbol, by itself or added to a
+        difference of two other symbols.  */
+      if (((GOT_symbol && GOT_symbol == exp.X_add_symbol)
+          || (! GOT_symbol && name
+              && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
+         && (exp.X_op == O_symbol
+             || (exp.X_op == O_add
+                 && ((symbol_get_value_expression (exp.X_op_symbol)->X_op)
+                     == O_subtract))))
+       {
+         reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
+                                                                BFD_RELOC_32);
+         int size = bfd_get_reloc_size (reloc_howto);
+
+         if (GOT_symbol == NULL)
+           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+         if (size > nbytes)
+           as_bad (_("%s relocations do not fit in %d bytes\n"),
+                   reloc_howto->name, nbytes);
+         else
+           {
+             register char *p = frag_more ((int) nbytes);
+             int offset = nbytes - size;
+
+             fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+                          size, &exp, 0, TC_RELOC_GLOBAL_OFFSET_TABLE);
+           }
+       }
+      /* Check if this symbol involves one of the magic suffixes, such
+        as @GOT, @GOTOFF or @PLT, and determine which relocation type
+        to use.  */
+      else if ((exp.X_op == O_symbol || (exp.X_op == O_add && exp.X_op_symbol))
+         && *input_line_pointer == '@'
+         && ((reloc = sh_elf_suffix (&input_line_pointer, &exp, &new_exp))
+             != BFD_RELOC_UNUSED))
+       {
+         reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
+                                                                reloc);
+         int size = bfd_get_reloc_size (reloc_howto);
+
+         /* Force a GOT to be generated.  */
+         if (GOT_symbol == NULL)
+           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+         if (size > nbytes)
+           as_bad (_("%s relocations do not fit in %d bytes\n"),
+                   reloc_howto->name, nbytes);
+         else
+           {
+             register char *p = frag_more ((int) nbytes);
+             int offset = nbytes - size;
+
+             fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+                          &exp, 0, reloc);
+             if (new_exp.X_op != O_absent)
+               fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+                            &new_exp, 0, BFD_RELOC_32);
+           }
+       }
+      else
+       emit_expr (&exp, (unsigned int) nbytes);
+    }
+  while (*input_line_pointer++ == ',');
+
+  input_line_pointer--;                /* Put terminator back into stream.  */
+  if (*input_line_pointer == '#' || *input_line_pointer == '!')
+    {
+       while (! is_end_of_line[(unsigned char) *input_line_pointer++]);
+    }
+  else
+    demand_empty_rest_of_line ();
+}
+#endif /* OBJ_ELF */
+
+\f
+/* This function is called once, at assembler startup time.  This should
+   set up all the tables, etc that the MD part of the assembler needs.  */
 
 void
 md_begin ()
@@ -206,26 +411,33 @@ md_begin ()
   char *prev_name = "";
   int target_arch;
 
+#ifdef TE_PE
+  /* The WinCE OS only supports little endian executables.  */
+  target_big_endian = 0;
+#else
   if (! shl)
     target_big_endian = 1;
+#endif
 
   target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
   valid_arch = target_arch;
 
   opcode_hash_control = hash_new ();
 
-  /* Insert unique names into hash table */
+  /* Insert unique names into hash table */
   for (opcode = sh_table; opcode->name; opcode++)
     {
-      if (opcode->arch & target_arch && strcmp (prev_name, opcode->name))
+      if (strcmp (prev_name, opcode->name))
        {
+         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 */
+            string */
          opcode->name = prev_name;
        }
     }
@@ -237,92 +449,96 @@ static int reg_x, reg_y;
 static int reg_efg;
 static int reg_b;
 
-static expressionS immediate;  /* absolute expression */
-
 typedef struct
   {
     sh_arg_type type;
     int reg;
+    expressionS immediate;
   }
-
 sh_operand_info;
 
-/* try and parse a reg name, returns number of chars consumed */
+#define IDENT_CHAR(c) (isalnum (c) || (c) == '_')
+
+/* Try to parse a reg name.  Return the number of chars consumed.  */
+
 static int
 parse_reg (src, mode, reg)
      char *src;
      int *mode;
      int *reg;
 {
-  /* We use !isalnum for the next character after the register name, to
+  char l0 = tolower (src[0]);
+  char l1 = l0 ? tolower (src[1]) : 0;
+
+  /* We use ! IDENT_CHAR for the next character after the register name, to
      make sure that we won't accidentally recognize a symbol name such as
-     'sram' as being a reference to the register 'sr'.  */
+     'sram' or sr_ram as being a reference to the register 'sr'.  */
 
-  if (src[0] == 'r')
+  if (l0 == 'r')
     {
-      if (src[1] == '1')
+      if (l1 == '1')
        {
          if (src[2] >= '0' && src[2] <= '5'
-             && ! isalnum ((unsigned char) src[3]))
+             && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = A_REG_N;
              *reg = 10 + src[2] - '0';
              return 3;
            }
        }
-      if (src[1] >= '0' && src[1] <= '9'
-         && ! isalnum ((unsigned char) src[2]))
+      if (l1 >= '0' && l1 <= '9'
+         && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_REG_N;
-         *reg = (src[1] - '0');
+         *reg = (l1 - '0');
          return 2;
        }
-      if (src[1] >= '0' && src[1] <= '7' && strncmp(&src[2], "_bank", 5) == 0
-         && ! isalnum ((unsigned char) src[7]))
+      if (l1 >= '0' && l1 <= '7' && strncasecmp (&src[2], "_bank", 5) == 0
+         && ! IDENT_CHAR ((unsigned char) src[7]))
        {
          *mode = A_REG_B;
-         *reg  = (src[1] - '0');
+         *reg  = (l1 - '0');
          return 7;
        }
 
-      if (src[1] == 'e' && ! isalnum ((unsigned char) src[2]))
+      if (l1 == 'e' && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_RE;
          return 2;
        }
-      if (src[1] == 's' && ! isalnum ((unsigned char) src[2]))
+      if (l1 == 's' && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_RS;
          return 2;
        }
     }
 
-  if (src[0] == 'a')
+  if (l0 == 'a')
     {
-      if (src[1] == '0')
+      if (l1 == '0')
        {
-         if (! isalnum ((unsigned char) src[2]))
+         if (! IDENT_CHAR ((unsigned char) src[2]))
            {
              *mode = DSP_REG_N;
              *reg = A_A0_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! isalnum ((unsigned char) src[3]))
+         if (tolower (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = DSP_REG_N;
              *reg = A_A0G_NUM;
              return 3;
            }
        }
-      if (src[1] == '1')
+      if (l1 == '1')
        {
-         if (! isalnum ((unsigned char) src[2]))
+         if (! IDENT_CHAR ((unsigned char) src[2]))
            {
              *mode = DSP_REG_N;
              *reg = A_A1_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! isalnum ((unsigned char) src[3]))
+         if (tolower (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = DSP_REG_N;
              *reg = A_A1G_NUM;
@@ -330,24 +546,24 @@ parse_reg (src, mode, reg)
            }
        }
 
-      if (src[1] == 'x' && src[2] >= '0' && src[2] <= '1'
-         && ! isalnum ((unsigned char) src[3]))
+      if (l1 == 'x' && src[2] >= '0' && src[2] <= '1'
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = A_REG_N;
-         *reg = 4 + (src[1] - '0');
+         *reg = 4 + (l1 - '0');
          return 3;
        }
-      if (src[1] == 'y' && src[2] >= '0' && src[2] <= '1'
-         && ! isalnum ((unsigned char) src[3]))
+      if (l1 == 'y' && src[2] >= '0' && src[2] <= '1'
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = A_REG_N;
-         *reg = 6 + (src[1] - '0');
+         *reg = 6 + (l1 - '0');
          return 3;
        }
-      if (src[1] == 's' && src[2] >= '0' && src[2] <= '3'
-         && ! isalnum ((unsigned char) src[3]))
+      if (l1 == 's' && src[2] >= '0' && src[2] <= '3'
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
-         int n = src[1] - '0';
+         int n = l1 - '0';
 
          *mode = A_REG_N;
          *reg = n | ((~n & 2) << 1);
@@ -355,21 +571,21 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'i' && src[1] && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[3]))
     {
-      if (src[1] == 's')
+      if (l1 == 's')
        {
          *mode = A_REG_N;
          *reg = 8;
          return 2;
        }
-      if (src[1] == 'x')
+      if (l1 == 'x')
        {
          *mode = A_REG_N;
          *reg = 8;
          return 2;
        }
-      if (src[1] == 'y')
+      if (l1 == 'y')
        {
          *mode = A_REG_N;
          *reg = 9;
@@ -377,128 +593,130 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'x' && src[1] >= '0' && src[1] <= '1'
-      && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 'x' && l1 >= '0' && l1 <= '1'
+      && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = A_X0_NUM + src[1] - '0';
+      *reg = A_X0_NUM + l1 - '0';
       return 2;
     }
 
-  if (src[0] == 'y' && src[1] >= '0' && src[1] <= '1'
-      && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 'y' && l1 >= '0' && l1 <= '1'
+      && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = A_Y0_NUM + src[1] - '0';
+      *reg = A_Y0_NUM + l1 - '0';
       return 2;
     }
 
-  if (src[0] == 'm' && src[1] >= '0' && src[1] <= '1'
-      && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 'm' && l1 >= '0' && l1 <= '1'
+      && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = DSP_REG_N;
-      *reg = src[1] == '0' ? A_M0_NUM : A_M1_NUM;
+      *reg = l1 == '0' ? A_M0_NUM : A_M1_NUM;
       return 2;
     }
 
-  if (src[0] == 's'
-      && src[1] == 's'
-      && src[2] == 'r' && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 's'
+      && l1 == 's'
+      && tolower (src[2]) == 'r' && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SSR;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'p' && src[2] == 'c'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 's' && l1 == 'p' && tolower (src[2]) == 'c'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SPC;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'g' && src[2] == 'r'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 's' && l1 == 'g' && tolower (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_SGR;
       return 3;
     }
 
-  if (src[0] == 'd' && src[1] == 's' && src[2] == 'r'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 'd' && l1 == 's' && tolower (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_DSR;
       return 3;
     }
 
-  if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 'd' && l1 == 'b' && tolower (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_DBR;
       return 3;
     }
 
-  if (src[0] == 's' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 's' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_SR;
       return 2;
     }
 
-  if (src[0] == 's' && src[1] == 'p' && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 's' && l1 == 'p' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_REG_N;
       *reg = 15;
       return 2;
     }
 
-  if (src[0] == 'p' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 'p' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       *mode = A_PR;
       return 2;
     }
-  if (src[0] == 'p' && src[1] == 'c' && ! isalnum ((unsigned char) src[2]))
+  if (l0 == 'p' && l1 == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
-      *mode = A_DISP_PC;
+      /* Don't use A_DISP_PC here - that would accept stuff like 'mova pc,r0'
+         and use an uninitialized immediate.  */
+      *mode = A_PC;
       return 2;
     }
-  if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 'g' && l1 == 'b' && tolower (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_GBR;
       return 3;
     }
-  if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r'
-      && ! isalnum ((unsigned char) src[3]))
+  if (l0 == 'v' && l1 == 'b' && tolower (src[2]) == 'r'
+      && ! IDENT_CHAR ((unsigned char) src[3]))
     {
       *mode = A_VBR;
       return 3;
     }
 
-  if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c'
-      && ! isalnum ((unsigned char) src[4]))
+  if (l0 == 'm' && l1 == 'a' && tolower (src[2]) == 'c'
+      && ! IDENT_CHAR ((unsigned char) src[4]))
     {
-      if (src[3] == 'l')
+      if (tolower (src[3]) == 'l')
        {
          *mode = A_MACL;
          return 4;
        }
-      if (src[3] == 'h')
+      if (tolower (src[3]) == 'h')
        {
          *mode = A_MACH;
          return 4;
        }
     }
-  if (src[0] == 'm' && src[1] == 'o' && src[2] == 'd'
-      && ! isalnum ((unsigned char) src[4]))
+  if (l0 == 'm' && l1 == 'o' && tolower (src[2]) == 'd'
+      && ! IDENT_CHAR ((unsigned char) src[4]))
     {
       *mode = A_MOD;
       return 3;
     }
-  if (src[0] == 'f' && src[1] == 'r')
+  if (l0 == 'f' && l1 == 'r')
     {
       if (src[2] == '1')
        {
          if (src[3] >= '0' && src[3] <= '5'
-             && ! isalnum ((unsigned char) src[4]))
+             && ! IDENT_CHAR ((unsigned char) src[4]))
            {
              *mode = F_REG_N;
              *reg = 10 + src[3] - '0';
@@ -506,19 +724,19 @@ parse_reg (src, mode, reg)
            }
        }
       if (src[2] >= '0' && src[2] <= '9'
-         && ! isalnum ((unsigned char) src[3]))
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = F_REG_N;
          *reg = (src[2] - '0');
          return 3;
        }
     }
-  if (src[0] == 'd' && src[1] == 'r')
+  if (l0 == 'd' && l1 == 'r')
     {
       if (src[2] == '1')
        {
          if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
-             && ! isalnum ((unsigned char) src[4]))
+             && ! IDENT_CHAR ((unsigned char) src[4]))
            {
              *mode = D_REG_N;
              *reg = 10 + src[3] - '0';
@@ -526,19 +744,19 @@ parse_reg (src, mode, reg)
            }
        }
       if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
-         && ! isalnum ((unsigned char) src[3]))
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = D_REG_N;
          *reg = (src[2] - '0');
          return 3;
        }
     }
-  if (src[0] == 'x' && src[1] == 'd')
+  if (l0 == 'x' && l1 == 'd')
     {
       if (src[2] == '1')
        {
          if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
-             && ! isalnum ((unsigned char) src[4]))
+             && ! IDENT_CHAR ((unsigned char) src[4]))
            {
              *mode = X_REG_N;
              *reg = 11 + src[3] - '0';
@@ -546,45 +764,48 @@ parse_reg (src, mode, reg)
            }
        }
       if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
-         && ! isalnum ((unsigned char) src[3]))
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = X_REG_N;
          *reg = (src[2] - '0') + 1;
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'v')
+  if (l0 == 'f' && l1 == 'v')
     {
-      if (src[2] == '1'&& src[3] == '2' && ! isalnum ((unsigned char) src[4]))
+      if (src[2] == '1'&& src[3] == '2' && ! IDENT_CHAR ((unsigned char) src[4]))
        {
          *mode = V_REG_N;
          *reg = 12;
          return 4;
        }
       if ((src[2] == '0' || src[2] == '4' || src[2] == '8')
-         && ! isalnum ((unsigned char) src[3]))
+         && ! IDENT_CHAR ((unsigned char) src[3]))
        {
          *mode = V_REG_N;
          *reg = (src[2] - '0');
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
-      && ! isalnum ((unsigned char) src[4]))
+  if (l0 == 'f' && l1 == 'p' && tolower (src[2]) == 'u'
+      && tolower (src[3]) == 'l'
+      && ! IDENT_CHAR ((unsigned char) src[4]))
     {
       *mode = FPUL_N;
       return 4;
     }
 
-  if (src[0] == 'f' && src[1] == 'p' && src[2] == 's' && src[3] == 'c'
-      && src[4] == 'r' && ! isalnum ((unsigned char) src[5]))
+  if (l0 == 'f' && l1 == 'p' && tolower (src[2]) == 's'
+      && tolower (src[3]) == 'c'
+      && tolower (src[4]) == 'r' && ! IDENT_CHAR ((unsigned char) src[5]))
     {
       *mode = FPSCR_N;
       return 5;
     }
 
-  if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r'
-      && src[4] == 'x' && ! isalnum ((unsigned char) src[5]))
+  if (l0 == 'x' && l1 == 'm' && tolower (src[2]) == 't'
+      && tolower (src[3]) == 'r'
+      && tolower (src[4]) == 'x' && ! IDENT_CHAR ((unsigned char) src[5]))
     {
       *mode = XMTRX_M4;
       return 5;
@@ -593,7 +814,8 @@ parse_reg (src, mode, reg)
   return 0;
 }
 
-static symbolS *dot()
+static symbolS *
+dot ()
 {
   const char *fake;
 
@@ -604,29 +826,26 @@ static symbolS *dot()
                      now_seg,
                      (valueT) frag_now_fix (),
                      frag_now);
-
 }
 
-
-static
-char *
-parse_exp (s)
+static char *
+parse_exp (s, op)
      char *s;
+     sh_operand_info *op;
 {
   char *save;
   char *new;
 
   save = input_line_pointer;
   input_line_pointer = s;
-  expression (&immediate);
-  if (immediate.X_op == O_absent)
+  expression (&op->immediate);
+  if (op->immediate.X_op == O_absent)
     as_bad (_("missing operand"));
   new = input_line_pointer;
   input_line_pointer = save;
   return new;
 }
 
-
 /* The many forms of operand:
 
    Rn                   Register direct
@@ -644,11 +863,9 @@ parse_exp (s)
    disp:12
    #imm8
    pr, gbr, vbr, macl, mach
-
  */
 
-static
-char *
+static char *
 parse_at (src, op)
      char *src;
      sh_operand_info *op;
@@ -658,7 +875,7 @@ parse_at (src, op)
   src++;
   if (src[0] == '-')
     {
-      /* Must be predecrement */
+      /* Must be predecrement */
       src++;
 
       len = parse_reg (src, &mode, &(op->reg));
@@ -701,7 +918,7 @@ parse_at (src, op)
       else
        {
          /* Must be an @(disp,.. thing) */
-         src = parse_exp (src);
+         src = parse_exp (src, op);
          if (src[0] == ',')
            src++;
          /* Now can be rn, gbr or pc */
@@ -716,13 +933,14 @@ parse_at (src, op)
                {
                  op->type = A_DISP_GBR;
                }
-             else if (mode == A_DISP_PC)
+             else if (mode == A_PC)
                {
-                 /* Turn a plain @(4,pc) into @(.+4,pc) */
-                 if (immediate.X_op == O_constant) { 
-                   immediate.X_add_symbol = dot();
-                   immediate.X_op = O_symbol;
-                 }
+                 /* Turn a plain @(4,pc) into @(.+4,pc).  */
+                 if (op->immediate.X_op == O_constant)
+                   {
+                     op->immediate.X_add_symbol = dot();
+                     op->immediate.X_op = O_symbol;
+                   }
                  op->type = A_DISP_PC;
                }
              else
@@ -786,7 +1004,7 @@ get_operand (ptr, op)
   if (src[0] == '#')
     {
       src++;
-      *ptr = parse_exp (src);
+      *ptr = parse_exp (src, op);
       op->type = A_IMM;
       return;
     }
@@ -805,20 +1023,18 @@ get_operand (ptr, op)
     }
   else
     {
-      /* Not a reg, the only thing left is a displacement */
-      *ptr = parse_exp (src);
+      /* Not a reg, the only thing left is a displacement */
+      *ptr = parse_exp (src, op);
       op->type = A_DISP_PC;
       return;
     }
 }
 
-static
-char *
+static char *
 get_operands (info, args, operand)
      sh_opcode_info *info;
      char *args;
      sh_operand_info *operand;
-
 {
   char *ptr = args;
   if (info->arg[0])
@@ -837,7 +1053,13 @@ get_operands (info, args, operand)
              ptr++;
            }
          get_operand (&ptr, operand + 1);
-         if (info->arg[2])
+         /* ??? Hack: psha/pshl have a varying operand number depending on
+            the type of the first operand.  We handle this by having the
+            three-operand version first and reducing the number of operands
+            parsed to two if we see that the first operand is an immediate.
+             This works because no insn with three operands has an immediate
+            as first operand.  */
+         if (info->arg[2] && operand[0].type != A_IMM)
            {
              if (*ptr == ',')
                {
@@ -867,11 +1089,9 @@ get_operands (info, args, operand)
 
 /* Passed a pointer to a list of opcodes which use different
    addressing modes, return the opcode which matches the opcodes
-   provided
- */
+   provided.  */
 
-static
-sh_opcode_info *
+static sh_opcode_info *
 get_specific (opcode, operands)
      sh_opcode_info *opcode;
      sh_operand_info *operands;
@@ -879,24 +1099,26 @@ get_specific (opcode, operands)
   sh_opcode_info *this_try = opcode;
   char *name = opcode->name;
   int n = 0;
+
   while (opcode->name)
     {
       this_try = opcode++;
       if (this_try->name != name)
        {
          /* We've looked so far down the table that we've run out of
-            opcodes with the same name */
+            opcodes with the same name */
          return 0;
        }
-      /* look at both operands needed by the opcodes and provided by
+
+      /* Look at both operands needed by the opcodes and provided by
          the user - since an arg test will often fail on the same arg
          again and again, we'll try and test the last failing arg the
-         first on each opcode try */
-
+         first on each opcode try.  */
       for (n = 0; this_try->arg[n]; n++)
        {
          sh_operand_info *user = operands + n;
          sh_arg_type arg = this_try->arg[n];
+
          switch (arg)
            {
            case A_IMM:
@@ -964,7 +1186,7 @@ get_specific (opcode, operands)
                goto fail;
              break;
 
-            case A_REG_B:
+           case A_REG_B:
              if (user->type != arg)
                goto fail;
              reg_b = user->reg;
@@ -1135,15 +1357,18 @@ get_specific (opcode, operands)
                goto fail;
              reg_m = 4;
              break;
-       
+
            default:
              printf (_("unhandled %d\n"), arg);
              goto fail;
            }
        }
+      if ( !(valid_arch & this_try->arch))
+       goto fail;
       valid_arch &= this_try->arch;
       return this_try;
-    fail:;
+    fail:
+      ;
     }
 
   return 0;
@@ -1164,24 +1389,25 @@ check (operand, low, high)
   return operand->X_add_number;
 }
 
-
 static void
-insert (where, how, pcrel)
+insert (where, how, pcrel, op)
      char *where;
      int how;
      int pcrel;
+     sh_operand_info *op;
 {
   fix_new_exp (frag_now,
               where - frag_now->fr_literal,
               2,
-              &immediate,
+              &op->immediate,
               pcrel,
               how);
 }
 
 static void
-build_relax (opcode)
+build_relax (opcode, op)
      sh_opcode_info *opcode;
+     sh_operand_info *op;
 {
   int high_byte = target_big_endian ? 0 : 1;
   char *p;
@@ -1193,8 +1419,8 @@ build_relax (opcode)
                    md_relax_table[C (what, COND32)].rlx_length,
                    md_relax_table[C (what, COND8)].rlx_length,
                    C (what, 0),
-                   immediate.X_add_symbol,
-                   immediate.X_add_number,
+                   op->immediate.X_add_symbol,
+                   op->immediate.X_add_number,
                    0);
       p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
     }
@@ -1204,17 +1430,67 @@ build_relax (opcode)
                    md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length,
                    md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
                    C (UNCOND_JUMP, 0),
-                   immediate.X_add_symbol,
-                   immediate.X_add_number,
+                   op->immediate.X_add_symbol,
+                   op->immediate.X_add_number,
                    0);
       p[high_byte] = (opcode->nibbles[0] << 4);
     }
 
 }
 
-/* Now we know what sort of opcodes it is, lets build the bytes -
- */
-static void
+/* Insert ldrs & ldre with fancy relocations that relaxation can recognize.  */
+
+static char *
+insert_loop_bounds (output, operand)
+     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
+     can just stash the high byte into both bytes and ignore endianness.  */
+  output[0] = 0x8c;
+  output[1] = 0x8c;
+  insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+  insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+  if (sh_relax)
+    {
+      static int count = 0;
+
+      /* 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
+        symbol - complete with SH_LABEL reloc - after the last loop insn.
+        If the last loop insn is four bytes long, the symbol will be
+        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);
+      /* 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;
+      fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL);
+    }
+
+  output = frag_more (2);
+  output[0] = 0x8e;
+  output[1] = 0x8e;
+  insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+  insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+  return frag_more (2);
+}
+
+/* Now we know what sort of opcodes it is, let's build the bytes.  */
+
+static unsigned int
 build_Mytes (opcode, operand)
      sh_opcode_info *opcode;
      sh_operand_info *operand;
@@ -1223,6 +1499,7 @@ build_Mytes (opcode, operand)
   int index;
   char nbuf[4];
   char *output = frag_more (2);
+  unsigned int size = 2;
   int low_byte = target_big_endian ? 1 : 0;
   nbuf[0] = 0;
   nbuf[1] = 0;
@@ -1254,49 +1531,72 @@ build_Mytes (opcode, operand)
            case REG_NM:
              nbuf[index] = reg_n | (reg_m >> 2);
              break;
-            case REG_B:
+           case REG_B:
              nbuf[index] = reg_b | 0x08;
              break;
-           case DISP_4:
-             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+           case IMM0_4BY4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
+             break;
+           case IMM0_4BY2:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand);
              break;
-           case IMM_4BY4:
-             insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0);
+           case IMM0_4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand);
              break;
-           case IMM_4BY2:
-             insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0);
+           case IMM1_4BY4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1);
              break;
-           case IMM_4:
-             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+           case IMM1_4BY2:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1);
              break;
-           case IMM_8BY4:
-             insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0);
+           case IMM1_4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1);
              break;
-           case IMM_8BY2:
-             insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0);
+           case IMM0_8BY4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand);
              break;
-           case IMM_8:
-             insert (output + low_byte, BFD_RELOC_SH_IMM8, 0);
+           case IMM0_8BY2:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand);
+             break;
+           case IMM0_8:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand);
+             break;
+           case IMM1_8BY4:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand + 1);
+             break;
+           case IMM1_8BY2:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand + 1);
+             break;
+           case IMM1_8:
+             insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1);
              break;
            case PCRELIMM_8BY4:
-             insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand);
              break;
            case PCRELIMM_8BY2:
-             insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand);
+             break;
+           case REPEAT:
+             output = insert_loop_bounds (output, operand);
+             nbuf[index] = opcode->nibbles[3];
+             operand += 2;
              break;
            default:
              printf (_("failed for %d\n"), i);
            }
        }
     }
-  if (! target_big_endian) {
-    output[1] = (nbuf[0] << 4) | (nbuf[1]);
-    output[0] = (nbuf[2] << 4) | (nbuf[3]);
-  }
-  else {
-    output[0] = (nbuf[0] << 4) | (nbuf[1]);
-    output[1] = (nbuf[2] << 4) | (nbuf[3]);
-  }
+  if (!target_big_endian)
+    {
+      output[1] = (nbuf[0] << 4) | (nbuf[1]);
+      output[0] = (nbuf[2] << 4) | (nbuf[3]);
+    }
+  else
+    {
+      output[0] = (nbuf[0] << 4) | (nbuf[1]);
+      output[1] = (nbuf[2] << 4) | (nbuf[3]);
+    }
+  return size;
 }
 
 /* Find an opcode at the start of *STR_P in the hash table, and set
@@ -1311,7 +1611,8 @@ find_cooked_opcode (str_p)
   unsigned char *op_end;
   char name[20];
   int nlen = 0;
-  /* Drop leading whitespace */
+
+  /* Drop leading whitespace.  */
   while (*str == ' ')
     str++;
 
@@ -1336,20 +1637,20 @@ find_cooked_opcode (str_p)
       name[nlen] = c;
       nlen++;
     }
+
   name[nlen] = 0;
   *str_p = op_end;
 
   if (nlen == 0)
-    {
-      as_bad (_("can't find opcode "));
-    }
+    as_bad (_("can't find opcode "));
 
   return (sh_opcode_info *) hash_find (opcode_hash_control, name);
 }
 
 /* Assemble a parallel processing insn.  */
 #define DDT_BASE 0xf000 /* Base value for double data transfer insns */
-static void
+
+static unsigned int
 assemble_ppi (op_end, opcode)
      char *op_end;
      sh_opcode_info *opcode;
@@ -1360,6 +1661,7 @@ assemble_ppi (op_end, opcode)
   int field_b = 0;
   char *output;
   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.  */
@@ -1375,14 +1677,16 @@ assemble_ppi (op_end, opcode)
       opcode = get_specific (opcode, operand);
       if (opcode == 0)
        {
-         /* Couldn't find an opcode which matched the operands */
+         /* Couldn't find an opcode which matched the operands */
          char *where = frag_more (2);
+         size = 2;
 
          where[0] = 0x0;
          where[1] = 0x0;
          as_bad (_("invalid operands for opcode"));
-         return;
+         return size;
        }
+
       if (opcode->nibbles[0] != PPI)
        as_bad (_("insn can't be combined with parallel processing insn"));
 
@@ -1446,10 +1750,10 @@ assemble_ppi (op_end, opcode)
          break;
 
        case PSH:
-         if (immediate.X_op != O_constant)
+         if (operand[0].immediate.X_op != O_constant)
            as_bad (_("dsp immediate shift value not constant"));
          field_b = ((opcode->nibbles[2] << 12)
-                    | (immediate.X_add_number & 127) << 4
+                    | (operand[0].immediate.X_add_number & 127) << 4
                     | reg_n);
          break;
        case PPI3:
@@ -1525,6 +1829,7 @@ assemble_ppi (op_end, opcode)
       unsigned long ppi_code = (movx | movy | 0xf800) << 16 | field_b;
 
       output = frag_more (4);
+      size = 4;
       if (! target_big_endian)
        {
          output[3] = ppi_code >> 8;
@@ -1538,8 +1843,11 @@ assemble_ppi (op_end, opcode)
       move_code |= 0xf800;
     }
   else
-    /* Just a double data transfer.  */
-    output = frag_more (2);
+    {
+      /* Just a double data transfer.  */
+      output = frag_more (2);
+      size = 2;
+    }
   if (! target_big_endian)
     {
       output[1] = move_code >> 8;
@@ -1550,12 +1858,12 @@ assemble_ppi (op_end, opcode)
       output[0] = move_code >> 8;
       output[1] = move_code;
     }
+  return size;
 }
 
 /* This is the guts of the machine-dependent assembler.  STR points to a
    machine dependent instruction.  This function is supposed to emit
-   the frags/bytes it assembles to.
- */
+   the frags/bytes it assembles to.  */
 
 void
 md_assemble (str)
@@ -1564,6 +1872,7 @@ md_assemble (str)
   unsigned char *op_end;
   sh_operand_info operand[3];
   sh_opcode_info *opcode;
+  unsigned int size = 0;
 
   opcode = find_cooked_opcode (&str);
   op_end = str;
@@ -1586,48 +1895,52 @@ md_assemble (str)
 
   if (opcode->nibbles[0] == PPI)
     {
-      assemble_ppi (op_end, opcode);
-      return;
-    }
-
-  if (opcode->arg[0] == A_BDISP12
-      || opcode->arg[0] == A_BDISP8)
-    {
-      parse_exp (op_end + 1);
-      build_relax (opcode);
+      size = assemble_ppi (op_end, opcode);
     }
   else
     {
-      if (opcode->arg[0] == A_END)
+      if (opcode->arg[0] == A_BDISP12
+         || opcode->arg[0] == A_BDISP8)
        {
-         /* Ignore trailing whitespace.  If there is any, it has already
-            been compressed to a single space.  */
-         if (*op_end == ' ')
-           op_end++;
+         parse_exp (op_end + 1, &operand[0]);
+         build_relax (opcode, &operand[0]);
        }
       else
        {
-         op_end = get_operands (opcode, op_end, operand);
-       }
-      opcode = get_specific (opcode, operand);
-
-      if (opcode == 0)
-       {
-         /* Couldn't find an opcode which matched the operands */
-         char *where = frag_more (2);
+         if (opcode->arg[0] == A_END)
+           {
+             /* Ignore trailing whitespace.  If there is any, it has already
+                been compressed to a single space.  */
+             if (*op_end == ' ')
+               op_end++;
+           }
+         else
+           {
+             op_end = get_operands (opcode, op_end, operand);
+           }
+         opcode = get_specific (opcode, operand);
 
-         where[0] = 0x0;
-         where[1] = 0x0;
-         as_bad (_("invalid operands for opcode"));
-         return;
-       }
+         if (opcode == 0)
+           {
+             /* Couldn't find an opcode which matched the operands.  */
+             char *where = frag_more (2);
+             size = 2;
 
-      if (*op_end)
-       as_bad (_("excess operands: '%s'"), op_end);
+             where[0] = 0x0;
+             where[1] = 0x0;
+             as_bad (_("invalid operands for opcode"));
+           }
+         else
+           {
+             if (*op_end)
+               as_bad (_("excess operands: '%s'"), op_end);
 
-      build_Mytes (opcode, operand);
+             size = build_Mytes (opcode, operand);
+           }
+       }
     }
 
+  dwarf2_emit_insn (size);
 }
 
 /* This routine is called each time a label definition is seen.  It
@@ -1647,7 +1960,7 @@ sh_frob_label ()
       offset = frag_now_fix ();
       if (frag_now != last_label_frag
          || offset != last_label_offset)
-       {       
+       {
          fix_new (frag_now, offset, 2, &abs_symbol, 0, 0, BFD_RELOC_SH_LABEL);
          last_label_frag = frag_now;
          last_label_offset = offset;
@@ -1671,38 +1984,59 @@ sh_flush_pending_output ()
 }
 
 symbolS *
-DEFUN (md_undefined_symbol, (name),
-       char *name)
+md_undefined_symbol (name)
+     char *name;
 {
+#ifdef OBJ_ELF
+  /* Under ELF we need to default _GLOBAL_OFFSET_TABLE.  Otherwise we
+     have no need to default values of symbols.  */
+  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+    {
+      if (!GOT_symbol)
+       {
+         if (symbol_find (name))
+           as_bad ("GOT already in the symbol table");
+
+         GOT_symbol = symbol_new (name, undefined_section,
+                                  (valueT)0, & zero_address_frag);
+       }
+
+      return GOT_symbol;
+    }
+#endif /* OBJ_ELF */
+
   return 0;
 }
 
 #ifdef OBJ_COFF
+#ifndef BFD_ASSEMBLER
 
 void
-DEFUN (tc_crawl_symbol_chain, (headers),
-       object_headers * headers)
+tc_crawl_symbol_chain (headers)
+     object_headers *headers;
 {
   printf (_("call to tc_crawl_symbol_chain \n"));
 }
 
 void
-DEFUN (tc_headers_hook, (headers),
-       object_headers * headers)
+tc_headers_hook (headers)
+     object_headers *headers;
 {
   printf (_("call to tc_headers_hook \n"));
 }
 
+#endif
 #endif
 
-/* Various routines to kill one day */
-/* Equal to MAX_PRECISION in atof-ieee.c */
+/* 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.
- */
+/* Turn a string in input_line_pointer into a floating point constant
+   of type TYPE, and store the appropriate bytes in *LITP.  The number
+   of LITTLENUMS emitted is stored in *SIZEP .  An error message is
+   returned, or NULL on OK.  */
+
 char *
 md_atof (type, litP, sizeP)
      int type;
@@ -1751,7 +2085,7 @@ md_atof (type, litP, sizeP)
          litP += 2;
        }
     }
-     
+
   return NULL;
 }
 
@@ -1762,7 +2096,7 @@ md_atof (type, litP, sizeP)
 
 static void
 s_uses (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   expressionS ex;
 
@@ -1784,8 +2118,8 @@ s_uses (ignore)
 }
 \f
 CONST char *md_shortopts = "";
-struct option md_longopts[] = {
-
+struct option md_longopts[] =
+{
 #define OPTION_RELAX  (OPTION_MD_BASE)
 #define OPTION_LITTLE (OPTION_MD_BASE + 1)
 #define OPTION_SMALL (OPTION_LITTLE + 1)
@@ -1797,12 +2131,12 @@ struct option md_longopts[] = {
   {"dsp", no_argument, NULL, OPTION_DSP},
   {NULL, no_argument, NULL, 0}
 };
-size_t md_longopts_size = sizeof(md_longopts);
+size_t md_longopts_size = sizeof (md_longopts);
 
 int
 md_parse_option (c, arg)
      int c;
-     char *arg;
+     char *arg ATTRIBUTE_UNUSED;
 {
   switch (c)
     {
@@ -1834,11 +2168,12 @@ void
 md_show_usage (stream)
      FILE *stream;
 {
-  fprintf(stream, _("\
+  fprintf (stream, _("\
 SH options:\n\
 -little                        generate little endian code\n\
 -relax                 alter jump instructions for long displacements\n\
--small                 align sections to 4 byte boundaries, not 16\n"));
+-small                 align sections to 4 byte boundaries, not 16\n\
+-dsp                   enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
 }
 \f
 void
@@ -1863,10 +2198,9 @@ struct sh_count_relocs
    symbol.  When using BFD_ASSEMBLER, this is called via
    bfd_map_over_sections.  */
 
-/*ARGSUSED*/
 static void
 sh_count_relocs (abfd, sec, data)
-     bfd *abfd;
+     bfd *abfd ATTRIBUTE_UNUSED;
      segT sec;
      PTR data;
 {
@@ -1893,12 +2227,11 @@ sh_count_relocs (abfd, sec, data)
 /* Handle the count relocs for a particular section.  When using
    BFD_ASSEMBLER, this is called via bfd_map_over_sections.  */
 
-/*ARGSUSED*/
 static void
 sh_frob_section (abfd, sec, ignore)
-     bfd *abfd;
+     bfd *abfd ATTRIBUTE_UNUSED;
      segT sec;
-     PTR ignore;
+     PTR ignore ATTRIBUTE_UNUSED;
 {
   segment_info_type *seginfo;
   fixS *fix;
@@ -1959,8 +2292,8 @@ sh_frob_section (abfd, sec, ignore)
          continue;
        }
 
-      /* fscan should also be a fixup to a local symbol in the same
-        section.  */
+      /* The variable fscan should also be a fixup to a local symbol
+        in the same section.  */
       sym = fscan->fx_addsy;
       if (sym == NULL
          || fscan->fx_subsy != NULL
@@ -2038,7 +2371,7 @@ sh_frob_file ()
 void
 md_convert_frag (headers, seg, fragP)
 #ifdef BFD_ASSEMBLER
-     bfd *headers;
+     bfd *headers ATTRIBUTE_UNUSED;
 #else
      object_headers *headers;
 #endif
@@ -2069,68 +2402,30 @@ md_convert_frag (headers, seg, fragP)
     case C (UNCOND_JUMP, UNCOND32):
     case C (UNCOND_JUMP, UNDEF_WORD_DISP):
       if (fragP->fr_symbol == NULL)
-       as_bad (_("at 0x%lx, displacement overflows 12-bit field"),
-               (unsigned long) fragP->fr_address);
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement overflows 12-bit field"));
       else if (S_IS_DEFINED (fragP->fr_symbol))
-       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 12-bit field"),
-               (unsigned long) fragP->fr_address,              
-               S_GET_NAME (fragP->fr_symbol));
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to defined symbol %s overflows 12-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
       else
-       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 12-bit field"),
-               (unsigned long) fragP->fr_address,              
-               S_GET_NAME (fragP->fr_symbol));
-
-#if 0                          /* This code works, but generates poor code and the compiler
-                                  should never produce a sequence that requires it to be used.  */
-
-      /* A jump wont fit in 12 bits, make code which looks like
-        bra foo
-        mov.w @(0, PC), r14
-        .long disp
-        foo: bra @r14
-        */
-      int t = buffer[0] & 0x10;
-
-      buffer[highbyte] = 0xa0; /* branch over move and disp */
-      buffer[lowbyte] = 3;
-      buffer[highbyte+2] = 0xd0 | JREG;        /* Build mov insn */
-      buffer[lowbyte+2] = 0x00;
-
-      buffer[highbyte+4] = 0;  /* space for 32 bit jump disp */
-      buffer[lowbyte+4] = 0;
-      buffer[highbyte+6] = 0;
-      buffer[lowbyte+6] = 0;
-
-      buffer[highbyte+8] = 0x40 | JREG;        /* Build jmp @JREG */
-      buffer[lowbyte+8] = t ? 0xb : 0x2b;
-
-      buffer[highbyte+10] = 0x20; /* build nop */
-      buffer[lowbyte+10] = 0x0b;
-
-      /* Make reloc for the long disp */
-      fix_new (fragP,
-              fragP->fr_fix + 4,
-              4,
-              fragP->fr_symbol,
-              fragP->fr_offset,
-              0,
-              BFD_RELOC_32);
-      fragP->fr_fix += UNCOND32_LENGTH;
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to undefined symbol %s overflows 12-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
+      /* Stabilize this frag, so we don't trip an assert.  */
+      fragP->fr_fix += fragP->fr_var;
       fragP->fr_var = 0;
-      donerelax = 1;
-#endif
-
       break;
 
     case C (COND_JUMP, COND12):
     case C (COND_JUMP_DELAY, COND12):
-      /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
+      /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
       /* I found that a relax failure for gcc.c-torture/execute/930628-1.c
         was due to gas incorrectly relaxing an out-of-range conditional
         branch with delay slot.  It turned:
                      bf.s    L6              (slot mov.l   r12,@(44,r0))
          into:
-   
+
 2c:  8f 01 a0 8b     bf.s    32 <_main+32>   (slot bra       L6)
 30:  00 09           nop
 32:  10 cb           mov.l   r12,@(44,r0)
@@ -2192,58 +2487,19 @@ md_convert_frag (headers, seg, fragP)
     case C (COND_JUMP, UNDEF_WORD_DISP):
     case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
       if (fragP->fr_symbol == NULL)
-       as_bad (_("at 0x%lx, displacement overflows 8-bit field"), 
-               (unsigned long) fragP->fr_address);
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement overflows 8-bit field"));
       else if (S_IS_DEFINED (fragP->fr_symbol))
-       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 8-bit field "),
-               (unsigned long) fragP->fr_address,              
-               S_GET_NAME (fragP->fr_symbol));
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to defined symbol %s overflows 8-bit field"),
+                     S_GET_NAME (fragP->fr_symbol));
       else
-       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 8-bit field "),
-               (unsigned long) fragP->fr_address,              
-               S_GET_NAME (fragP->fr_symbol));
-
-#if 0                          /* This code works, but generates poor code, and the compiler
-                                  should never produce a sequence that requires it to be used.  */
-
-      /* A bcond won't fit and it won't go into a 12 bit
-        displacement either, the code sequence looks like:
-        b!cond foop
-        mov.w @(n, PC), r14
-        jmp  @r14
-        nop
-        .long where
-        foop:
-        */
-
-      buffer[0] ^= 0x2;                /* Toggle T/F bit */
-#define JREG 14
-      buffer[1] = 5;           /* branch over mov, jump, nop and ptr */
-      buffer[2] = 0xd0 | JREG; /* Build mov insn */
-      buffer[3] = 0x2;
-      buffer[4] = 0x40 | JREG; /* Build jmp @JREG */
-      buffer[5] = 0x0b;
-      buffer[6] = 0x20;                /* build nop */
-      buffer[7] = 0x0b;
-      buffer[8] = 0;           /* space for 32 bit jump disp */
-      buffer[9] = 0;
-      buffer[10] = 0;
-      buffer[11] = 0;
-      buffer[12] = 0;
-      buffer[13] = 0;
-      /* Make reloc for the long disp */
-      fix_new (fragP,
-              fragP->fr_fix + 8,
-              4,
-              fragP->fr_symbol,
-              fragP->fr_offset,
-              0,
-              BFD_RELOC_32);
-      fragP->fr_fix += COND32_LENGTH;
+       as_bad_where (fragP->fr_file, fragP->fr_line,
+                     _("displacement to undefined symbol %s overflows 8-bit field "),
+                     S_GET_NAME (fragP->fr_symbol));
+      /* Stabilize this frag, so we don't trip an assert.  */
+      fragP->fr_fix += fragP->fr_var;
       fragP->fr_var = 0;
-      donerelax = 1;
-#endif
-
       break;
 
     default:
@@ -2259,9 +2515,9 @@ md_convert_frag (headers, seg, fragP)
 }
 
 valueT
-DEFUN (md_section_align, (seg, size),
-       segT seg AND
-       valueT size)
+md_section_align (seg, size)
+     segT seg ATTRIBUTE_UNUSED;
+     valueT size;
 {
 #ifdef BFD_ASSEMBLER
 #ifdef OBJ_ELF
@@ -2331,7 +2587,7 @@ sh_cons_align (nbytes)
       return;
     }
 
-  p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+  p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
                (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
 
   record_alignment (now_seg, nalign);
@@ -2345,17 +2601,47 @@ void
 sh_handle_align (frag)
      fragS *frag;
 {
+  int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+
+  if (frag->fr_type == rs_align_code)
+    {
+      static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+      static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+      char *p = frag->fr_literal + frag->fr_fix;
+
+      if (bytes & 1)
+       {
+         *p++ = 0;
+         bytes--;
+         frag->fr_fix += 1;
+       }
+
+      if (target_big_endian)
+       {
+         memcpy (p, big_nop_pattern, sizeof big_nop_pattern);
+         frag->fr_var = sizeof big_nop_pattern;
+       }
+      else
+       {
+         memcpy (p, little_nop_pattern, sizeof little_nop_pattern);
+         frag->fr_var = sizeof little_nop_pattern;
+       }
+    }
+  else if (frag->fr_type == rs_align_test)
+    {
+      if (bytes != 0)
+       as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+    }
+
   if (sh_relax
-      && frag->fr_type == rs_align
+      && (frag->fr_type == rs_align
+         || frag->fr_type == rs_align_code)
       && frag->fr_address + frag->fr_fix > 0
       && frag->fr_offset > 1
       && now_seg != bss_section)
     fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
             BFD_RELOC_SH_ALIGN);
-
-  if (frag->fr_type == rs_align_code
-      && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
-    as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
 }
 
 /* This macro decides whether a particular reloc is an entry in a
@@ -2393,7 +2679,9 @@ sh_force_relocation (fix)
 {
 
   if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+      || 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)
     return 1;
 
   if (! sh_relax)
@@ -2416,7 +2704,21 @@ sh_fix_adjustable (fixP)
 
   if (fixP->fx_addsy == NULL)
     return 1;
-  
+
+  if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+      || fixP->fx_r_type == BFD_RELOC_8_PCREL
+      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16
+      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32)
+    return 1;
+
+  if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP)
+      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_RVA)
+    return 0;
+
   /* We need the symbol name for the VTABLE entries */
   if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
@@ -2425,7 +2727,8 @@ sh_fix_adjustable (fixP)
   return 1;
 }
 
-void sh_elf_final_processing()
+void
+sh_elf_final_processing ()
 {
   int val;
 
@@ -2477,11 +2780,49 @@ md_apply_fix (fixP, val)
   int shift;
 
 #ifdef BFD_ASSEMBLER
-  /* adjust_reloc_syms won't convert a reloc against a weak symbol
-     into a reloc against a section, but bfd_install_relocation will
-     screw up if the symbol is defined, so we have to adjust val here
-     to avoid the screw up later.  */
-  if (fixP->fx_addsy != NULL
+  /* A difference between two symbols, the second of which is in the
+     current section, is transformed in a PC-relative relocation to
+     the other symbol.  We have to adjust the relocation type here.  */
+  if (fixP->fx_pcrel)
+    {
+      switch (fixP->fx_r_type)
+       {
+       default:
+         break;
+
+       case BFD_RELOC_32:
+         fixP->fx_r_type = BFD_RELOC_32_PCREL;
+         break;
+
+         /* Currently, we only support 32-bit PCREL relocations.
+            We'd need a new reloc type to handle 16_PCREL, and
+            8_PCREL is already taken for R_SH_SWITCH8, which
+            apparently does something completely different than what
+            we need.  FIXME.  */
+       case BFD_RELOC_16:
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+
+       case BFD_RELOC_8:
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
+    }
+
+  /* The function adjust_reloc_syms won't convert a reloc against a weak
+     symbol into a reloc against a section, but bfd_install_relocation
+     will screw up if the symbol is defined, so we have to adjust val here
+     to avoid the screw up later.
+
+     For ordinary relocs, this does not happen for ELF, since for ELF,
+     bfd_install_relocation uses the "special function" field of the
+     howto, and does not execute the code that needs to be undone, as long
+     as the special function does not return bfd_reloc_continue.
+     It can happen for GOT- and PLT-type relocs the way they are
+     described in elf32-sh.c as they use bfd_elf_generic_reloc, but it
+     doesn't matter here since those relocs don't use VAL; see below.  */
+  if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+      && fixP->fx_addsy != NULL
       && S_IS_WEAK (fixP->fx_addsy))
     val -= S_GET_VALUE  (fixP->fx_addsy);
 #endif
@@ -2540,7 +2881,7 @@ md_apply_fix (fixP, val)
          Note that adding further restrictions may invalidate
          reasonable looking assembly code, such as ``and -0x1,r0''.  */
       max = 0xff;
-      min = - 0xff;
+      min = -0xff;
       *buf++ = val;
       break;
 
@@ -2580,40 +2921,19 @@ md_apply_fix (fixP, val)
 
     case BFD_RELOC_SH_PCDISP12BY2:
       val /= 2;
-      if (val < -0x800 || val >= 0x7ff)
+      if (val < -0x800 || val > 0x7ff)
        as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
       buf[lowbyte] = val & 0xff;
       buf[highbyte] |= (val >> 8) & 0xf;
       break;
 
     case BFD_RELOC_32:
-      if (! target_big_endian) 
-       {
-         *buf++ = val >> 0;
-         *buf++ = val >> 8;
-         *buf++ = val >> 16;
-         *buf++ = val >> 24;
-       }
-      else 
-       {
-         *buf++ = val >> 24;
-         *buf++ = val >> 16;
-         *buf++ = val >> 8;
-         *buf++ = val >> 0;
-       }
+    case BFD_RELOC_32_PCREL:
+      md_number_to_chars (buf, val, 4);
       break;
 
     case BFD_RELOC_16:
-      if (! target_big_endian)
-       {
-         *buf++ = val >> 0;
-         *buf++ = val >> 8;
-       } 
-      else 
-       {
-         *buf++ = val >> 8;
-         *buf++ = val >> 0;
-       }
+      md_number_to_chars (buf, val, 2);
       break;
 
     case BFD_RELOC_SH_USES:
@@ -2629,6 +2949,9 @@ md_apply_fix (fixP, val)
       /* Nothing to do here.  */
       break;
 
+    case BFD_RELOC_SH_LOOP_START:
+    case BFD_RELOC_SH_LOOP_END:
+
     case BFD_RELOC_VTABLE_INHERIT:
     case BFD_RELOC_VTABLE_ENTRY:
       fixP->fx_done = 0;
@@ -2638,6 +2961,43 @@ md_apply_fix (fixP, val)
       return;
 #endif
 
+#ifdef OBJ_ELF
+    case BFD_RELOC_32_PLT_PCREL:
+      /* Make the jump instruction point to the address of the operand.  At
+        runtime we merely add the offset to the actual PLT entry.  */
+      *valp = 0xfffffffc;
+      break;
+
+    case BFD_RELOC_SH_GOTPC:
+      /* This is tough to explain.  We end up with this one if we have
+         operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]".
+         The goal here is to obtain the absolute address of the GOT,
+         and it is strongly preferable from a performance point of
+         view to avoid using a runtime relocation for this.  There are
+         cases where you have something like:
+
+         .long _GLOBAL_OFFSET_TABLE_+[.-.L66]
+
+         and here no correction would be required.  Internally in the
+         assembler we treat operands of this form as not being pcrel
+         since the '.' is explicitly mentioned, and I wonder whether
+         it would simplify matters to do it this way.  Who knows.  In
+         earlier versions of the PIC patches, the pcrel_adjust field
+         was used to store the correction, but since the expression is
+         not pcrel, I felt it would be confusing to do it this way.  */
+      *valp -= 1;
+      md_number_to_chars (buf, val, 4);
+      break;
+
+    case BFD_RELOC_32_GOT_PCREL:
+      *valp = 0; /* Fully resolved at runtime.  No addend.  */
+      md_number_to_chars (buf, 0, 4);
+      break;
+
+    case BFD_RELOC_32_GOTOFF:
+      break;
+#endif
+
     default:
       abort ();
     }
@@ -2670,8 +3030,11 @@ md_estimate_size_before_relax (fragP, segment_type)
 {
   switch (fragP->fr_subtype)
     {
+    default:
+      abort ();
+
     case C (UNCOND_JUMP, UNDEF_DISP):
-      /* used to be a branch to somewhere which was unknown */
+      /* Used to be a branch to somewhere which was unknown.  */
       if (!fragP->fr_symbol)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
@@ -2686,46 +3049,51 @@ md_estimate_size_before_relax (fragP, segment_type)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP);
          fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
-         return md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
        }
       break;
 
-    default:
-      abort ();
     case C (COND_JUMP, UNDEF_DISP):
     case C (COND_JUMP_DELAY, UNDEF_DISP):
-      /* used to be a branch to somewhere which was unknown */
+      /* Used to be a branch to somewhere which was unknown.  */
       if (fragP->fr_symbol
          && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
        {
          int what = GET_WHAT (fragP->fr_subtype);
          /* Got a symbol and it's defined in this segment, become byte
-            sized - maybe it will fix up */
+            sized - maybe it will fix up */
          fragP->fr_subtype = C (what, COND8);
          fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
       else if (fragP->fr_symbol)
        {
          int what = GET_WHAT (fragP->fr_subtype);
-         /* Its got a segment, but its not ours, so it will always be long */
+         /* Its got a segment, but its not ours, so it will always be long */
          fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
          fragP->fr_var = md_relax_table[C (what, COND32)].rlx_length;
-         return md_relax_table[C (what, COND32)].rlx_length;
        }
       else
        {
          int what = GET_WHAT (fragP->fr_subtype);
-         /* We know the abs value */
+         /* We know the abs value */
          fragP->fr_subtype = C (what, COND8);
          fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
+      break;
 
+    case C (UNCOND_JUMP, UNCOND12):
+    case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP, COND8):
+    case C (COND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP_DELAY, COND8):
+    case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+      /* When relaxing a section for the second time, we don't need to
+        do anything.  */
       break;
     }
   return fragP->fr_var;
 }
 
-/* Put number into target byte order */
+/* Put number into target byte order */
 
 void
 md_number_to_chars (ptr, use, nbytes)
@@ -2757,36 +3125,6 @@ tc_coff_sizemachdep (frag)
 
 #endif /* OBJ_COFF */
 
-/* When we align the .text section, insert the correct NOP pattern.  */
-
-int
-sh_do_align (n, fill, len, max)
-     int n;
-     const char *fill;
-     int len;
-     int max;
-{
-  if (fill == NULL
-      && subseg_text_p (now_seg)
-      && n > 1)
-    {
-      static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
-      static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
-
-      /* First align to a 2 byte boundary, in case there is an odd
-         .byte.  */
-      frag_align (1, 0, 0);
-      if (target_big_endian)
-       frag_align_pattern (n, big_nop_pattern, sizeof big_nop_pattern, max);
-      else
-       frag_align_pattern (n, little_nop_pattern, sizeof little_nop_pattern,
-                           max);
-      return 1;
-    }
-
-  return 0;
-}
-
 #ifndef BFD_ASSEMBLER
 #ifdef OBJ_COFF
 
@@ -2945,7 +3283,7 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
 
 arelent *
 tc_gen_reloc (section, fixp)
-     asection *section;
+     asection *section ATTRIBUTE_UNUSED;
      fixS *fixp;
 {
   arelent *rel;
@@ -2979,8 +3317,18 @@ tc_gen_reloc (section, fixp)
   else if (r_type == BFD_RELOC_VTABLE_INHERIT
            || r_type == BFD_RELOC_VTABLE_ENTRY)
     rel->addend = fixp->fx_offset;
+  else if (r_type == BFD_RELOC_SH_LOOP_START
+           || r_type == BFD_RELOC_SH_LOOP_END)
+    rel->addend = fixp->fx_offset;
+  else if (r_type == BFD_RELOC_SH_LABEL && fixp->fx_pcrel)
+    {
+      rel->addend = 0;
+      rel->address = rel->addend = fixp->fx_offset;
+    }
   else if (fixp->fx_pcrel)
     rel->addend = fixp->fx_addnumber;
+  else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
+    rel->addend = fixp->fx_addnumber;
   else
     rel->addend = 0;
 
This page took 0.05376 seconds and 4 git commands to generate.