]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/write.c
bfd/ChangeLog:
[thirdparty/binutils-gdb.git] / gas / write.c
index a148b248d20bd31de5ea6ab0dc3fdd76dda7f446..71ac63562c9d5e28a346aa124e77114afd9acbda 100644 (file)
@@ -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);
 }