/* write.c - emit .o file
- Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- 2010 Free Software Foundation, Inc.
+ Copyright (C) 1986-2014 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "libbfd.h"
#include "compress-debug.h"
-#ifndef TC_ADJUST_RELOC_COUNT
-#define TC_ADJUST_RELOC_COUNT(FIX, COUNT)
-#endif
-
#ifndef TC_FORCE_RELOCATION
#define TC_FORCE_RELOCATION(FIX) \
(generic_force_reloc (FIX))
/* Remember the value of dot when parsing expressions. */
addressT dot_value;
+/* The frag that dot_value is based from. */
+fragS *dot_frag;
+
/* Relocs generated by ".reloc" pseudo. */
struct reloc_list* reloc_list;
symbolS *sub_symbol, /* X_op_symbol. */
offsetT offset, /* X_add_number. */
int pcrel, /* TRUE if PC-relative relocation. */
- RELOC_ENUM r_type ATTRIBUTE_UNUSED /* Relocation type. */,
+ RELOC_ENUM r_type /* Relocation type. */,
int at_beginning) /* Add to the start of the list? */
{
fixS *fixP;
fixP->fx_subsy = sub_symbol;
fixP->fx_offset = offset;
fixP->fx_dot_value = dot_value;
+ fixP->fx_dot_frag = dot_frag;
fixP->fx_pcrel = pcrel;
fixP->fx_r_type = r_type;
fixP->fx_im_disp = 0;
prev_fix = frchp->fix_tail;
}
}
- gas_assert (prev_frag->fr_type != 0);
- gas_assert (prev_frag != &dummy);
+ gas_assert (prev_frag != &dummy
+ && prev_frag->fr_type != 0);
prev_frag->fr_next = 0;
return prev_frag;
}
static void
resolve_reloc_expr_symbols (void)
{
+ bfd_vma addr_mask = 1;
struct reloc_list *r;
+ /* Avoid a shift by the width of type. */
+ addr_mask <<= bfd_arch_bits_per_address (stdoutput) - 1;
+ addr_mask <<= 1;
+ addr_mask -= 1;
+
for (r = reloc_list; r; r = r->next)
{
+ reloc_howto_type *howto = r->u.a.howto;
expressionS *symval;
symbolS *sym;
bfd_vma offset, addend;
asection *sec;
- reloc_howto_type *howto;
resolve_symbol_value (r->u.a.offset_sym);
symval = symbol_get_value_expression (r->u.a.offset_sym);
sec = NULL;
}
else if (sym != NULL)
- symbol_mark_used_in_reloc (sym);
+ {
+ /* Convert relocs against local symbols to refer to the
+ corresponding section symbol plus offset instead. Keep
+ PC-relative relocs of the REL variety intact though to
+ prevent the offset from overflowing the relocated field,
+ unless it has enough bits to cover the whole address
+ space. */
+ if (S_IS_LOCAL (sym) && !symbol_section_p (sym)
+ && (sec->use_rela_p
+ || (howto->partial_inplace
+ && (!howto->pc_relative
+ || howto->src_mask == addr_mask))))
+ {
+ asection *symsec = S_GET_SEGMENT (sym);
+ if (!(((symsec->flags & SEC_MERGE) != 0
+ && addend != 0)
+ || (symsec->flags & SEC_THREAD_LOCAL) != 0))
+ {
+ addend += S_GET_VALUE (sym);
+ sym = section_symbol (symsec);
+ }
+ }
+ symbol_mark_used_in_reloc (sym);
+ }
}
if (sym == NULL)
{
sym = abs_section_sym;
}
- howto = r->u.a.howto;
-
r->u.b.sec = sec;
r->u.b.s = symbol_get_bfdsym (sym);
r->u.b.r.sym_ptr_ptr = &r->u.b.s;
if (symsec == NULL)
abort ();
- if (bfd_is_abs_section (symsec))
+ if (bfd_is_abs_section (symsec)
+ || symsec == reg_section)
{
/* The fixup_segment routine normally will not use this
symbol in a relocation. */
handled now. (These consist of fixS where we have since discovered
the value of a symbol, or the address of the frag involved.)
For each one, call md_apply_fix to put the fix into the frag data.
+ Ones that we couldn't completely handle here will be output later
+ by emit_relocations. */
- Result is a count of how many relocation structs will be needed to
- handle the remaining fixS's that we couldn't completely handle here.
- These will be output later by emit_relocations(). */
-
-static long
+static void
fixup_segment (fixS *fixP, segT this_segment)
{
- long seg_reloc_count = 0;
valueT add_number;
fragS *fragP;
segT add_symbol_segment = absolute_section;
symbol_mark_used_in_reloc (fixP->fx_addsy);
if (fixP->fx_subsy != NULL)
symbol_mark_used_in_reloc (fixP->fx_subsy);
- seg_reloc_count++;
}
- TC_ADJUST_RELOC_COUNT (fixP, seg_reloc_count);
- return seg_reloc_count;
+ return;
}
for (; fixP; fixP = fixP->fx_next)
sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
if (fixP->fx_addsy != NULL
&& sub_symbol_segment == add_symbol_segment
+ && !S_FORCE_RELOC (fixP->fx_addsy, 0)
+ && !S_FORCE_RELOC (fixP->fx_subsy, 0)
&& !TC_FORCE_RELOCATION_SUB_SAME (fixP, add_symbol_segment))
{
add_number += S_GET_VALUE (fixP->fx_addsy);
#endif
}
else if (sub_symbol_segment == absolute_section
+ && !S_FORCE_RELOC (fixP->fx_subsy, 0)
&& !TC_FORCE_RELOCATION_SUB_ABS (fixP, add_symbol_segment))
{
add_number -= S_GET_VALUE (fixP->fx_subsy);
fixP->fx_subsy = NULL;
}
else if (sub_symbol_segment == this_segment
+ && !S_FORCE_RELOC (fixP->fx_subsy, 0)
&& !TC_FORCE_RELOCATION_SUB_LOCAL (fixP, add_symbol_segment))
{
add_number -= S_GET_VALUE (fixP->fx_subsy);
fixP->fx_offset = (add_number + fixP->fx_dot_value
- + fixP->fx_frag->fr_address);
+ + fixP->fx_dot_frag->fr_address);
/* Make it pc-relative. If the back-end code has not
selected a pc-relative reloc, cancel the adjustment
S_GET_NAME (fixP->fx_subsy),
segment_name (sub_symbol_segment));
}
+ else if (sub_symbol_segment != undefined_section
+ && ! bfd_is_com_section (sub_symbol_segment)
+ && MD_APPLY_SYM_VALUE (fixP))
+ add_number -= S_GET_VALUE (fixP->fx_subsy);
}
if (fixP->fx_addsy)
{
if (add_symbol_segment == this_segment
+ && !S_FORCE_RELOC (fixP->fx_addsy, 0)
&& !TC_FORCE_RELOCATION_LOCAL (fixP))
{
/* This fixup was made when the symbol's segment was
fixP->fx_pcrel = 0;
}
else if (add_symbol_segment == absolute_section
+ && !S_FORCE_RELOC (fixP->fx_addsy, 0)
&& !TC_FORCE_RELOCATION_ABS (fixP))
{
add_number += S_GET_VALUE (fixP->fx_addsy);
if (!fixP->fx_done)
{
- ++seg_reloc_count;
if (fixP->fx_addsy == NULL)
fixP->fx_addsy = abs_section_sym;
symbol_mark_used_in_reloc (fixP->fx_addsy);
print_fixup (fixP);
#endif
} /* For each fixS in this segment. */
-
- TC_ADJUST_RELOC_COUNT (fixP, seg_reloc_count);
- return seg_reloc_count;
}
static void
}
}
+static fragS *
+get_frag_for_reloc (fragS *last_frag,
+ const segment_info_type *seginfo,
+ const struct reloc_list *r)
+{
+ fragS *f;
+
+ for (f = last_frag; f != NULL; f = f->fr_next)
+ if (f->fr_address <= r->u.b.r.address
+ && r->u.b.r.address < f->fr_address + f->fr_fix)
+ return f;
+
+ for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next)
+ if (f->fr_address <= r->u.b.r.address
+ && r->u.b.r.address < f->fr_address + f->fr_fix)
+ return f;
+
+ as_bad_where (r->file, r->line,
+ _("reloc not within (fixed part of) section"));
+ return NULL;
+}
+
static void
write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
{
segment_info_type *seginfo = seg_info (sec);
- unsigned int i;
unsigned int n;
struct reloc_list *my_reloc_list, **rp, *r;
arelent **relocs;
fixS *fixp;
+ fragS *last_frag;
/* If seginfo is NULL, we did not create this section; don't do
anything with it. */
relocs = (arelent **) xcalloc (n, sizeof (arelent *));
- i = 0;
+ n = 0;
+ r = my_reloc_list;
+ last_frag = NULL;
for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
{
- int j;
int fx_size, slack;
offsetT loc;
+ arelent **reloc;
+#ifndef RELOC_EXPANSION_POSSIBLE
+ arelent *rel;
+
+ reloc = &rel;
+#endif
if (fixp->fx_done)
continue;
_("internal error: fixup not contained within frag"));
#ifndef RELOC_EXPANSION_POSSIBLE
- {
- arelent *reloc = tc_gen_reloc (sec, fixp);
-
- if (!reloc)
- continue;
- relocs[i++] = reloc;
- j = 1;
- }
+ *reloc = tc_gen_reloc (sec, fixp);
#else
- {
- arelent **reloc = tc_gen_reloc (sec, fixp);
+ reloc = tc_gen_reloc (sec, fixp);
+#endif
- for (j = 0; reloc[j]; j++)
- relocs[i++] = reloc[j];
- }
+ while (*reloc)
+ {
+ while (r != NULL && r->u.b.r.address < (*reloc)->address)
+ {
+ fragS *f = get_frag_for_reloc (last_frag, seginfo, r);
+ if (f != NULL)
+ {
+ last_frag = f;
+ relocs[n++] = &r->u.b.r;
+ install_reloc (sec, &r->u.b.r, f, r->file, r->line);
+ }
+ r = r->next;
+ }
+ relocs[n++] = *reloc;
+ install_reloc (sec, *reloc, fixp->fx_frag,
+ fixp->fx_file, fixp->fx_line);
+#ifndef RELOC_EXPANSION_POSSIBLE
+ break;
+#else
+ reloc++;
#endif
+ }
+ }
- for ( ; j != 0; --j)
- install_reloc (sec, relocs[i - j], fixp->fx_frag,
- fixp->fx_file, fixp->fx_line);
+ while (r != NULL)
+ {
+ fragS *f = get_frag_for_reloc (last_frag, seginfo, r);
+ if (f != NULL)
+ {
+ last_frag = f;
+ relocs[n++] = &r->u.b.r;
+ install_reloc (sec, &r->u.b.r, f, r->file, r->line);
+ }
+ r = r->next;
}
- n = i;
#ifdef DEBUG4
{
- unsigned int i, j, nsyms;
+ unsigned int k, j, nsyms;
asymbol **sympp;
sympp = bfd_get_outsymbols (stdoutput);
nsyms = bfd_get_symcount (stdoutput);
- for (i = 0; i < n; i++)
- if (((*relocs[i]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0)
+ for (k = 0; k < n; k++)
+ if (((*relocs[k]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0)
{
for (j = 0; j < nsyms; j++)
- if (sympp[j] == *relocs[i]->sym_ptr_ptr)
+ if (sympp[j] == *relocs[k]->sym_ptr_ptr)
break;
if (j == nsyms)
abort ();
}
#endif
- for (r = my_reloc_list; r != NULL; r = r->next)
- {
- fragS *f;
- for (f = seginfo->frchainP->frch_root; f; f = f->fr_next)
- if (f->fr_address <= r->u.b.r.address
- && r->u.b.r.address < f->fr_address + f->fr_fix)
- break;
- if (f == NULL)
- as_bad_where (r->file, r->line,
- _("reloc not within (fixed part of) section"));
- else
- {
- relocs[n++] = &r->u.b.r;
- install_reloc (sec, &r->u.b.r, f, r->file, r->line);
- }
- }
-
if (n)
{
flagword flags = bfd_get_section_flags (abfd, sec);
#ifdef DEBUG3
{
- unsigned int i;
- arelent *r;
- asymbol *s;
+ unsigned int k;
+
fprintf (stderr, "relocs for sec %s\n", sec->name);
- for (i = 0; i < n; i++)
+ for (k = 0; k < n; k++)
{
- r = relocs[i];
- s = *r->sym_ptr_ptr;
+ arelent *rel = relocs[k];
+ asymbol *s = *rel->sym_ptr_ptr;
fprintf (stderr, " reloc %2d @%p off %4lx : sym %-10s addend %lx\n",
- i, r, (unsigned long)r->address, s->name, (unsigned long)r->addend);
+ k, rel, (unsigned long)rel->address, s->name,
+ (unsigned long)rel->addend);
}
}
#endif
char *header;
struct z_stream_s *strm;
int x;
+ flagword flags = bfd_get_section_flags (abfd, sec);
if (seginfo == NULL
- || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS)
- || (bfd_get_section_flags (abfd, sec) & SEC_ALLOC))
+ || sec->size < 32
+ || (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC)
return;
section_name = bfd_get_section_name (stdoutput, sec);
(stdoutput, sec, buf, (file_ptr) offset,
(bfd_size_type) n_per_buf * fill_size);
if (!x)
- as_fatal (_("cannot write to output file"));
+ as_fatal (_("cannot write to output file '%s': %s"),
+ stdoutput->filename,
+ bfd_errmsg (bfd_get_error ()));
offset += n_per_buf * fill_size;
}
}
#endif
#endif
-void
-subsegs_finish (void)
+static void
+subsegs_finish_section (asection *s)
{
struct frchain *frchainP;
- asection *s;
+ segment_info_type *seginfo = seg_info (s);
+ if (!seginfo)
+ return;
- for (s = stdoutput->sections; s; s = s->next)
+ for (frchainP = seginfo->frchainP;
+ frchainP != NULL;
+ frchainP = frchainP->frch_next)
{
- segment_info_type *seginfo = seg_info (s);
- if (!seginfo)
- continue;
+ int alignment = 0;
- for (frchainP = seginfo->frchainP;
- frchainP != NULL;
- frchainP = frchainP->frch_next)
- {
- int alignment = 0;
+ subseg_set (s, frchainP->frch_subseg);
- 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 ())
+ /* 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)
{
- 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;
+ unsigned int entsize = now_seg->entsize;
+ int entalign = 0;
- while ((entsize & 1) == 0)
- {
- ++entalign;
- entsize >>= 1;
- }
- if (entalign > alignment)
- alignment = entalign;
+ while ((entsize & 1) == 0)
+ {
+ ++entalign;
+ entsize >>= 1;
}
+
+ 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);
}
}
+static void
+subsegs_finish (void)
+{
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ subsegs_finish_section (s);
+}
+
+#ifdef OBJ_ELF
+static void
+create_obj_attrs_section (void)
+{
+ segT s;
+ char *p;
+ offsetT size;
+ const char *name;
+
+ size = bfd_elf_obj_attr_size (stdoutput);
+ if (size)
+ {
+ name = get_elf_backend_data (stdoutput)->obj_attrs_section;
+ if (!name)
+ name = ".gnu.attributes";
+ s = subseg_new (name, 0);
+ elf_section_type (s)
+ = get_elf_backend_data (stdoutput)->obj_attrs_section_type;
+ bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA);
+ frag_now_fix ();
+ p = frag_more (size);
+ bfd_elf_set_obj_attr_contents (stdoutput, (bfd_byte *)p, size);
+
+ subsegs_finish_section (s);
+ relax_segment (seg_info (s)->frchainP->frch_root, s, 0);
+ size_seg (stdoutput, s, NULL);
+ }
+}
+#endif
+
/* Write the object file. */
void
fragS *fragP; /* Track along all frags. */
#endif
- /* Do we really want to write it? */
- {
- int n_warns, n_errs;
- n_warns = had_warnings ();
- n_errs = had_errors ();
- /* The -Z flag indicates that an object file should be generated,
- regardless of warnings and errors. */
- if (flag_always_generate_output)
- {
- if (n_warns || n_errs)
- as_warn (_("%d error%s, %d warning%s, generating bad object file"),
- n_errs, n_errs == 1 ? "" : "s",
- n_warns, n_warns == 1 ? "" : "s");
- }
- else
- {
- if (n_errs)
- as_fatal (_("%d error%s, %d warning%s, no object file generated"),
- n_errs, n_errs == 1 ? "" : "s",
- n_warns, n_warns == 1 ? "" : "s");
- }
- }
+ subsegs_finish ();
+
+#ifdef md_pre_output_hook
+ md_pre_output_hook;
+#endif
-#ifdef OBJ_VMS
- /* Under VMS we try to be compatible with VAX-11 "C". Thus, we call
- a routine to check for the definition of the procedure "_main",
- and if so -- fix it up so that it can be program entry point. */
- vms_check_for_main ();
-#endif /* OBJ_VMS */
+#ifdef md_pre_relax_hook
+ md_pre_relax_hook;
+#endif
/* From now on, we don't care about sub-segments. Build one frag chain
for each segment. Linked thru fr_next. */
md_post_relax_hook;
#endif
+#ifdef OBJ_ELF
+ if (IS_ELF)
+ create_obj_attrs_section ();
+#endif
+
#ifndef WORKING_DOT_WORD
{
struct broken_word *lie;
#ifdef TC_CONS_FIX_NEW
TC_CONS_FIX_NEW (lie->frag,
lie->word_goes_here - lie->frag->fr_literal,
- 2, &exp);
+ 2, &exp, TC_PARSE_CONS_RETURN_NONE);
#else
fix_new_exp (lie->frag,
lie->word_goes_here - lie->frag->fr_literal,
if (stretch < 0
|| sym_frag->region == fragP->region)
target += stretch;
+ /* If we get here we know we have a forward branch. This
+ relax pass may have stretched previous instructions so
+ far that omitting STRETCH would make the branch
+ negative. Don't allow this in case the negative reach is
+ large enough to require a larger branch instruction. */
+ else if (target < address)
+ target = fragP->fr_next->fr_address + stretch;
}
}
relax_addressT mask;
relax_addressT new_address;
- mask = ~((~0) << alignment);
+ mask = ~((relax_addressT) ~0 << alignment);
new_address = (address + mask) & (~mask);
#ifdef LINKER_RELAXING_SHRINKS_ONLY
if (linkrelax)
fragP->fr_literal + fragP->fr_fix,
fragP->fr_var);
newf->fr_type = rs_fill;
+ newf->fr_address = address + fragP->fr_fix + newoff;
newf->fr_fix = 0;
newf->fr_offset = (((offsetT) 1 << fragP->fr_offset)
/ fragP->fr_var);
newf->fr_offset = (offsetT) 1 << fragP->fr_offset;
newf->fr_var = 1;
}
- /* Include growth of new frag, because rs_fill
- frags don't normally grow. */
+ /* Include size of new frag in GROWTH. */
growth += newf->fr_offset * newf->fr_var;
- /* The new frag address is newoff. Adjust this
- for the amount we'll add when we process the
- new frag. */
- newf->fr_address = newoff - stretch - growth;
+ /* Adjust the new frag address for the amount
+ we'll add when we process the new frag. */
+ newf->fr_address -= stretch + growth;
newf->relax_marker ^= 1;
fragP->fr_next = newf;
#ifdef DEBUG