/* write.c - emit .o file
Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
|| fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
return 1;
+ if (fix->fx_addsy == NULL)
+ return 0;
+
return S_FORCE_RELOC (fix->fx_addsy, fix->fx_subsy == NULL);
}
fragS dummy, *prev_frag = &dummy;
fixS fix_dummy, *prev_fix = &fix_dummy;
- for (; frchp && frchp->frch_seg == section; frchp = frchp->frch_next)
+ for (; frchp; frchp = frchp->frch_next)
{
prev_frag->fr_next = frchp->frch_root;
prev_frag = frchp->frch_last;
}
}
assert (prev_frag->fr_type != 0);
+ assert (prev_frag != &dummy);
prev_frag->fr_next = 0;
return prev_frag;
}
#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);
/* If this symbol is equated to an undefined or common symbol,
convert the fixup to being against that symbol. */
- if (symbol_equated_reloc_p (sym))
+ if (symbol_equated_reloc_p (sym)
+ || S_IS_WEAKREFR (sym))
{
- symbolS *new_sym
- = symbol_get_value_expression (sym)->X_add_symbol;
- const char *name = S_GET_NAME (sym);
- if (!S_IS_COMMON (new_sym)
- && !TC_FAKE_LABEL (name)
- && (!S_IS_EXTERNAL (sym) || S_IS_LOCAL (sym)))
- as_bad (_("Local symbol `%s' can't be equated to undefined symbol `%s'"),
- name, S_GET_NAME (new_sym));
fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
- sym = new_sym;
+ sym = symbol_get_value_expression (sym)->X_add_symbol;
fixp->fx_addsy = sym;
}
f->fr_literal, (file_ptr) offset,
(bfd_size_type) f->fr_fix);
if (!x)
- {
- bfd_perror (stdoutput->filename);
- as_perror (_("FATAL: Can't write %s"), stdoutput->filename);
- exit (EXIT_FAILURE);
- }
+ as_fatal (_("can't write %s: %s"), stdoutput->filename,
+ bfd_errmsg (bfd_get_error ()));
offset += f->fr_fix;
}
fill_literal = f->fr_literal + f->fr_fix;
(file_ptr) offset,
(bfd_size_type) fill_size);
if (!x)
- {
- bfd_perror (stdoutput->filename);
- as_perror (_("FATAL: Can't write %s"),
- stdoutput->filename);
- exit (EXIT_FAILURE);
- }
+ as_fatal (_("can't write %s: %s"), stdoutput->filename,
+ bfd_errmsg (bfd_get_error ()));
offset += fill_size;
}
}
of the section. This allows proper nop-filling at the end of
code-bearing sections. */
#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \
- (!(FRCHAIN)->frch_next || (FRCHAIN)->frch_next->frch_seg != (SEG) \
- ? get_recorded_alignment (SEG) : 0)
+ (!(FRCHAIN)->frch_next ? get_recorded_alignment (SEG) : 0)
#else
#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
#endif
subsegs_finish (void)
{
struct frchain *frchainP;
+ asection *s;
- for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
+ for (s = stdoutput->sections; s; s = s->next)
{
- int alignment = 0;
+ segment_info_type *seginfo = seg_info (s);
+ if (!seginfo)
+ continue;
- subseg_set (frchainP->frch_seg, frchainP->frch_subseg);
-
- /* This now gets called even if we had errors. In that case,
- any alignment is meaningless, and, moreover, will look weird
- if we are generating a listing. */
- if (!had_errors ())
+ for (frchainP = seginfo->frchainP;
+ frchainP != NULL;
+ frchainP = frchainP->frch_next)
{
- alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
- if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE)
- && now_seg->entsize)
- {
- unsigned int entsize = now_seg->entsize;
- int entalign = 0;
+ int alignment = 0;
- while ((entsize & 1) == 0)
+ subseg_set (s, frchainP->frch_subseg);
+
+ /* This now gets called even if we had errors. In that case,
+ any alignment is meaningless, and, moreover, will look weird
+ if we are generating a listing. */
+ if (!had_errors ())
+ {
+ alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
+ if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE)
+ && now_seg->entsize)
{
- ++entalign;
- entsize >>= 1;
+ unsigned int entsize = now_seg->entsize;
+ int entalign = 0;
+
+ while ((entsize & 1) == 0)
+ {
+ ++entalign;
+ entsize >>= 1;
+ }
+ if (entalign > alignment)
+ alignment = entalign;
}
- if (entalign > alignment)
- alignment = entalign;
}
- }
- if (subseg_text_p (now_seg))
- frag_align_code (alignment, 0);
- else
- frag_align (alignment, 0, 0);
+ if (subseg_text_p (now_seg))
+ frag_align_code (alignment, 0);
+ else
+ frag_align (alignment, 0, 0);
- /* frag_align will have left a new frag.
- Use this last frag for an empty ".fill".
+ /* frag_align will have left a new frag.
+ Use this last frag for an empty ".fill".
- For this segment ...
- Create a last frag. Do not leave a "being filled in frag". */
- frag_wane (frag_now);
- frag_now->fr_fix = 0;
- know (frag_now->fr_next == NULL);
+ For this segment ...
+ Create a last frag. Do not leave a "being filled in frag". */
+ frag_wane (frag_now);
+ frag_now->fr_fix = 0;
+ know (frag_now->fr_next == NULL);
+ }
}
}
void
write_object_file (void)
{
+ struct relax_seg_info rsi;
#ifndef WORKING_DOT_WORD
fragS *fragP; /* Track along all frags. */
#endif
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
}
#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;
}
/* Skip symbols which were equated to undefined or common
symbols. */
- if (symbol_equated_reloc_p (symp))
+ if (symbol_equated_reloc_p (symp)
+ || S_IS_WEAKREFR (symp))
{
const char *name = S_GET_NAME (symp);
if (S_IS_COMMON (symp)
&& !TC_FAKE_LABEL (name)
+ && !S_IS_WEAKREFR (symp)
&& (!S_IS_EXTERNAL (symp) || S_IS_LOCAL (symp)))
{
expressionS *e = symbol_get_value_expression (symp);
continue;
}
- /* So far, common symbols have been treated like undefined symbols.
- Put them in the common section now. */
- if (S_IS_DEFINED (symp) == 0
- && S_GET_VALUE (symp) != 0)
- S_SET_SEGMENT (symp, bfd_com_section_ptr);
-
#ifdef obj_frob_symbol
obj_frob_symbol (symp, punt);
#endif
opposites. Sometimes the former checks flags and the
latter examines the name... */
|| (!S_IS_EXTERNAL (symp)
- && (punt || S_IS_LOCAL (symp))
+ && (punt || S_IS_LOCAL (symp) ||
+ (S_IS_WEAKREFD (symp) && ! symbol_used_p (symp)))
&& ! symbol_used_in_reloc_p (symp)))
{
symbol_remove (symp, &symbol_rootP, &symbol_lastP);
addresses. */
int
-relax_segment (struct frag *segment_frag_root, segT segment)
+relax_segment (struct frag *segment_frag_root, segT segment, int pass)
{
- register struct frag *fragP;
- register relax_addressT address;
+ unsigned long frag_count;
+ struct frag *fragP;
+ relax_addressT address;
int ret;
/* In case md_estimate_size_before_relax() wants to make fixSs. */
/* For each frag in segment: count and store (a 1st guess of)
fr_address. */
address = 0;
- for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
+ for (frag_count = 0, fragP = segment_frag_root;
+ fragP;
+ fragP = fragP->fr_next, frag_count ++)
{
fragP->relax_marker = 0;
fragP->fr_address = address;
/* Do relax(). */
{
+ unsigned long max_iterations;
offsetT stretch; /* May be any size, 0 or negative. */
/* Cumulative number of addresses we have relaxed this pass.
We may have relaxed more than one address. */
grew, and another shrank. If a branch instruction doesn't fit anymore,
we could be scrod. */
+ /* We want to prevent going into an infinite loop where one frag grows
+ depending upon the location of a symbol which is in turn moved by
+ the growing frag. eg:
+
+ foo = .
+ .org foo+16
+ foo = .
+
+ So we dictate that this algorithm can be at most O2. */
+ max_iterations = frag_count * frag_count;
+ /* Check for overflow. */
+ if (max_iterations < frag_count)
+ max_iterations = frag_count;
+
+ ret = 0;
do
{
stretch = 0;
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. */
fragP->fr_subtype = 0;
fragP->fr_offset = 0;
fragP->fr_fix = after - was_address;
- growth = stretch;
+ break;
}
/* This is an absolute growth factor */
}
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;
stretch += growth;
stretched = 1;
}
- } /* For each frag in the segment. */
+ }
}
- while (stretched); /* Until nothing further to relax. */
- } /* do_relax */
+ /* Until nothing further to relax. */
+ while (stretched && -- max_iterations);
+
+ if (stretched)
+ as_fatal (_("Infinite loop encountered whilst attempting to compute the addresses of symbols in section %s"),
+ segment_name (segment));
+ }
- ret = 0;
for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
if (fragP->last_fr_address != fragP->fr_address)
{