This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

SH: new, simpler PIC parsing technique


A while ago, I've brought up a discussion regarding a new, perhaps
more efficient way to parse PIC expressions such as symbol@GOT, @PLT,
@GOTOFF and GLOBAL_OFFSET_TABLE.  Here's the implementation I came up
with.  IMO, it greatly simplifies PIC handling in the SH assembler,
it's more reusable, in part because it's not limited to PIC
expressions in .longs or so, but rather usable in expressions in
general.  I've verified that this patch makes no difference in the
binutils and GCC testsuites results.  Ok to install?

Index: gas/ChangeLog
from  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.

Index: gas/config/tc-sh.c
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/config/tc-sh.c,v
retrieving revision 1.50
diff -u -p -c -r1.50 tc-sh.c
*** gas/config/tc-sh.c	20 Dec 2001 17:28:13 -0000	1.50
--- gas/config/tc-sh.c	24 Jan 2002 01:58:53 -0000
*************** static sh_opcode_info *find_cooked_opcod
*** 58,65 ****
  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 *));
--- 58,63 ----
*************** static unsigned int build_Mytes
*** 78,83 ****
--- 76,85 ----
  #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
  
*************** static struct hash_control *opcode_hash_
*** 262,338 ****
  
  
  #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
--- 264,413 ----
  
  
  #ifdef OBJ_ELF
! /* Determinet whether the symbol needs any kind of PIC relocation.  */
! 
! inline static int
! sh_PIC_related_p (sym)
!      symbolS *sym;
  {
!   expressionS *exp;
  
!   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)
        {
!       case 1:
! 	r_type = BFD_RELOC_8;
! 	break;
  
!       case 2:
! 	r_type = BFD_RELOC_16;
! 	break;
  
!       case 4:
! 	r_type = BFD_RELOC_32;
! 	break;
  
!       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
*************** static void
*** 343,351 ****
  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 ())
      {
--- 418,424 ----
  sh_elf_cons (nbytes)
       register int nbytes;	/* 1=.byte, 2=.word, 4=.long */
  {
!   expressionS exp;
  
    if (is_it_end_of_statement ())
      {
*************** sh_elf_cons (nbytes)
*** 356,434 ****
    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++ == ',');
  
--- 429,435 ----
    do
      {
        expression (&exp);
!       emit_expr (&exp, (unsigned int) nbytes);
      }
    while (*input_line_pointer++ == ',');
  
*************** parse_exp (s, op)
*** 867,872 ****
--- 868,879 ----
    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;
*************** sh_flush_pending_output ()
*** 2012,2037 ****
  
  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;
  }
  
--- 2019,2026 ----
  
  symbolS *
  md_undefined_symbol (name)
!      char *name ATTRIBUTE_UNUSED;
  {
    return 0;
  }
  
*************** sh_fix_adjustable (fixP)
*** 2741,2747 ****
      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;
  
--- 2730,2735 ----
*************** md_apply_fix3 (fixP, valP, seg)
*** 2980,2985 ****
--- 2968,2975 ----
        /* 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:
*************** md_apply_fix3 (fixP, valP, seg)
*** 3009,3014 ****
--- 2999,3005 ----
        break;
  
      case BFD_RELOC_32_GOTOFF:
+       md_number_to_chars (buf, val, 4);
        break;
  #endif
  
*************** tc_gen_reloc (section, fixp)
*** 3322,3327 ****
--- 3313,3325 ----
    *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))
*************** tc_gen_reloc (section, fixp)
*** 3361,3367 ****
      rel->addend = 0;
  
    rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
!   if (rel->howto == NULL)
      {
        as_bad_where (fixp->fx_file, fixp->fx_line,
  		    _("Cannot represent relocation type %s"),
--- 3359,3365 ----
      rel->addend = 0;
  
    rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
!   if (rel->howto == NULL || fixp->fx_subsy)
      {
        as_bad_where (fixp->fx_file, fixp->fx_line,
  		    _("Cannot represent relocation type %s"),
*************** tc_gen_reloc (section, fixp)
*** 3374,3377 ****
--- 3372,3458 ----
    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: gas/config/tc-sh.h
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/config/tc-sh.h,v
retrieving revision 1.17
diff -u -p -c -r1.17 tc-sh.h
*** gas/config/tc-sh.h	8 Jan 2002 04:22:59 -0000	1.17
--- gas/config/tc-sh.h	23 Jan 2002 18:54:50 -0000
*************** extern void sh_elf_final_processing PARA
*** 210,213 ****
--- 210,227 ----
  	   && 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 */

-- 
Alexandre Oliva   Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer                  aoliva@{cygnus.com, redhat.com}
CS PhD student at IC-Unicamp        oliva@{lsd.ic.unicamp.br, gnu.org}
Free Software Evangelist    *Please* write to mailing lists, not to me

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]