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]

Re: relaxation segv


On Sun, 11 Mar 2001, Richard Henderson wrote:

> Now, what I'm most concerned about is how this ever worked.
> My guess is "by accident".  It's currently dependant on what
> order the sections exist in the bfd.
> 
> If a relaxation expresion involves differences between symbols
> in a different section, then we need to relax the sections in
> the proper order.  Moreover, there can be loops in the dependancy
> graph that require us to re-run relaxation on a section.
> 
> However, this is somewhat difficult, and is not aided by the
> fact that it currently doesn't work to re-run relax_section
> (you'll get aborts in cvt_frag_to_fill for unknown reasons).
> 
> Something that does work for this test case is to relax all the
> code sections before all the data sections.  I have a feeling
> this catches the bulk of the interesting cases, as happens with
> EH data and Dwarf2 frame data.  Thus the attached patch.

I spent a little time analysing this problem, and implemented a fix for
x86.  Re-running relaxation fails on x86 because fr_subtype isn't set back
to the unrelaxed state but md_estimate_size_before_relax returns a size
corresponding to the unrelaxed state.

The following doesn't have a ChangeLog yet, nor comments.  I'm just
posting it for feedback at the moment.  The basic idea is
a) Complex expressions cannot be marked `resolved' until relax is
   complete.  That's what finalize_syms is all about.
b) Relaxation repeats until no frag changes.  This means that we always
   relax at least twice, except in very trivial cases.

With this patch installed we still pass "make check", and I was able to
bootstrap current CVS gcc --enable-languages="c,c++,f" so things are
looking good.  However, I'll need to carefully check each back-end for
problems similar to x86 md_estimate_size_after_relax.

Oh yeah, testing all this revealed problems elsewhere in the
assembler.  More on that in a separate email.  

Alan
-- 
Linuxcare

Index: frags.h
===================================================================
RCS file: /cvs/src/src/gas/frags.h,v
retrieving revision 1.9
diff -u -p -r1.9 frags.h
--- frags.h	2001/03/08 23:24:22	1.9
+++ frags.h	2001/03/13 05:03:25
@@ -43,6 +43,9 @@ struct obstack;
 struct frag {
   /* Object file address (as an octet offset).  */
   addressT fr_address;
+  /* When relaxing multiple times, remember the address the frag had
+     in the last relax pass.  */
+  addressT last_fr_address;
   /* Chain forward; ascending address order.  Rooted in frch_root.  */
   struct frag *fr_next;
 
Index: symbols.c
===================================================================
RCS file: /cvs/src/src/gas/symbols.c,v
retrieving revision 1.19
diff -u -p -r1.19 symbols.c
--- symbols.c	2001/03/08 23:24:22	1.19
+++ symbols.c	2001/03/13 05:03:39
@@ -866,6 +866,8 @@ resolve_symbol_value (symp, finalize)
 
   resolved = 0;
   final_seg = S_GET_SEGMENT (symp);
+  if (final_seg == expr_section && finalize != 2)
+    finalize = 0;
 
   if (symp->sy_resolving)
     {
@@ -1182,7 +1184,7 @@ resolve_local_symbol (key, value)
      PTR value;
 {
   if (value != NULL)
-    resolve_symbol_value (value, 1);
+    resolve_symbol_value (value, finalize_syms);
 }
 
 #endif
@@ -1574,7 +1576,11 @@ S_GET_VALUE (s)
 #endif
 
   if (!s->sy_resolved && s->sy_value.X_op != O_constant)
-    resolve_symbol_value (s, 1);
+    {
+      valueT val = resolve_symbol_value (s, finalize_syms);
+      if (finalize_syms != 2 && S_GET_SEGMENT (s) == expr_section)
+	return val;
+    }
   if (s->sy_value.X_op != O_constant)
     {
       static symbolS *recur;
Index: write.c
===================================================================
RCS file: /cvs/src/src/gas/write.c,v
retrieving revision 1.27
diff -u -p -r1.27 write.c
--- write.c	2001/03/08 23:24:22	1.27
+++ write.c	2001/03/13 05:04:01
@@ -61,6 +61,8 @@ extern CONST int md_short_jump_size;
 extern CONST int md_long_jump_size;
 #endif
 
+int finalize_syms = 1;
+
 int symbol_table_frozen;
 void print_fixup PARAMS ((fixS *));
 
@@ -122,7 +124,6 @@ static fragS *chain_frchains_together_1 
 #ifdef BFD_ASSEMBLER
 static void chain_frchains_together PARAMS ((bfd *, segT, PTR));
 static void cvt_frag_to_fill PARAMS ((segT, fragS *));
-static void relax_and_size_seg PARAMS ((bfd *, asection *, PTR));
 static void adjust_reloc_syms PARAMS ((bfd *, asection *, PTR));
 static void write_relocs PARAMS ((bfd *, asection *, PTR));
 static void write_contents PARAMS ((bfd *, asection *, PTR));
@@ -593,8 +594,26 @@ cvt_frag_to_fill (headersP, sec, fragP)
 #endif /* defined (BFD_ASSEMBLER) || !defined (BFD)  */
 
 #ifdef BFD_ASSEMBLER
+static void relax_seg PARAMS ((bfd *, asection *, PTR));
 static void
-relax_and_size_seg (abfd, sec, xxx)
+relax_seg (abfd, sec, xxx)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *sec;
+     PTR xxx;
+{
+  segment_info_type *seginfo = seg_info (sec);
+
+  if (seginfo && seginfo->frchainP
+      && relax_segment (seginfo->frchainP->frch_root, sec))
+    {
+      int *result = (int *) xxx;
+      *result = 1;
+    }
+}
+
+static void size_seg PARAMS ((bfd *, asection *, PTR));
+static void
+size_seg (abfd, sec, xxx)
      bfd *abfd;
      asection *sec;
      PTR xxx ATTRIBUTE_UNUSED;
@@ -607,12 +626,9 @@ relax_and_size_seg (abfd, sec, xxx)
 
   subseg_change (sec, 0);
 
-  flags = bfd_get_section_flags (abfd, sec);
-
   seginfo = seg_info (sec);
   if (seginfo && seginfo->frchainP)
     {
-      relax_segment (seginfo->frchainP->frch_root, sec);
       for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
 	cvt_frag_to_fill (sec, fragp);
       for (fragp = seginfo->frchainP->frch_root;
@@ -625,6 +641,8 @@ relax_and_size_seg (abfd, sec, xxx)
   else
     size = 0;
 
+  flags = bfd_get_section_flags (abfd, sec);
+
   if (size > 0 && ! seginfo->bss)
     flags |= SEC_HAS_CONTENTS;
 
@@ -735,10 +753,10 @@ adjust_reloc_syms (abfd, sec, xxx)
 	   symbols, though, since they are not in the regular symbol
 	   table.  */
 	if (sym != NULL)
-	  resolve_symbol_value (sym, 1);
+	  resolve_symbol_value (sym, 2);
 
 	if (fixp->fx_subsy != NULL)
-	  resolve_symbol_value (fixp->fx_subsy, 1);
+	  resolve_symbol_value (fixp->fx_subsy, 2);
 
 	/* If this symbol is equated to an undefined symbol, convert
            the fixup to being against that symbol.  */
@@ -1515,11 +1533,23 @@ write_object_file ()
 #endif
 
 #ifdef BFD_ASSEMBLER
-  bfd_map_over_sections (stdoutput, relax_and_size_seg, (char *) 0);
+  while (1)
+    {
+      int changed;
+
+      changed = 0;
+      bfd_map_over_sections (stdoutput, relax_seg, &changed);
+      if (!changed)
+	break;
+    }
+  bfd_map_over_sections (stdoutput, size_seg, (char *) 0);
 #else
   relax_and_size_all_segments ();
 #endif /* BFD_ASSEMBLER  */
 
+  /* Relaxation has completed.  Freeze all syms.  */
+  finalize_syms = 2;
+
 #if defined (BFD_ASSEMBLER) && defined (OBJ_COFF) && defined (TE_GO32)
   /* Now that the segments have their final sizes, run through the
      sections and set their vma and lma. !BFD gas sets them, and BFD gas
@@ -1838,7 +1868,7 @@ write_object_file ()
       symbolS *symp;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
-	resolve_symbol_value (symp, 1);
+	resolve_symbol_value (symp, 2);
     }
   resolve_local_symbol_values ();
 
@@ -1886,7 +1916,7 @@ write_object_file ()
 	  /* Do it again, because adjust_reloc_syms might introduce
 	     more symbols.  They'll probably only be section symbols,
 	     but they'll still need to have the values computed.  */
-	  resolve_symbol_value (symp, 1);
+	  resolve_symbol_value (symp, 2);
 
 	  /* Skip symbols which were equated to undefined or common
              symbols.  */
@@ -2161,13 +2191,15 @@ relax_align (address, alignment)
    these frag addresses may not be the same as final object-file
    addresses.  */
 
-void
+int
 relax_segment (segment_frag_root, segment)
      struct frag *segment_frag_root;
      segT segment;
 {
   register struct frag *fragP;
   register relax_addressT address;
+  int ret;
+
 #if !defined (MANY_SEGMENTS) && !defined (BFD_ASSEMBLER)
   know (segment == SEG_DATA || segment == SEG_TEXT || segment == SEG_BSS);
 #endif
@@ -2465,7 +2497,7 @@ relax_segment (segment_frag_root, segmen
 	    if (growth)
 	      {
 		stretch += growth;
-		stretched++;
+		stretched = 1;
 	      }
 	  }			/* For each frag in the segment.  */
       }
@@ -2476,6 +2508,14 @@ relax_segment (segment_frag_root, segmen
 
   /* All fr_address's are correct, relative to their own segment.
      We have made all the fixS we will ever make.  */
+  ret = 0;
+  for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
+    if (fragP->last_fr_address != fragP->fr_address)
+      {
+	fragP->last_fr_address = fragP->fr_address;
+	ret = 1;
+      }
+  return ret;
 }
 
 #if defined (BFD_ASSEMBLER) || (!defined (BFD) && !defined (OBJ_VMS))
Index: write.h
===================================================================
RCS file: /cvs/src/src/gas/write.h,v
retrieving revision 1.5
diff -u -p -r1.5 write.h
--- write.h	2001/03/08 23:24:22	1.5
+++ write.h	2001/03/13 05:04:04
@@ -157,6 +157,8 @@ struct fix
 
 typedef struct fix fixS;
 
+extern int finalize_syms;
+
 #ifndef BFD_ASSEMBLER
 extern char *next_object_file_charP;
 
@@ -182,7 +184,7 @@ extern int get_recorded_alignment PARAMS
 extern void subsegs_finish PARAMS ((void));
 extern void write_object_file PARAMS ((void));
 extern long relax_frag PARAMS ((segT, fragS *, long));
-extern void relax_segment
+extern int relax_segment
   PARAMS ((struct frag * seg_frag_root, segT seg_type));
 
 extern void number_to_chars_littleendian PARAMS ((char *, valueT, int));
Index: config/tc-i386.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-i386.c,v
retrieving revision 1.90
diff -u -p -r1.90 tc-i386.c
--- tc-i386.c	2001/03/13 04:37:12	1.90
+++ tc-i386.c	2001/03/13 05:04:38
@@ -4003,8 +4003,13 @@ md_estimate_size_before_relax (fragP, se
       frag_wane (fragP);
       return fragP->fr_fix - old_fr_fix;
     }
-  /* Guess a short jump.  */
-  return 1;
+  /* Guess size depending on current relax state.  Initially the relax
+     state will correspond to a short jump and we return 1, because
+     the variable part of the frag (the branch offset) is one byte
+     long.  However, we can relax a section more than once and in that
+     case we must either set fr_subtype back to the unrelaxed state,
+     or return the value for the appropriate branch.  */
+  return 1 + md_relax_table[fragP->fr_subtype].rlx_length;
 }
 
 /* Called after relax() is finished.


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