diff options
Diffstat (limited to 'gas/write.c')
-rw-r--r-- | gas/write.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/gas/write.c b/gas/write.c index a148b24..71ac635 100644 --- a/gas/write.c +++ b/gas/write.c @@ -28,6 +28,7 @@ #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) @@ -1288,6 +1289,194 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) #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, @@ -1912,6 +2101,13 @@ write_object_file (void) 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); } |