diff options
-rw-r--r-- | gas/ChangeLog | 8 | ||||
-rw-r--r-- | gas/write.c | 61 | ||||
-rw-r--r-- | gas/write.h | 4 |
3 files changed, 56 insertions, 17 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 76386d7..669e4cc 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,11 @@ +2006-05-08 Alan Modra <amodra@bigpond.net.au> + + * write.c (relax_segment): Add pass count arg. Don't error on + negative org/space on first two passes. + (relax_seg_info): New struct. + (relax_seg, write_object_file): Adjust. + * write.h (relax_segment): Update prototype. + 2006-05-05 Julian Brown <julian@codesourcery.com> * config/tc-arm.c (parse_vfp_reg_list): Improve register bounds diff --git a/gas/write.c b/gas/write.c index 93b157f..9e28a6f 100644 --- a/gas/write.c +++ b/gas/write.c @@ -512,19 +512,21 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP) #endif } -static void relax_seg (bfd *, asection *, PTR); +struct relax_seg_info +{ + int pass; + int changed; +}; static void -relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, PTR xxx) +relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *xxx) { segment_info_type *seginfo = seg_info (sec); + struct relax_seg_info *info = (struct relax_seg_info *) xxx; if (seginfo && seginfo->frchainP - && relax_segment (seginfo->frchainP->frch_root, sec)) - { - int *result = (int *) xxx; - *result = 1; - } + && relax_segment (seginfo->frchainP->frch_root, sec, info->pass)) + info->changed = 1; } static void size_seg (bfd *, asection *, PTR); @@ -1206,6 +1208,7 @@ subsegs_finish (void) void write_object_file (void) { + struct relax_seg_info rsi; #ifndef WORKING_DOT_WORD fragS *fragP; /* Track along all frags. */ #endif @@ -1264,10 +1267,9 @@ write_object_file (void) merge_data_into_text (); } + rsi.pass = 0; while (1) { - int changed; - #ifndef WORKING_DOT_WORD /* We need to reset the markers in the broken word list and associated frags between calls to relax_segment (via @@ -1288,9 +1290,10 @@ write_object_file (void) } #endif - changed = 0; - bfd_map_over_sections (stdoutput, relax_seg, &changed); - if (!changed) + rsi.changed = 0; + bfd_map_over_sections (stdoutput, relax_seg, &rsi); + rsi.pass++; + if (!rsi.changed) break; } @@ -1721,7 +1724,7 @@ relax_align (register relax_addressT address, /* Address now. */ addresses. */ int -relax_segment (struct frag *segment_frag_root, segT segment) +relax_segment (struct frag *segment_frag_root, segT segment, int pass) { unsigned long frag_count; struct frag *fragP; @@ -1835,6 +1838,7 @@ relax_segment (struct frag *segment_frag_root, segT segment) if (max_iterations < frag_count) max_iterations = frag_count; + ret = 0; do { stretch = 0; @@ -1964,6 +1968,26 @@ relax_segment (struct frag *segment_frag_root, segT segment) growth = target - after; if (growth < 0) { + growth = 0; + + /* Don't error on first few frag relax passes. + The symbol might be an expression involving + symbol values from other sections. If those + sections have not yet been processed their + frags will all have zero addresses, so we + will calculate incorrect values for them. The + number of passes we allow before giving an + error is somewhat arbitrary. It should be at + least one, with larger values requiring + increasingly contrived dependencies between + frags to trigger a false error. */ + if (pass < 2) + { + /* Force another pass. */ + ret = 1; + break; + } + /* Growth may be negative, but variable part of frag cannot have fewer than 0 chars. That is, we can't .org backwards. */ @@ -1976,7 +2000,7 @@ relax_segment (struct frag *segment_frag_root, segT segment) fragP->fr_subtype = 0; fragP->fr_offset = 0; fragP->fr_fix = after - was_address; - growth = stretch; + break; } /* This is an absolute growth factor */ @@ -2002,6 +2026,14 @@ relax_segment (struct frag *segment_frag_root, segT segment) } else if (amount < 0) { + /* Don't error on first few frag relax passes. + See rs_org comment for a longer explanation. */ + if (pass < 2) + { + ret = 1; + break; + } + as_warn_where (fragP->fr_file, fragP->fr_line, _(".space or .fill with negative value, ignored")); fragP->fr_symbol = 0; @@ -2063,7 +2095,6 @@ relax_segment (struct frag *segment_frag_root, segT segment) segment_name (segment)); } - ret = 0; for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) if (fragP->last_fr_address != fragP->fr_address) { diff --git a/gas/write.h b/gas/write.h index 77ce75e..1f9b72d 100644 --- a/gas/write.h +++ b/gas/write.h @@ -1,6 +1,6 @@ /* write.h Copyright 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, 2001, - 2002, 2003, 2005 Free Software Foundation, Inc. + 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -156,7 +156,7 @@ extern int get_recorded_alignment (segT seg); extern void subsegs_finish (void); extern void write_object_file (void); extern long relax_frag (segT, fragS *, long); -extern int relax_segment (struct frag * seg_frag_root, segT seg_type); +extern int relax_segment (struct frag *, segT, int); extern void number_to_chars_littleendian (char *, valueT, int); extern void number_to_chars_bigendian (char *, valueT, int); extern fixS *fix_new |