/* 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
- Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "output-file.h"
#include "dwarf2dbg.h"
#include "libbfd.h"
+#include "compress-debug.h"
#ifndef TC_ADJUST_RELOC_COUNT
#define TC_ADJUST_RELOC_COUNT(FIX, COUNT)
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 ATTRIBUTE_UNUSED /* Relocation type. */,
+ int at_beginning) /* Add to the start of the list? */
{
fixS *fixP;
as_where (&fixP->fx_file, &fixP->fx_line);
- /* Usually, we want relocs sorted numerically, but while
- comparing to older versions of gas that have relocs
- reverse sorted, it is convenient to have this compile
- time option. xoxorich. */
{
fixS **seg_fix_rootP = (frags_chained
? &seg_info (now_seg)->fix_tail
: &frchain_now->fix_tail);
-#ifdef REVERSE_SORT_RELOCS
-
- fixP->fx_next = *seg_fix_rootP;
- *seg_fix_rootP = fixP;
-
-#else /* REVERSE_SORT_RELOCS */
-
- fixP->fx_next = NULL;
-
- if (*seg_fix_tailP)
- (*seg_fix_tailP)->fx_next = fixP;
+ if (at_beginning)
+ {
+ fixP->fx_next = *seg_fix_rootP;
+ *seg_fix_rootP = fixP;
+ if (fixP->fx_next == NULL)
+ *seg_fix_tailP = fixP;
+ }
else
- *seg_fix_rootP = fixP;
- *seg_fix_tailP = fixP;
-
-#endif /* REVERSE_SORT_RELOCS */
+ {
+ fixP->fx_next = NULL;
+ if (*seg_fix_tailP)
+ (*seg_fix_tailP)->fx_next = fixP;
+ else
+ *seg_fix_rootP = fixP;
+ *seg_fix_tailP = fixP;
+ }
}
return fixP;
RELOC_ENUM r_type /* Relocation type. */)
{
return fix_new_internal (frag, where, size, add_symbol,
- (symbolS *) NULL, offset, pcrel, r_type);
+ (symbolS *) NULL, offset, pcrel, r_type, FALSE);
}
/* Create a fixup for an expression. Currently we only support fixups
break;
}
- return fix_new_internal (frag, where, size, add, sub, off, pcrel, r_type);
+ return fix_new_internal (frag, where, size, add, sub, off, pcrel,
+ r_type, FALSE);
+}
+
+/* Create a fixup at the beginning of FRAG. The arguments are the same
+ as for fix_new, except that WHERE is implicitly 0. */
+
+fixS *
+fix_at_start (fragS *frag, int size, symbolS *add_symbol,
+ offsetT offset, int pcrel, RELOC_ENUM r_type)
+{
+ return fix_new_internal (frag, 0, size, add_symbol,
+ (symbolS *) NULL, offset, pcrel, r_type, TRUE);
}
/* Generic function to determine whether a fixup requires a relocation. */
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);
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);
#endif
}
+static int
+compress_frag (struct z_stream_s *strm, const char *contents, int in_size,
+ fragS **last_newf, struct obstack *ob)
+{
+ int out_size;
+ int total_out_size = 0;
+ fragS *f = *last_newf;
+ char *next_out;
+ int avail_out;
+
+ /* Call the compression routine repeatedly until it has finished
+ processing the frag. */
+ while (in_size > 0)
+ {
+ /* Reserve all the space available in the current chunk.
+ If none is available, start a new frag. */
+ avail_out = obstack_room (ob);
+ if (avail_out <= 0)
+ {
+ obstack_finish (ob);
+ f = frag_alloc (ob);
+ f->fr_type = rs_fill;
+ (*last_newf)->fr_next = f;
+ *last_newf = f;
+ avail_out = obstack_room (ob);
+ }
+ if (avail_out <= 0)
+ as_fatal (_("can't extend frag"));
+ next_out = obstack_next_free (ob);
+ obstack_blank_fast (ob, avail_out);
+ out_size = compress_data (strm, &contents, &in_size,
+ &next_out, &avail_out);
+ if (out_size < 0)
+ return -1;
+
+ f->fr_fix += out_size;
+ total_out_size += out_size;
+
+ /* Return unused space. */
+ if (avail_out > 0)
+ obstack_blank_fast (ob, -avail_out);
+ }
+
+ return total_out_size;
+}
+
+static void
+compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *f;
+ fragS *first_newf;
+ fragS *last_newf;
+ struct obstack *ob = &seginfo->frchainP->frch_obstack;
+ bfd_size_type uncompressed_size = (bfd_size_type) sec->size;
+ bfd_size_type compressed_size;
+ const char *section_name;
+ char *compressed_name;
+ char *header;
+ struct z_stream_s *strm;
+ int x;
+ flagword flags = bfd_get_section_flags (abfd, sec);
+
+ if (seginfo == NULL
+ || (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC)
+ return;
+
+ section_name = bfd_get_section_name (stdoutput, sec);
+ if (strncmp (section_name, ".debug_", 7) != 0)
+ return;
+
+ strm = compress_init ();
+ if (strm == NULL)
+ return;
+
+ /* Create a new frag to contain the "ZLIB" header. */
+ first_newf = frag_alloc (ob);
+ if (obstack_room (ob) < 12)
+ first_newf = frag_alloc (ob);
+ if (obstack_room (ob) < 12)
+ as_fatal (_("can't extend frag %u chars"), 12);
+ last_newf = first_newf;
+ obstack_blank_fast (ob, 12);
+ last_newf->fr_type = rs_fill;
+ last_newf->fr_fix = 12;
+ 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;
+
+ /* Stream the frags through the compression engine, adding new frags
+ as necessary to accomodate the compressed output. */
+ for (f = seginfo->frchainP->frch_root;
+ f;
+ f = f->fr_next)
+ {
+ offsetT fill_size;
+ char *fill_literal;
+ offsetT count;
+ int out_size;
+
+ gas_assert (f->fr_type == rs_fill);
+ if (f->fr_fix)
+ {
+ out_size = compress_frag (strm, f->fr_literal, f->fr_fix,
+ &last_newf, ob);
+ if (out_size < 0)
+ return;
+ compressed_size += out_size;
+ }
+ fill_literal = f->fr_literal + f->fr_fix;
+ fill_size = f->fr_var;
+ count = f->fr_offset;
+ gas_assert (count >= 0);
+ if (fill_size && count)
+ {
+ while (count--)
+ {
+ out_size = compress_frag (strm, fill_literal, (int) fill_size,
+ &last_newf, ob);
+ if (out_size < 0)
+ return;
+ compressed_size += out_size;
+ }
+ }
+ }
+
+ /* Flush the compression state. */
+ for (;;)
+ {
+ int avail_out;
+ char *next_out;
+ int out_size;
+
+ /* Reserve all the space available in the current chunk.
+ If none is available, start a new frag. */
+ avail_out = obstack_room (ob);
+ if (avail_out <= 0)
+ {
+ fragS *newf;
+
+ obstack_finish (ob);
+ newf = frag_alloc (ob);
+ newf->fr_type = rs_fill;
+ last_newf->fr_next = newf;
+ last_newf = newf;
+ avail_out = obstack_room (ob);
+ }
+ if (avail_out <= 0)
+ as_fatal (_("can't extend frag"));
+ next_out = obstack_next_free (ob);
+ obstack_blank_fast (ob, avail_out);
+ x = compress_finish (strm, &next_out, &avail_out, &out_size);
+ if (x < 0)
+ return;
+
+ last_newf->fr_fix += out_size;
+ compressed_size += out_size;
+
+ /* Return unused space. */
+ if (avail_out > 0)
+ obstack_blank_fast (ob, -avail_out);
+
+ if (x == 0)
+ break;
+ }
+
+ /* 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. */
+ 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;
+}
+
static void
write_contents (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
obj_frob_file_after_relocs ();
#endif
+ /* Once all relocations have been written, we can compress the
+ contents of the debug sections. This needs to be done before
+ we start writing any sections, because it will affect the file
+ layout, which is fixed once we start writing contents. */
+ if (flag_compress_debug)
+ bfd_map_over_sections (stdoutput, compress_debug, (char *) 0);
+
bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
}
|| sym_frag == &zero_address_frag);
target += S_GET_VALUE (symbolP);
- /* If frag has yet to be reached on this pass,
- assume it will move by STRETCH just as we did.
- If this is not so, it will be because some frag
- between grows, and that will force another pass. */
+ /* If SYM_FRAG has yet to be reached on this pass, assume it
+ will move by STRETCH just as we did, unless there is an
+ alignment frag between here and SYM_FRAG. An alignment may
+ well absorb any STRETCH, and we don't want to choose a larger
+ branch insn by overestimating the needed reach of this
+ branch. It isn't critical to calculate TARGET exactly; We
+ know we'll be doing another pass if STRETCH is non-zero. */
if (stretch != 0
&& sym_frag->relax_marker != fragP->relax_marker
&& S_GET_SEGMENT (symbolP) == segment)
{
- target += stretch;
+ 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;
}
}
unsigned long frag_count;
struct frag *fragP;
relax_addressT address;
+ int region;
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;
+ region = 0;
for (frag_count = 0, fragP = segment_frag_root;
fragP;
fragP = fragP->fr_next, frag_count ++)
{
+ fragP->region = region;
fragP->relax_marker = 0;
fragP->fr_address = address;
address += fragP->fr_fix;
}
address += offset;
+ region += 1;
}
break;
case rs_org:
- case rs_space:
/* Assume .org is nugatory. It will grow with 1st relax. */
+ region += 1;
+ break;
+
+ case rs_space:
break;
case rs_machine_dependent:
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