2001-10-24 Chris Demetriou <cgd@broadcom.com>
[deliverable/binutils-gdb.git] / gas / config / tc-sh.c
index c120d0cf6952dfbf423baab32b79a0e994ab0fe4..1bf258948f44b087ec703fc4f3df94aeaf2e8ed6 100644 (file)
@@ -1,5 +1,6 @@
 /* tc-sh.c -- Assemble code for the Hitachi Super-H
-   Copyright (C) 1993, 94, 95, 96, 97, 98, 99, 2000 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.
 
 #include "subsegs.h"
 #define DEFINE_TABLE
 #include "opcodes/sh-opc.h"
-#include <ctype.h>
+#include "safe-ctype.h"
 #include "struc-symbol.h"
 
 #ifdef OBJ_ELF
 #include "elf/sh.h"
 #endif
 
+#include "dwarf2dbg.h"
+
+typedef struct
+  {
+    sh_arg_type type;
+    int reg;
+    expressionS immediate;
+  }
+sh_operand_info;
+
 const char comment_chars[] = "!";
 const char line_separator_chars[] = ";";
 const char line_comment_chars[] = "!#";
@@ -42,19 +53,53 @@ static void s_uses PARAMS ((int));
 static void sh_count_relocs PARAMS ((bfd *, segT, PTR));
 static void sh_frob_section PARAMS ((bfd *, segT, PTR));
 
-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 *));
+static void little PARAMS ((int));
+static void big PARAMS ((int));
+static bfd_reloc_code_real_type sh_elf_suffix
+  PARAMS ((char **str_p, expressionS *, expressionS *new_exp_p));
+static int parse_reg PARAMS ((char *, int *, int *));
+static symbolS *dot PARAMS ((void));
+static char *parse_exp PARAMS ((char *, sh_operand_info *));
+static char *parse_at PARAMS ((char *, sh_operand_info *));
+static void get_operand PARAMS ((char **, sh_operand_info *));
+static char *get_operands
+  PARAMS ((sh_opcode_info *, char *, sh_operand_info *));
+static sh_opcode_info *get_specific
+  PARAMS ((sh_opcode_info *, sh_operand_info *));
+static void insert PARAMS ((char *, int, int, sh_operand_info *));
+static void build_relax PARAMS ((sh_opcode_info *, sh_operand_info *));
+static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
+static unsigned int build_Mytes
+  PARAMS ((sh_opcode_info *, sh_operand_info *));
+
+#ifdef OBJ_ELF
+static void sh_elf_cons PARAMS ((int));
+
+symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
+
+static void
+big (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  if (! target_big_endian)
+    as_bad (_("directive .big encountered when option -big required"));
 
-int shl = 0;
+  /* Stop further messages.  */
+  target_big_endian = 1;
+}
 
 static void
 little (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
-  shl = 1;
+  if (target_big_endian)
+    as_bad (_("directive .little encountered when option -little required"));
+
+  /* Stop further messages.  */
   target_big_endian = 0;
 }
 
@@ -66,8 +111,16 @@ little (ignore)
 
 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 */
+  {"big", big, 0},
   {"form", listing_psize, 0},
   {"little", little, 0},
   {"heading", listing_title, 0},
@@ -77,6 +130,14 @@ 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},
+#ifdef BFD_ASSEMBLER
+  {"file", dwarf2_directive_file, 0 },
+  {"loc", dwarf2_directive_loc, 0 },
+#endif
   {0, 0, 0}
 };
 
@@ -105,7 +166,6 @@ 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))
 
@@ -119,8 +179,6 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define COND8  1
 #define COND12 2
 #define COND32 3
-#define UNCOND12 1
-#define UNCOND32 2
 #define UNDEF_WORD_DISP 4
 
 #define UNCOND12 1
@@ -169,7 +227,9 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
   /* C (COND_JUMP, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY,
+  /* C (COND_JUMP, UNDEF_WORD_DISP) */
+  { 0, 0, COND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
   EMPTY,
@@ -179,7 +239,9 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
   /* C (COND_JUMP_DELAY, COND32) */
   { COND32_F, COND32_M, COND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY,
+  /* C (COND_JUMP_DELAY, UNDEF_WORD_DISP) */
+  { 0, 0, COND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 
   EMPTY,
@@ -187,7 +249,10 @@ const relax_typeS md_relax_table[C (END, 0)] = {
   { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
   /* C (UNCOND_JUMP, UNCOND32) */
   { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
-  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+  EMPTY,
+  /* C (UNCOND_JUMP, UNDEF_WORD_DISP) */
+  { 0, 0, UNCOND32_LENGTH, 0, },
+  EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
 };
 
@@ -195,6 +260,189 @@ const relax_typeS md_relax_table[C (END, 0)] = {
 
 static struct hash_control *opcode_hash_control;       /* Opcode mnemonics */
 
+\f
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation.
+   If we have additional arithmetic expression, then we fill in new_exp_p.  */
+static bfd_reloc_code_real_type
+sh_elf_suffix (str_p, exp_p, new_exp_p)
+     char **str_p;
+     expressionS *exp_p, *new_exp_p;
+{
+  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++ = 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.  */
 
@@ -205,14 +453,6 @@ 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;
 
@@ -243,15 +483,7 @@ static int reg_x, reg_y;
 static int reg_efg;
 static int reg_b;
 
-typedef struct
-  {
-    sh_arg_type type;
-    int reg;
-    expressionS immediate;
-  }
-sh_operand_info;
-
-#define IDENT_CHAR(c) (isalnum (c) || (c) == '_')
+#define IDENT_CHAR(c) (ISALNUM (c) || (c) == '_')
 
 /* Try to parse a reg name.  Return the number of chars consumed.  */
 
@@ -261,13 +493,16 @@ parse_reg (src, mode, reg)
      int *mode;
      int *reg;
 {
+  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' 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'
              && ! IDENT_CHAR ((unsigned char) src[3]))
@@ -277,36 +512,36 @@ parse_reg (src, mode, reg)
              return 3;
            }
        }
-      if (src[1] >= '0' && src[1] <= '9'
+      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
+      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' && ! IDENT_CHAR ((unsigned char) src[2]))
+      if (l1 == 'e' && ! IDENT_CHAR ((unsigned char) src[2]))
        {
          *mode = A_RE;
          return 2;
        }
-      if (src[1] == 's' && ! IDENT_CHAR ((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 (! IDENT_CHAR ((unsigned char) src[2]))
            {
@@ -314,14 +549,14 @@ parse_reg (src, mode, reg)
              *reg = A_A0_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! IDENT_CHAR ((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 (! IDENT_CHAR ((unsigned char) src[2]))
            {
@@ -329,7 +564,7 @@ parse_reg (src, mode, reg)
              *reg = A_A1_NUM;
              return 2;
            }
-         if (src[2] == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
+         if (TOLOWER (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
            {
              *mode = DSP_REG_N;
              *reg = A_A1G_NUM;
@@ -337,24 +572,24 @@ parse_reg (src, mode, reg)
            }
        }
 
-      if (src[1] == 'x' && src[2] >= '0' && src[2] <= '1'
+      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'
+      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'
+      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);
@@ -362,21 +597,21 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'i' && src[1] && ! IDENT_CHAR ((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;
@@ -384,125 +619,125 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 'x' && src[1] >= '0' && src[1] <= '1'
+  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'
+  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'
+  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' && ! IDENT_CHAR ((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'
+  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'
+  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'
+  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'
+  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' && ! IDENT_CHAR ((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' && ! IDENT_CHAR ((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' && ! IDENT_CHAR ((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' && ! IDENT_CHAR ((unsigned char) src[2]))
+  if (l0 == 'p' && l1 == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
     {
       /* 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'
+  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'
+  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'
+  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'
+  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')
        {
@@ -522,7 +757,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'd' && src[1] == 'r')
+  if (l0 == 'd' && l1 == 'r')
     {
       if (src[2] == '1')
        {
@@ -542,7 +777,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'x' && src[1] == 'd')
+  if (l0 == 'x' && l1 == 'd')
     {
       if (src[2] == '1')
        {
@@ -562,7 +797,7 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'v')
+  if (l0 == 'f' && l1 == 'v')
     {
       if (src[2] == '1'&& src[3] == '2' && ! IDENT_CHAR ((unsigned char) src[4]))
        {
@@ -578,22 +813,25 @@ parse_reg (src, mode, reg)
          return 3;
        }
     }
-  if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
+  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' && ! IDENT_CHAR ((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' && ! IDENT_CHAR ((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;
@@ -1162,21 +1400,6 @@ get_specific (opcode, operands)
   return 0;
 }
 
-int
-check (operand, low, high)
-     expressionS *operand;
-     int low;
-     int high;
-{
-  if (operand->X_op != O_constant
-      || operand->X_add_number < low
-      || operand->X_add_number > high)
-    {
-      as_bad (_("operand must be absolute in range %d..%d"), low, high);
-    }
-  return operand->X_add_number;
-}
-
 static void
 insert (where, how, pcrel, op)
      char *where;
@@ -1278,15 +1501,15 @@ insert_loop_bounds (output, operand)
 
 /* Now we know what sort of opcodes it is, let's build the bytes.  */
 
-static void
+static unsigned int
 build_Mytes (opcode, operand)
      sh_opcode_info *opcode;
      sh_operand_info *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;
@@ -1383,6 +1606,7 @@ build_Mytes (opcode, operand)
       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
@@ -1417,9 +1641,9 @@ find_cooked_opcode (str_p)
       /* The machine independent code will convert CMP/EQ into cmp/EQ
         because it thinks the '/' is the end of the symbol.  Moreover,
         all but the first sub-insn is a parallel processing insn won't
-        be capitailzed.  Instead of hacking up the machine independent
+        be capitalized.  Instead of hacking up the machine independent
         code, we just deal with it here.  */
-      c = isupper (c) ? tolower (c) : c;
+      c = TOLOWER (c);
       name[nlen] = c;
       nlen++;
     }
@@ -1436,7 +1660,7 @@ find_cooked_opcode (str_p)
 /* 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;
@@ -1447,6 +1671,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.  */
@@ -1464,11 +1689,12 @@ assemble_ppi (op_end, opcode)
        {
          /* 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)
@@ -1613,6 +1839,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;
@@ -1626,8 +1853,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;
@@ -1638,6 +1868,7 @@ 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
@@ -1651,6 +1882,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;
@@ -1673,48 +1905,54 @@ 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, &operand[0]);
-      build_relax (opcode, &operand[0]);
+      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);
+           }
+       }
     }
 
+#ifdef BFD_ASSEMBLER
+  dwarf2_emit_insn (size);
+#endif
 }
 
 /* This routine is called each time a label definition is seen.  It
@@ -1761,6 +1999,24 @@ symbolS *
 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;
 }
 
@@ -1877,11 +2133,13 @@ CONST char *md_shortopts = "";
 struct option md_longopts[] =
 {
 #define OPTION_RELAX  (OPTION_MD_BASE)
-#define OPTION_LITTLE (OPTION_MD_BASE + 1)
+#define OPTION_BIG (OPTION_MD_BASE + 1)
+#define OPTION_LITTLE (OPTION_BIG + 1)
 #define OPTION_SMALL (OPTION_LITTLE + 1)
 #define OPTION_DSP (OPTION_SMALL + 1)
 
   {"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},
@@ -1900,8 +2158,11 @@ md_parse_option (c, arg)
       sh_relax = 1;
       break;
 
+    case OPTION_BIG:
+      target_big_endian = 1;
+      break;
+
     case OPTION_LITTLE:
-      shl = 1;
       target_big_endian = 0;
       break;
 
@@ -1927,18 +2188,12 @@ md_show_usage (stream)
   fprintf (stream, _("\
 SH options:\n\
 -little                        generate little endian code\n\
+-big                   generate big endian code\n\
 -relax                 alter jump instructions for long displacements\n\
 -small                 align sections to 4 byte boundaries, not 16\n\
 -dsp                   enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
 }
 \f
-void
-tc_Nout_fix_to_chars ()
-{
-  printf (_("call to tc_Nout_fix_to_chars \n"));
-  abort ();
-}
-
 /* This struct is used to pass arguments to sh_count_relocs through
    bfd_map_over_sections.  */
 
@@ -2087,8 +2342,8 @@ sh_frob_section (abfd, sec, ignore)
         We have already adjusted the value of sym to include the
         fragment address, so we undo that adjustment here.  */
       subseg_change (sec, 0);
-      fix_new (symbol_get_frag (sym),
-              S_GET_VALUE (sym) - symbol_get_frag (sym)->fr_address,
+      fix_new (fscan->fx_frag,
+              S_GET_VALUE (sym) - fscan->fx_frag->fr_address,
               4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
     }
 }
@@ -2158,58 +2413,19 @@ 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):
@@ -2236,7 +2452,7 @@ md_convert_frag (headers, seg, fragP)
        /* Toggle the true/false bit of the bcond.  */
        buffer[highbyte] ^= 0x2;
 
-       /* If this is a dalayed branch, we may not put the the bra in the
+       /* If this is a delayed branch, we may not put the bra in the
           slot.  So we change it to a non-delayed branch, like that:
           b! cond slot_label; bra disp; slot_label: slot_insn
           ??? We should try if swapping the conditional branch and
@@ -2282,59 +2498,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:
@@ -2351,7 +2527,7 @@ md_convert_frag (headers, seg, fragP)
 
 valueT
 md_section_align (seg, size)
-     segT seg;
+     segT seg ATTRIBUTE_UNUSED;
      valueT size;
 {
 #ifdef BFD_ASSEMBLER
@@ -2422,7 +2598,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);
@@ -2436,17 +2612,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
@@ -2510,6 +2716,20 @@ 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)
@@ -2571,11 +2791,49 @@ md_apply_fix (fixP, val)
   int shift;
 
 #ifdef BFD_ASSEMBLER
+  /* A difference between two symbols, the second of which is in the
+     current section, is transformed in a PC-relative relocation to
+     the other symbol.  We have to adjust the relocation type here.  */
+  if (fixP->fx_pcrel)
+    {
+      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.  */
-  if (fixP->fx_addsy != NULL
+     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
@@ -2674,40 +2932,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:
@@ -2735,6 +2972,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 ();
     }
@@ -2765,60 +3039,69 @@ md_estimate_size_before_relax (fragP, segment_type)
      register fragS *fragP;
      register segT segment_type;
 {
+  int what;
+
   switch (fragP->fr_subtype)
     {
+    default:
+      abort ();
+
     case C (UNCOND_JUMP, UNDEF_DISP):
       /* Used to be a branch to somewhere which was unknown.  */
       if (!fragP->fr_symbol)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
-         fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
        }
       else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
        {
          fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
-         fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
        }
       else
        {
          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):
+      what = GET_WHAT (fragP->fr_subtype);
       /* 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.  */
          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.  */
          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.  */
          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, UNCOND32):
+    case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP, COND8):
+    case C (COND_JUMP, COND12):
+    case C (COND_JUMP, COND32):
+    case C (COND_JUMP, UNDEF_WORD_DISP):
+    case C (COND_JUMP_DELAY, COND8):
+    case C (COND_JUMP_DELAY, COND12):
+    case C (COND_JUMP_DELAY, COND32):
+    case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+      /* When relaxing a section for the second time, we don't need to
+        do anything besides return the current size.  */
       break;
     }
+
+  fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
   return fragP->fr_var;
 }
 
@@ -2837,9 +3120,23 @@ md_number_to_chars (ptr, use, nbytes)
 }
 
 long
-md_pcrel_from (fixP)
+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)
+         || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+    {
+      /* The symbol is undefined (or is defined but not in this section,
+        or we're not sure about it being the final definition).  Let the
+        linker figure it out.  We need to adjust the subtraction of a
+        symbol to the position of the relocated data, though.  */
+      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;
 }
 
@@ -2854,36 +3151,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 ATTRIBUTE_UNUSED;
-     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
 
@@ -3086,6 +3353,8 @@ tc_gen_reloc (section, fixp)
     }
   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.044424 seconds and 4 git commands to generate.