This is the mail archive of the binutils@sourceware.org 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 PR997, gas listings breakage


Turning on gas listings can break perfectly good assembly.  See the
PR for an example and analysis.   This patch mends some of the damage.

Except when listings are turned on, the frag list traversal will
terminate fairly quickly so there shouldn't be a noticeable slowdown
due to the extra processing here.

	PR 997
	* frags.c (frag_offset_fixed_p): New function.
	* frags.h (frag_offset_fixed_p): Declare.
	* expr.c (expr): Use frag_offset_fixed_p when simplifying subtraction.
	(resolve_expression): Likewise.

Index: gas/frags.c
===================================================================
RCS file: /cvs/src/src/gas/frags.c,v
retrieving revision 1.18
diff -u -p -r1.18 frags.c
--- gas/frags.c	10 May 2005 15:10:04 -0000	1.18
+++ gas/frags.c	4 Apr 2006 07:18:32 -0000
@@ -1,6 +1,6 @@
 /* frags.c - manage frags -
    Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2003, 2004
+   1999, 2000, 2001, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -383,3 +383,55 @@ frag_append_1_char (int datum)
     }
   obstack_1grow (&frchain_now->frch_obstack, datum);
 }
+
+/* Return TRUE if FRAG1 and FRAG2 have a fixed relationship between
+   their start addresses.  Set OFFSET to the difference in address
+   not already accounted for in the frag FR_ADDRESS.  */
+
+bfd_boolean
+frag_offset_fixed_p (fragS *frag1, fragS *frag2, bfd_vma *offset)
+{
+  fragS *frag;
+  bfd_vma off;
+
+  /* Start with offset initialised to difference between the two frags.
+     Prior to assigning frag addresses this will be zero.  */
+  off = frag1->fr_address - frag2->fr_address;
+  if (frag1 == frag2)
+    {
+      *offset = off;
+      return TRUE;
+    }
+
+  /* Maybe frag2 is after frag1.  */
+  frag = frag1;
+  while (frag->fr_type == rs_fill)
+    {
+      off += frag->fr_fix + frag->fr_offset * frag->fr_var;
+      frag = frag->fr_next;
+      if (frag == NULL)
+	break;
+      if (frag == frag2)
+	{
+	  *offset = off;
+	  return TRUE;
+	}
+    }
+
+  /* Maybe frag1 is after frag2.  */
+  frag = frag2;
+  while (frag->fr_type == rs_fill)
+    {
+      off -= frag->fr_fix + frag->fr_offset * frag->fr_var;
+      frag = frag->fr_next;
+      if (frag == NULL)
+	break;
+      if (frag == frag1)
+	{
+	  *offset = off;
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
Index: gas/frags.h
===================================================================
RCS file: /cvs/src/src/gas/frags.h,v
retrieving revision 1.20
diff -u -p -r1.20 frags.h
--- gas/frags.h	8 Jul 2005 05:57:20 -0000	1.20
+++ gas/frags.h	4 Apr 2006 07:18:32 -0000
@@ -1,6 +1,6 @@
 /* frags.h - Header file for the frag concept.
    Copyright 1987, 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -148,4 +148,6 @@ char *frag_var (relax_stateT type,
 		offsetT offset,
 		char *opcode);
 
+bfd_boolean frag_offset_fixed_p (fragS *, fragS *, bfd_vma *);
+
 #endif /* FRAGS_H */
Index: gas/expr.c
===================================================================
RCS file: /cvs/src/src/gas/expr.c,v
retrieving revision 1.64
diff -u -p -r1.64 expr.c
--- gas/expr.c	22 Dec 2005 17:05:40 -0000	1.64
+++ gas/expr.c	4 Apr 2006 08:02:27 -0000
@@ -1,6 +1,6 @@
 /* expr.c -operands, expressions-
    Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -1662,6 +1662,7 @@ expr (int rankarg,		/* Larger # is highe
   while (op_left != O_illegal && op_rank[(int) op_left] > rank)
     {
       segT rightseg;
+      bfd_vma frag_off;
 
       input_line_pointer += op_chars;	/* -> after operator.  */
 
@@ -1741,12 +1742,15 @@ expr (int rankarg,		/* Larger # is highe
       else if (op_left == O_subtract
 	       && right.X_op == O_symbol
 	       && resultP->X_op == O_symbol
-	       && (symbol_get_frag (right.X_add_symbol)
-		   == symbol_get_frag (resultP->X_add_symbol))
+	       && retval == rightseg
 	       && (SEG_NORMAL (rightseg)
-		   || right.X_add_symbol == resultP->X_add_symbol))
+		   || right.X_add_symbol == resultP->X_add_symbol)
+	       && frag_offset_fixed_p (symbol_get_frag (resultP->X_add_symbol),
+				       symbol_get_frag (right.X_add_symbol),
+				       &frag_off))
 	{
 	  resultP->X_add_number -= right.X_add_number;
+	  resultP->X_add_number -= frag_off / OCTETS_PER_BYTE;
 	  resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol)
 				    - S_GET_VALUE (right.X_add_symbol));
 	  resultP->X_op = O_constant;
@@ -1900,6 +1904,7 @@ resolve_expression (expressionS *express
   valueT left, right;
   segT seg_left, seg_right;
   fragS *frag_left, *frag_right;
+  bfd_vma frag_off;
 
   switch (op)
     {
@@ -2002,13 +2007,15 @@ resolve_expression (expressionS *express
 	 on the input value.
 	 Otherwise, both operands must be absolute.  We already handled
 	 the case of addition or subtraction of a constant above.  */
+      frag_off = 0;
       if (!(seg_left == absolute_section
 	       && seg_right == absolute_section)
 	  && !(op == O_eq || op == O_ne)
 	  && !((op == O_subtract
 		|| op == O_lt || op == O_le || op == O_ge || op == O_gt)
 	       && seg_left == seg_right
-	       && (finalize_syms || frag_left == frag_right)
+	       && (finalize_syms
+		   || frag_offset_fixed_p (frag_left, frag_right, &frag_off))
 	       && (seg_left != reg_section || left == right)
 	       && (seg_left != undefined_section || add_symbol == op_symbol)))
 	{
@@ -2068,6 +2075,7 @@ resolve_expression (expressionS *express
 	    return 0;
 	}
 
+      right += frag_off / OCTETS_PER_BYTE;
       switch (op)
 	{
 	case O_add:			left += right; break;

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


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