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]

fix missing Xtensa relocations for difference of symbols


I've committed this patch for the Xtensa port of GAS to fix a problem of missing relocations. When doing link-time relaxation (which is now the default for Xtensa), a relocation is required to identify values that are the difference of two symbols from the same section. I missed this change last fall when I dumped a bunch of Xtensa changes into the code. The patch also removes a few unnecessary checks, and moves code that belongs in md_apply_fix3 out of tc_gen_reloc.

I'm planning to put this patch on the 2.16 branch. In fact, I have a number of Xtensa-specific changes that I'd like to make before 2.16 goes out. Some of these changes are more risky than would usually be appropriate for a release branch, but I think they will still be improvements to the current state of the Xtensa code. I would have liked to get the Xtensa-related binutils code in better shape before the branch was made, but I haven't been able to spend much time on it until recently. Daniel said to use good judgement and I assume this is OK, but I wanted to explain the situation ahead of time in case anyone objects.

2005-03-17 Bob Wilson <bob.wilson@acm.org>

        * config/tc-xtensa.c (xg_apply_tentative_value): Rename to
        xg_apply_fix_value and return a value to indicate success.
        (md_pcrel_from): Skip check of fx_done.  Return 0 if not PC-relative.
        (xtensa_force_relocation): Remove checks for VTABLE relocs.
        (xtensa_validate_fix_sub): New.
        (xtensa_fix_adjustable): Remove check for external or weak symbols.
        (tc_gen_reloc): Move code to handle difference of symbols and code to
        apply tentative fix values to ...
        (md_apply_fix3): ...here.  Enable standard overflow checks for simple
        8, 16, and 32 bit relocations.  Apply fixes for slot-specific
        relocations when linkrelax flag is not set.
        * config/tc-xtensa.h (xtensa_validate_fix_sub): Add prototype.
        (TC_FORCE_RELOCATION_SUB_SAME, TC_VALIDATE_FIX_SUB): Define.


Index: gas/config/tc-xtensa.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-xtensa.c,v
retrieving revision 1.23
diff -u -p -r1.23 tc-xtensa.c
--- gas/config/tc-xtensa.c	11 Mar 2005 00:14:15 -0000	1.23
+++ gas/config/tc-xtensa.c	17 Mar 2005 18:52:03 -0000
@@ -4934,8 +4934,8 @@ xtensa_find_unaligned_loops (bfd *abfd A
 }
 
 
-static void
-xg_apply_tentative_value (fixS *fixP, valueT val)
+static int
+xg_apply_fix_value (fixS *fixP, valueT val)
 {
   xtensa_isa isa = xtensa_default_isa;
   static xtensa_insnbuf insnbuf = NULL;
@@ -4967,11 +4967,9 @@ xg_apply_tentative_value (fixS *fixP, va
 
   /* CONST16 immediates are not PC-relative, despite the fact that we
      reuse the normal PC-relative operand relocations for the low part
-     of a CONST16 operand.  The code in tc_gen_reloc does not decode
-     the opcodes so it is more convenient to detect this special case
-     here.  */
+     of a CONST16 operand.  */
   if (opcode == xtensa_const16_opcode)
-    return;
+    return 0;
 
   xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
 			      get_relaxable_immed (opcode), val,
@@ -4979,6 +4977,8 @@ xg_apply_tentative_value (fixS *fixP, va
 
   xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
   xtensa_insnbuf_to_chars (isa, insnbuf, fixpos, 0);
+
+  return 1;
 }
 
 
@@ -5452,11 +5452,8 @@ md_pcrel_from (fixS *fixP)
   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
   bfd_boolean alt_reloc;
 
-  if (fixP->fx_done)
-    return addr;
-
   if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
-    return addr;
+    return 0;
 
   if (!insnbuf)
     {
@@ -5477,14 +5474,15 @@ md_pcrel_from (fixS *fixP)
   xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
   opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
 
-  /* Check for "alternate" relocation (operand not specified).  */
+  /* Check for "alternate" relocations (operand not specified).  None
+     of the current uses for these are really PC-relative.  */
   if (alt_reloc || opcode == xtensa_const16_opcode)
     {
       if (opcode != xtensa_l32r_opcode
 	  && opcode != xtensa_const16_opcode)
 	as_fatal (_("invalid relocation for '%s' instruction"),
 		  xtensa_opcode_name (isa, opcode));
-      return addr;
+      return 0;
     }
 
   opnum = get_relaxable_immed (opcode);
@@ -5496,7 +5494,7 @@ md_pcrel_from (fixS *fixP)
 		    fixP->fx_line,
 		    _("invalid relocation for operand %d of '%s'"),
 		    opnum, xtensa_opcode_name (isa, opcode));
-      return addr;
+      return 0;
     }
   return 0 - opnd_value;
 }
@@ -5508,7 +5506,8 @@ int
 xtensa_force_relocation (fixS *fix)
 {
   switch (fix->fx_r_type)
-   {
+    {
+    case BFD_RELOC_XTENSA_ASM_EXPAND:
     case BFD_RELOC_XTENSA_SLOT0_ALT:
     case BFD_RELOC_XTENSA_SLOT1_ALT:
     case BFD_RELOC_XTENSA_SLOT2_ALT:
@@ -5524,8 +5523,6 @@ xtensa_force_relocation (fixS *fix)
     case BFD_RELOC_XTENSA_SLOT12_ALT:
     case BFD_RELOC_XTENSA_SLOT13_ALT:
     case BFD_RELOC_XTENSA_SLOT14_ALT:
-    case BFD_RELOC_VTABLE_INHERIT:
-    case BFD_RELOC_VTABLE_ENTRY:
       return 1;
     default:
       break;
@@ -5539,6 +5536,33 @@ xtensa_force_relocation (fixS *fix)
 }
 
 
+/* TC_VALIDATE_FIX_SUB hook */
+
+int
+xtensa_validate_fix_sub (fixS *fix)
+{
+  segT add_symbol_segment, sub_symbol_segment;
+
+  /* The difference of two symbols should be resolved by the assembler when
+     linkrelax is not set.  If the linker may relax the section containing
+     the symbols, then an Xtensa DIFF relocation must be generated so that
+     the linker knows to adjust the difference value.  */
+  if (!linkrelax || fix->fx_addsy == NULL)
+    return 0;
+
+  /* Make sure both symbols are in the same segment, and that segment is
+     "normal" and relaxable.  If the segment is not "normal", then the
+     fix is not valid.  If the segment is not "relaxable", then the fix
+     should have been handled earlier.  */
+  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+  if (! SEG_NORMAL (add_symbol_segment) ||
+      ! relaxable_section (add_symbol_segment))
+    return 0;
+  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+  return (sub_symbol_segment == add_symbol_segment);
+}
+
+
 /* NO_PSEUDO_DOT hook */
 
 /* This function has nothing to do with pseudo dots, but this is the
@@ -5586,49 +5610,134 @@ xtensa_fix_adjustable (fixS *fixP)
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 0;
 
-  if (fixP->fx_addsy
-      && (S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)))
-    return 0;
-
   return 1;
 }
 
 
 void
-md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg)
 {
-  if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+  char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+  valueT val;
+
+  switch (fixP->fx_r_type)
     {
-      char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+    case BFD_RELOC_32:
+    case BFD_RELOC_16:
+    case BFD_RELOC_8:
+      if (linkrelax && fixP->fx_subsy)
+	{
+	  switch (fixP->fx_r_type)
+	    {
+	    case BFD_RELOC_8:
+	      fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+	      break;
+	    case BFD_RELOC_16:
+	      fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+	      break;
+	    case BFD_RELOC_32:
+	      fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+	      break;
+	    default:
+	      break;
+	    }
 
-      switch (fixP->fx_r_type)
+	  /* An offset is only allowed when it results from adjusting a
+	     local symbol into a section-relative offset.  If the offset
+	     came from the original expression, tc_fix_adjustable will have
+	     prevented the fix from being converted to a section-relative
+	     form so that we can flag the error here.  */
+	  if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("cannot represent subtraction with an offset"));
+
+	  val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+		 - S_GET_VALUE (fixP->fx_subsy));
+
+	  /* The difference value gets written out, and the DIFF reloc
+	     identifies the address of the subtracted symbol (i.e., the one
+	     with the lowest address).  */
+	  *valP = val;
+	  fixP->fx_offset -= val;
+	  fixP->fx_subsy = NULL;
+	}
+      else if (! fixP->fx_addsy)
 	{
-	case BFD_RELOC_XTENSA_ASM_EXPAND:
+	  val = *valP;
 	  fixP->fx_done = 1;
-	  break;
+	}
+      else
+	break;
+      md_number_to_chars (fixpos, val, fixP->fx_size);
+      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
+      break;
 
-	case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-	  as_bad (_("unhandled local relocation fix %s"),
-		  bfd_get_reloc_code_name (fixP->fx_r_type));
-	  break;
+    case BFD_RELOC_XTENSA_SLOT0_OP:
+    case BFD_RELOC_XTENSA_SLOT1_OP:
+    case BFD_RELOC_XTENSA_SLOT2_OP:
+    case BFD_RELOC_XTENSA_SLOT3_OP:
+    case BFD_RELOC_XTENSA_SLOT4_OP:
+    case BFD_RELOC_XTENSA_SLOT5_OP:
+    case BFD_RELOC_XTENSA_SLOT6_OP:
+    case BFD_RELOC_XTENSA_SLOT7_OP:
+    case BFD_RELOC_XTENSA_SLOT8_OP:
+    case BFD_RELOC_XTENSA_SLOT9_OP:
+    case BFD_RELOC_XTENSA_SLOT10_OP:
+    case BFD_RELOC_XTENSA_SLOT11_OP:
+    case BFD_RELOC_XTENSA_SLOT12_OP:
+    case BFD_RELOC_XTENSA_SLOT13_OP:
+    case BFD_RELOC_XTENSA_SLOT14_OP:
+      if (linkrelax)
+	{
+	  /* Write the tentative value of a PC-relative relocation to a
+	     local symbol into the instruction.  The value will be ignored
+	     by the linker, and it makes the object file disassembly
+	     readable when all branch targets are encoded in relocations.  */
+
+	  assert (fixP->fx_addsy);
+	  if (S_GET_SEGMENT (fixP->fx_addsy) == seg && !fixP->fx_plt
+	      && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+	    {
+	      val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+		     - md_pcrel_from (fixP));
+	      (void) xg_apply_fix_value (fixP, val);
+	    }
+	}
+      else if (! fixP->fx_addsy)
+	{
+	  val = *valP;
+	  if (xg_apply_fix_value (fixP, val))
+	    fixP->fx_done = 1;
+	}
+      break;
 
-	case BFD_RELOC_32:
-	case BFD_RELOC_16:
-	case BFD_RELOC_8:
-	  /* The only one we support that isn't an instruction field.  */
-	  md_number_to_chars (fixpos, *valP, fixP->fx_size);
-	  fixP->fx_done = 1;
-	  break;
+    case BFD_RELOC_XTENSA_ASM_EXPAND:
+    case BFD_RELOC_XTENSA_SLOT0_ALT:
+    case BFD_RELOC_XTENSA_SLOT1_ALT:
+    case BFD_RELOC_XTENSA_SLOT2_ALT:
+    case BFD_RELOC_XTENSA_SLOT3_ALT:
+    case BFD_RELOC_XTENSA_SLOT4_ALT:
+    case BFD_RELOC_XTENSA_SLOT5_ALT:
+    case BFD_RELOC_XTENSA_SLOT6_ALT:
+    case BFD_RELOC_XTENSA_SLOT7_ALT:
+    case BFD_RELOC_XTENSA_SLOT8_ALT:
+    case BFD_RELOC_XTENSA_SLOT9_ALT:
+    case BFD_RELOC_XTENSA_SLOT10_ALT:
+    case BFD_RELOC_XTENSA_SLOT11_ALT:
+    case BFD_RELOC_XTENSA_SLOT12_ALT:
+    case BFD_RELOC_XTENSA_SLOT13_ALT:
+    case BFD_RELOC_XTENSA_SLOT14_ALT:
+      /* These all need to be resolved at link-time.  Do nothing now.  */
+      break;
 
-	case BFD_RELOC_VTABLE_INHERIT:
-	case BFD_RELOC_VTABLE_ENTRY:
-	  fixP->fx_done = 0;
-	  break;
+    case BFD_RELOC_VTABLE_INHERIT:
+    case BFD_RELOC_VTABLE_ENTRY:
+      fixP->fx_done = 0;
+      break;
 
-	default:
-	  as_bad (_("unhandled local relocation fix %s"),
-		  bfd_get_reloc_code_name (fixP->fx_r_type));
-	}
+    default:
+      as_bad (_("unhandled local relocation fix %s"),
+	      bfd_get_reloc_code_name (fixP->fx_r_type));
     }
 }
 
@@ -5687,10 +5796,9 @@ md_estimate_size_before_relax (fragS *fr
    format.  */
 
 arelent *
-tc_gen_reloc (asection *section, fixS *fixp)
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 {
   arelent *reloc;
-  bfd_boolean apply_tentative_value = FALSE;
 
   reloc = (arelent *) xmalloc (sizeof (arelent));
   reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
@@ -5701,128 +5809,7 @@ tc_gen_reloc (asection *section, fixS *f
      They'd better have been fully resolved by this point.  */
   assert ((int) fixp->fx_r_type > 0);
 
-  if (linkrelax && fixp->fx_subsy
-      && (fixp->fx_r_type == BFD_RELOC_8
-	  || fixp->fx_r_type == BFD_RELOC_16
-	  || fixp->fx_r_type == BFD_RELOC_32))
-    {
-      int diff_size = 0;
-      bfd_vma diff_value, diff_mask = 0;
-
-      switch (fixp->fx_r_type)
-	{
-	case BFD_RELOC_8:
-	  fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
-	  diff_size = 1;
-	  diff_mask = 0xff;
-	  break;
-	case BFD_RELOC_16:
-	  fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
-	  diff_size = 2;
-	  diff_mask = 0xffff;
-	  break;
-	case BFD_RELOC_32:
-	  fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
-	  diff_size = 4;
-	  diff_mask = 0xffffffff;
-	  break;
-	default:
-	  break;
-	}
-
-      /* An offset is only allowed when it results from adjusting a local
-	 symbol into a section-relative offset.  If the offset came from the
-	 original expression, tc_fix_adjustable will have prevented the fix
-	 from being converted to a section-relative form so that we can flag
-	 the error here.  */
-      if (fixp->fx_offset != 0 && !symbol_section_p (fixp->fx_addsy))
-	{
-	  as_bad_where (fixp->fx_file, fixp->fx_line,
-			_("cannot represent subtraction with an offset"));
-	  free (reloc->sym_ptr_ptr);
-	  free (reloc);
-	  return NULL;
-	}
-
-      assert (S_GET_SEGMENT (fixp->fx_addsy)
-	      == S_GET_SEGMENT (fixp->fx_subsy));
-
-      diff_value = (S_GET_VALUE (fixp->fx_addsy) + fixp->fx_offset
-		    - S_GET_VALUE (fixp->fx_subsy));
-
-      /* Check for overflow.  */
-      if ((diff_value & ~diff_mask) != 0)
-	{
-	  as_bad_where (fixp->fx_file, fixp->fx_line,
-			_("value of %ld too large"), diff_value);
-	  free (reloc->sym_ptr_ptr);
-	  free (reloc);
-	  return NULL;
-	}
-
-      md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
-			  diff_value, diff_size);
-      reloc->addend = fixp->fx_offset - diff_value;
-    }
-  else
-    {
-      reloc->addend = fixp->fx_offset;
-
-      switch (fixp->fx_r_type)
-	{
-	case BFD_RELOC_XTENSA_SLOT0_OP:
-	case BFD_RELOC_XTENSA_SLOT1_OP:
-	case BFD_RELOC_XTENSA_SLOT2_OP:
-	case BFD_RELOC_XTENSA_SLOT3_OP:
-	case BFD_RELOC_XTENSA_SLOT4_OP:
-	case BFD_RELOC_XTENSA_SLOT5_OP:
-	case BFD_RELOC_XTENSA_SLOT6_OP:
-	case BFD_RELOC_XTENSA_SLOT7_OP:
-	case BFD_RELOC_XTENSA_SLOT8_OP:
-	case BFD_RELOC_XTENSA_SLOT9_OP:
-	case BFD_RELOC_XTENSA_SLOT10_OP:
-	case BFD_RELOC_XTENSA_SLOT11_OP:
-	case BFD_RELOC_XTENSA_SLOT12_OP:
-	case BFD_RELOC_XTENSA_SLOT13_OP:
-	case BFD_RELOC_XTENSA_SLOT14_OP:
-	  /* As a special case, the immediate value for a CONST16 opcode
-	     should not be applied, since this kind of relocation is
-	     handled specially for CONST16 and is not really PC-relative.
-	     Rather than decode the opcode here, just wait and handle it
-	     in xg_apply_tentative_value.  */
-	  apply_tentative_value = TRUE;
-	  break;
-
-	case BFD_RELOC_XTENSA_SLOT0_ALT:
-	case BFD_RELOC_XTENSA_SLOT1_ALT:
-	case BFD_RELOC_XTENSA_SLOT2_ALT:
-	case BFD_RELOC_XTENSA_SLOT3_ALT:
-	case BFD_RELOC_XTENSA_SLOT4_ALT:
-	case BFD_RELOC_XTENSA_SLOT5_ALT:
-	case BFD_RELOC_XTENSA_SLOT6_ALT:
-	case BFD_RELOC_XTENSA_SLOT7_ALT:
-	case BFD_RELOC_XTENSA_SLOT8_ALT:
-	case BFD_RELOC_XTENSA_SLOT9_ALT:
-	case BFD_RELOC_XTENSA_SLOT10_ALT:
-	case BFD_RELOC_XTENSA_SLOT11_ALT:
-	case BFD_RELOC_XTENSA_SLOT12_ALT:
-	case BFD_RELOC_XTENSA_SLOT13_ALT:
-	case BFD_RELOC_XTENSA_SLOT14_ALT:
-	case BFD_RELOC_XTENSA_ASM_EXPAND:
-	case BFD_RELOC_32:
-	case BFD_RELOC_XTENSA_PLT:
-	case BFD_RELOC_VTABLE_INHERIT:
-	case BFD_RELOC_VTABLE_ENTRY:
-	  break;
-
-	case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
-	  as_warn (_("emitting simplification relocation"));
-	  break;
-
-	default:
-	  as_warn (_("emitting unknown relocation"));
-	}
-    }
+  reloc->addend = fixp->fx_offset;
 
   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
   if (reloc->howto == NULL)
@@ -5839,24 +5826,6 @@ tc_gen_reloc (asection *section, fixS *f
     as_fatal (_("internal error? cannot generate `%s' relocation"),
 	      bfd_get_reloc_code_name (fixp->fx_r_type));
 
-  /* Write the tentative value of a PC-relative relocation to a local symbol
-     into the instruction.  The value will be ignored by the linker, and it
-     makes the object file disassembly readable when the linkrelax flag is
-     set and all branch targets are encoded in relocations.  */
-
-  if (linkrelax && apply_tentative_value && fixp->fx_pcrel)
-    {
-      valueT val;
-      assert (fixp->fx_addsy);
-      if (S_GET_SEGMENT (fixp->fx_addsy) == section && !fixp->fx_plt
-	  && !S_FORCE_RELOC (fixp->fx_addsy, 1))
-	{
-	  val = (S_GET_VALUE (fixp->fx_addsy) + fixp->fx_offset
-		 - md_pcrel_from (fixp));
-	  xg_apply_tentative_value (fixp, val);
-	}
-    }
-
   return reloc;
 }
 
Index: gas/config/tc-xtensa.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-xtensa.h,v
retrieving revision 1.8
diff -u -p -r1.8 tc-xtensa.h
--- gas/config/tc-xtensa.h	11 Mar 2005 00:14:15 -0000	1.8
+++ gas/config/tc-xtensa.h	17 Mar 2005 18:52:03 -0000
@@ -291,6 +291,7 @@ extern const char *xtensa_target_format 
 extern void xtensa_init_fix_data (struct fix *);
 extern void xtensa_frag_init (fragS *);
 extern int xtensa_force_relocation (struct fix *);
+extern int xtensa_validate_fix_sub (struct fix *);
 extern void xtensa_frob_label (struct symbol *);
 extern void xtensa_end (void);
 extern void xtensa_post_relax_hook (void);
@@ -314,6 +315,9 @@ extern char *xtensa_section_rename (char
 #define TC_FRAG_TYPE			struct xtensa_frag_type
 #define TC_FRAG_INIT(frag)		xtensa_frag_init (frag)
 #define TC_FORCE_RELOCATION(fix)	xtensa_force_relocation (fix)
+#define TC_FORCE_RELOCATION_SUB_SAME(fix, seg) \
+  (! SEG_NORMAL (seg) || xtensa_force_relocation (fix))
+#define	TC_VALIDATE_FIX_SUB(fix)	xtensa_validate_fix_sub (fix)
 #define NO_PSEUDO_DOT			xtensa_check_inside_bundle ()
 #define tc_canonicalize_symbol_name(s)	xtensa_section_rename (s)
 #define tc_canonicalize_section_name(s)	xtensa_section_rename (s)

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