aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog8
-rw-r--r--gas/write.c61
-rw-r--r--gas/write.h4
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