* config/tc-sh.c (sh_elf_suffix): Removed.
authorAlexandre Oliva <aoliva@redhat.com>
Thu, 24 Jan 2002 02:54:47 +0000 (02:54 +0000)
committerAlexandre Oliva <aoliva@redhat.com>
Thu, 24 Jan 2002 02:54:47 +0000 (02:54 +0000)
(sh_PIC_related_p, sh_check_fixup, sh_cons_fix_new,
sh_end_of_match, sh_parse_name): New functions.
(sh_elf_cons): Simplify.
(parse_exp): Reject misplaced PIC operands.
(md_undefined_symbol): Simplify.
(sh_fix_adjustable): Let @GOTOFF be adjusted.
(md_apply_fix3): Write @PLT and @GOTOFF addends in place.
(tc_gen_reloc): Move fixp subsy absolute value into addnumber.
Complain if subsy remains at the end.
* config/tc-sh.h (sh_parse_name, sh_cons_fix_new): Declare.
(md_parse_name, TC_CONS_FIX_NEW, O_PIC_reloc): Define.

gas/ChangeLog
gas/config/tc-sh.c
gas/config/tc-sh.h

index 16961a3b126cb0bc94ba883868ce10c4335d92fe..3532a9b77110c9a69f4a6dd66dbe4bf679481b20 100644 (file)
@@ -1,3 +1,18 @@
+2002-01-24  Alexandre Oliva  <aoliva@redhat.com>
+
+       * config/tc-sh.c (sh_elf_suffix): Removed.
+       (sh_PIC_related_p, sh_check_fixup, sh_cons_fix_new,
+       sh_end_of_match, sh_parse_name): New functions.
+       (sh_elf_cons): Simplify.
+       (parse_exp): Reject misplaced PIC operands.
+       (md_undefined_symbol): Simplify.
+       (sh_fix_adjustable): Let @GOTOFF be adjusted.
+       (md_apply_fix3): Write @PLT and @GOTOFF addends in place.
+       (tc_gen_reloc): Move fixp subsy absolute value into addnumber.
+       Complain if subsy remains at the end.
+       * config/tc-sh.h (sh_parse_name, sh_cons_fix_new): Declare.
+       (md_parse_name, TC_CONS_FIX_NEW, O_PIC_reloc): Define.
+
 2002-01-22  Alexandre Oliva  <aoliva@redhat.com>
 
        * config/tc-mn10300.c (xr_registers): Move `pc'...
index 27c33cd016a6e835a4372229098029b0cb762ba7..b2a7d9729472e3560c6998b396f00857242fbbb9 100644 (file)
@@ -58,8 +58,6 @@ static sh_opcode_info *find_cooked_opcode PARAMS ((char **));
 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 *));
@@ -78,6 +76,10 @@ static unsigned int build_Mytes
 #ifdef OBJ_ELF
 static void sh_elf_cons PARAMS ((int));
 
+inline static int sh_PIC_related_p PARAMS ((symbolS *));
+static int sh_check_fixup PARAMS ((expressionS *, bfd_reloc_code_real_type *));
+inline static char *sh_end_of_match PARAMS ((char *, char *));
+
 symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 #endif
 
@@ -262,77 +264,150 @@ 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;
+/* Determinet whether the symbol needs any kind of PIC relocation.  */
+
+inline static int
+sh_PIC_related_p (sym)
+     symbolS *sym;
 {
-  struct map_bfd {
-    char *string;
-    int length;
-    bfd_reloc_code_real_type reloc;
-  };
+  expressionS *exp;
 
-  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)
+  if (! sym)
+    return 0;
+
+  if (sym == GOT_symbol)
+    return 1;
+
+  exp = symbol_get_value_expression (sym);
+
+  return (exp->X_op == O_PIC_reloc
+         || sh_PIC_related_p (exp->X_add_symbol)
+         || sh_PIC_related_p (exp->X_op_symbol));
+}
+
+/* Determine the relocation type to be used to represent the
+   expression, that may be rearranged.  */
+
+static int
+sh_check_fixup (main_exp, r_type_p)
+     expressionS *main_exp;
+     bfd_reloc_code_real_type *r_type_p;
+{
+  expressionS *exp = main_exp;
+
+  /* This is here for backward-compatibility only.  GCC used to generated:
+
+       f@PLT + . - (.LPCS# + 2)
+
+     but we'd rather be able to handle this as a PIC-related reference
+     plus/minus a symbol.  However, gas' parser gives us:
+
+       O_subtract (O_add (f@PLT, .), .LPCS#+2)
+       
+     so we attempt to transform this into:
+
+        O_subtract (f@PLT, O_subtract (.LPCS#+2, .))
+
+     which we can handle simply below.  */     
+  if (exp->X_op == O_subtract)
+    {
+      if (sh_PIC_related_p (exp->X_op_symbol))
+       return 1;
+
+      exp = symbol_get_value_expression (exp->X_add_symbol);
+
+      if (exp && sh_PIC_related_p (exp->X_op_symbol))
+       return 1;
+
+      if (exp && exp->X_op == O_add
+         && sh_PIC_related_p (exp->X_add_symbol))
+       {
+         symbolS *sym = exp->X_add_symbol;
+
+         exp->X_op = O_subtract;
+         exp->X_add_symbol = main_exp->X_op_symbol;
+
+         main_exp->X_op_symbol = main_exp->X_add_symbol;
+         main_exp->X_add_symbol = sym;
+
+         main_exp->X_add_number += exp->X_add_number;
+         exp->X_add_number = 0;
+       }
+
+      exp = main_exp;
+    }
+  else if (exp->X_op == O_add && sh_PIC_related_p (exp->X_op_symbol))
+    return 1;
+
+  if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract)
+    {
+      if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+       {
+         *r_type_p = BFD_RELOC_SH_GOTPC;
+         return 0;
+       }
+      exp = symbol_get_value_expression (exp->X_add_symbol);
+      if (! exp)
+       return 0;
+    }
+
+  if (exp->X_op == O_PIC_reloc)
+    {
+      *r_type_p = exp->X_md;
+      if (exp == main_exp)
+       exp->X_op = O_symbol;
+      else
+       {
+         main_exp->X_add_symbol = exp->X_add_symbol;
+         main_exp->X_add_number += exp->X_add_number;
+       }
+    }
+  else
+    return (sh_PIC_related_p (exp->X_add_symbol)
+           || sh_PIC_related_p (exp->X_op_symbol));
+
+  return 0;
+}
+
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG.  */
+
+void
+sh_cons_fix_new (frag, off, size, exp)
+     fragS *frag;
+     int off, size;
+     expressionS *exp;
+{
+  bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+
+  if (sh_check_fixup (exp, &r_type))
+    as_bad (_("Invalid PIC expression."));
+
+  if (r_type == BFD_RELOC_UNUSED)
+    switch (size)
       {
-       /* 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;
-         }
+      case 1:
+       r_type = BFD_RELOC_8;
+       break;
 
-       *str_p = str;
-       return ptr->reloc;
-      }
+      case 2:
+       r_type = BFD_RELOC_16;
+       break;
+
+      case 4:
+       r_type = BFD_RELOC_32;
+       break;
 
-  return BFD_RELOC_UNUSED;
+      default:
+       goto error;
+      }
+  else if (size != 4)
+    {
+    error:
+      as_bad (_("unsupported BFD relocation size %u"), size);
+      r_type = BFD_RELOC_UNUSED;
+    }
+    
+  fix_new_exp (frag, off, size, exp, 0, r_type);
 }
 
 /* The regular cons() function, that reads constants, doesn't support
@@ -343,9 +418,7 @@ 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;
+  expressionS exp;
 
   if (is_it_end_of_statement ())
     {
@@ -356,79 +429,7 @@ sh_elf_cons (nbytes)
   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);
+      emit_expr (&exp, (unsigned int) nbytes);
     }
   while (*input_line_pointer++ == ',');
 
@@ -867,6 +868,12 @@ parse_exp (s, op)
   expression (&op->immediate);
   if (op->immediate.X_op == O_absent)
     as_bad (_("missing operand"));
+#ifdef OBJ_ELF
+  else if (op->immediate.X_op == O_PIC_reloc
+          || sh_PIC_related_p (op->immediate.X_add_symbol)
+          || sh_PIC_related_p (op->immediate.X_op_symbol))
+    as_bad (_("misplaced PIC operand"));
+#endif
   new = input_line_pointer;
   input_line_pointer = save;
   return new;
@@ -2012,26 +2019,8 @@ sh_flush_pending_output ()
 
 symbolS *
 md_undefined_symbol (name)
-     char *name;
+     char *name ATTRIBUTE_UNUSED;
 {
-#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;
 }
 
@@ -2741,7 +2730,6 @@ sh_fix_adjustable (fixP)
     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;
 
@@ -2980,6 +2968,8 @@ md_apply_fix3 (fixP, valP, seg)
       /* Make the jump instruction point to the address of the operand.  At
         runtime we merely add the offset to the actual PLT entry.  */
       * valP = 0xfffffffc;
+      val = fixP->fx_addnumber - S_GET_VALUE (fixP->fx_subsy);
+      md_number_to_chars (buf, val, 4);
       break;
 
     case BFD_RELOC_SH_GOTPC:
@@ -3009,6 +2999,7 @@ md_apply_fix3 (fixP, valP, seg)
       break;
 
     case BFD_RELOC_32_GOTOFF:
+      md_number_to_chars (buf, val, 4);
       break;
 #endif
 
@@ -3322,6 +3313,13 @@ tc_gen_reloc (section, fixp)
   *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
+  if (fixp->fx_subsy
+      && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+    {
+      fixp->fx_addnumber -= S_GET_VALUE (fixp->fx_subsy);
+      fixp->fx_subsy = 0;
+    }
+
   r_type = fixp->fx_r_type;
 
   if (SWITCH_TABLE (fixp))
@@ -3361,7 +3359,7 @@ tc_gen_reloc (section, fixp)
     rel->addend = 0;
 
   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
-  if (rel->howto == NULL)
+  if (rel->howto == NULL || fixp->fx_subsy)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("Cannot represent relocation type %s"),
@@ -3374,4 +3372,87 @@ tc_gen_reloc (section, fixp)
   return rel;
 }
 
+#ifdef OBJ_ELF
+inline static char *
+sh_end_of_match (cont, what)
+     char *cont, *what;
+{
+  int len = strlen (what);
+
+  if (strncasecmp (cont, what, strlen (what)) == 0
+      && ! is_part_of_name (cont[len]))
+    return cont + len;
+
+  return NULL;
+}  
+
+int
+sh_parse_name (name, exprP, nextcharP)
+     char const *name;
+     expressionS *exprP;
+     char *nextcharP;
+{
+  char *next = input_line_pointer;
+  char *next_end;
+  int reloc_type;
+  segT segment;
+
+  exprP->X_op_symbol = NULL;
+
+  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+    {
+      if (! GOT_symbol)
+       GOT_symbol = symbol_find_or_make (name);
+
+      exprP->X_add_symbol = GOT_symbol;
+    no_suffix:
+      /* If we have an absolute symbol or a reg, then we know its
+            value now.  */
+      segment = S_GET_SEGMENT (exprP->X_add_symbol);
+      if (segment == absolute_section)
+       {
+         exprP->X_op = O_constant;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else if (segment == reg_section)
+       {
+         exprP->X_op = O_register;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else
+       {
+         exprP->X_op = O_symbol;
+         exprP->X_add_number = 0;
+       }
+
+      return 1;
+    }
+
+  exprP->X_add_symbol = symbol_find_or_make (name);
+  
+  if (*nextcharP != '@')
+    goto no_suffix;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTOFF")))
+    reloc_type = BFD_RELOC_32_GOTOFF;
+  else if ((next_end = sh_end_of_match (next + 1, "GOT")))
+    reloc_type = BFD_RELOC_32_GOT_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "PLT")))
+    reloc_type = BFD_RELOC_32_PLT_PCREL;
+  else
+    goto no_suffix;
+
+  *input_line_pointer = *nextcharP;
+  input_line_pointer = next_end;
+  *nextcharP = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  exprP->X_op = O_PIC_reloc;
+  exprP->X_add_number = 0;
+  exprP->X_md = reloc_type;
+
+  return 1;
+}
+#endif
 #endif /* BFD_ASSEMBLER */
index dc7d17331346f04687d6b6351c00fd05ac400007..09aacd7ee443d56f553f08c6938d282ad10a556b 100644 (file)
@@ -210,4 +210,18 @@ extern void sh_elf_final_processing PARAMS ((void));
           && S_IS_DEFINED ((FIX)->fx_addsy)                    \
           && ! S_IS_COMMON ((FIX)->fx_addsy))))
 
+#define md_parse_name(name, exprP, nextcharP) \
+  sh_parse_name ((name), (exprP), (nextcharP))
+int sh_parse_name PARAMS ((char const *name,
+                          expressionS *exprP,
+                          char *nextchar));
+
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \
+  sh_cons_fix_new ((FRAG), (OFF), (LEN), (EXP))
+void sh_cons_fix_new PARAMS ((fragS *, int, int, expressionS *));
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+   symbols.  The relocation type is stored in X_md.  */
+#define O_PIC_reloc O_md1
+
 #endif /* OBJ_ELF */