/* 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-2015 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)
{
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 (!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;
+
+ 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
{
}
#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);
struct z_stream_s *strm;
int x;
flagword flags = bfd_get_section_flags (abfd, sec);
+ unsigned int header_size, compression_header_size;
if (seginfo == NULL
|| sec->size < 32
if (strm == NULL)
return;
- /* Create a new frag to contain the "ZLIB" header. */
+ if (flag_compress_debug == COMPRESS_DEBUG_GABI_ZLIB)
+ {
+ stdoutput->flags |= BFD_COMPRESS | BFD_COMPRESS_GABI;
+ compression_header_size
+ = bfd_get_compression_header_size (stdoutput, NULL);
+ header_size = compression_header_size;
+ }
+ else
+ {
+ stdoutput->flags |= BFD_COMPRESS;
+ compression_header_size = 0;
+ header_size = 12;
+ }
+
+ /* Create a new frag to contain the compression header. */
first_newf = frag_alloc (ob);
- if (obstack_room (ob) < 12)
+ if (obstack_room (ob) < header_size)
first_newf = frag_alloc (ob);
- if (obstack_room (ob) < 12)
- as_fatal (_("can't extend frag %u chars"), 12);
+ if (obstack_room (ob) < header_size)
+ as_fatal (_("can't extend frag %u chars"), header_size);
last_newf = first_newf;
- obstack_blank_fast (ob, 12);
+ obstack_blank_fast (ob, header_size);
last_newf->fr_type = rs_fill;
- last_newf->fr_fix = 12;
+ last_newf->fr_fix = header_size;
header = last_newf->fr_literal;
- memcpy (header, "ZLIB", 4);
- header[11] = uncompressed_size; uncompressed_size >>= 8;
- header[10] = uncompressed_size; uncompressed_size >>= 8;
- header[9] = uncompressed_size; uncompressed_size >>= 8;
- header[8] = uncompressed_size; uncompressed_size >>= 8;
- header[7] = uncompressed_size; uncompressed_size >>= 8;
- header[6] = uncompressed_size; uncompressed_size >>= 8;
- header[5] = uncompressed_size; uncompressed_size >>= 8;
- header[4] = uncompressed_size;
- compressed_size = 12;
+ compressed_size = header_size;
/* Stream the frags through the compression engine, adding new frags
as necessary to accomodate the compressed output. */
break;
}
+ /* PR binutils/18087: If compression didn't make the section smaller,
+ just keep it uncompressed. */
+ if (compressed_size >= uncompressed_size)
+ return;
+
/* Replace the uncompressed frag list with the compressed frag list. */
seginfo->frchainP->frch_root = first_newf;
seginfo->frchainP->frch_last = last_newf;
/* Update the section size and its name. */
+ bfd_update_compression_header (abfd, (bfd_byte *) header, sec);
x = bfd_set_section_size (abfd, sec, compressed_size);
gas_assert (x);
- compressed_name = (char *) xmalloc (strlen (section_name) + 2);
- compressed_name[0] = '.';
- compressed_name[1] = 'z';
- strcpy (compressed_name + 2, section_name + 1);
- bfd_section_name (stdoutput, sec) = compressed_name;
+ if (!compression_header_size)
+ {
+ compressed_name = (char *) xmalloc (strlen (section_name) + 2);
+ compressed_name[0] = '.';
+ compressed_name[1] = 'z';
+ strcpy (compressed_name + 2, section_name + 1);
+ bfd_section_name (stdoutput, sec) = compressed_name;
+ }
}
static void
f->fr_literal, (file_ptr) offset,
(bfd_size_type) f->fr_fix);
if (!x)
- as_fatal (_("can't write %s: %s"), stdoutput->filename,
+ as_fatal (_("can't write %ld bytes to section %s of %s because: '%s'"),
+ (long) f->fr_fix, sec->name,
+ stdoutput->filename,
bfd_errmsg (bfd_get_error ()));
offset += f->fr_fix;
}
(file_ptr) offset,
(bfd_size_type) fill_size);
if (!x)
- as_fatal (_("can't write %s: %s"), stdoutput->filename,
+ as_fatal (_("can't fill %ld bytes in section %s of %s because '%s'"),
+ (long) fill_size, sec->name,
+ stdoutput->filename,
bfd_errmsg (bfd_get_error ()));
offset += fill_size;
}
(stdoutput, sec, buf, (file_ptr) offset,
(bfd_size_type) n_per_buf * fill_size);
if (!x)
- as_fatal (_("cannot write to output file '%s': %s"),
+ as_fatal (_("cannot fill %ld bytes in section %s of %s because: '%s'"),
+ (long)(n_per_buf * fill_size), sec->name,
stdoutput->filename,
bfd_errmsg (bfd_get_error ()));
offset += n_per_buf * 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 ? get_recorded_alignment (SEG) : 0)
+ (!(FRCHAIN)->frch_next && subseg_text_p (SEG) \
+ ? get_recorded_alignment (SEG) \
+ : 0)
#else
#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
#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;
-
- for (frchainP = seginfo->frchainP;
- frchainP != NULL;
- frchainP = frchainP->frch_next)
- {
- int alignment = 0;
+ 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. */
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 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,
obj_adjust_symtab ();
#endif
- set_symtab ();
-
/* Stop if there is an error. */
if (had_errors ())
return;
/* Now that all the sizes are known, and contents correct, we can
start writing to the file. */
+ set_symtab ();
/* If *_frob_file changes the symbol value at this point, it is
responsible for moving the changed value into symp->bsym->value
/* Relax_align. Advance location counter to next address that has 'alignment'
lowest order bits all 0s, return size of adjustment made. */
static relax_addressT
-relax_align (register relax_addressT address, /* Address now. */
- register int alignment /* Alignment (binary). */)
+relax_align (relax_addressT address, /* Address now. */
+ int alignment /* Alignment (binary). */)
{
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)