#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. */
#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;
+
+ if (seginfo == NULL
+ || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS)
+ || (bfd_get_section_flags (abfd, sec) & 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);
}