* compressed_output.cc (zlib_decompress): New function.
authorCary Coutant <ccoutant@google.com>
Mon, 12 Jul 2010 17:59:58 +0000 (17:59 +0000)
committerCary Coutant <ccoutant@google.com>
Mon, 12 Jul 2010 17:59:58 +0000 (17:59 +0000)
(get_uncompressed_size): New function.
(decompress_input_section): New function.
* compressed_output.h (get_uncompressed_size): New function.
(decompress_input_section): New function.
* dwarf_reader.cc (Sized_dwarf_line_info::Sized_dwarf_line_info)
Handle compressed debug sections.
* layout.cc (is_compressed_debug_section): New function.
(Layout::output_section_name): Map compressed section names to
canonical names.
* layout.h (is_compressed_debug_section): New function.
(is_debug_info_section): Recognize compressed debug sections.
* merge.cc: Include compressed_output.h.
(Output_merge_data::do_add_input_section): Handle compressed
debug sections.
(Output_merge_string::do_add_input_section): Handle compressed
debug sections.
* object.cc: Include compressed_output.h.
(Sized_relobj::Sized_relobj): Initialize new data members.
(build_compressed_section_map): New function.
(Sized_relobj::do_read_symbols): Handle compressed debug sections.
* object.h (Object::section_is_compressed): New method.
(Object::do_section_is_compressed): New method.
(Sized_relobj::Compressed_section_map): New type.
(Sized_relobj::do_section_is_compressed): New method.
(Sized_relobj::compressed_sections_): New data member.
* output.cc (Output_section::add_input_section): Handle compressed
debug sections.
* reloc.cc: Include compressed_output.h.
(Sized_relobj::write_sections): Handle compressed debug sections.

gold/ChangeLog
gold/compressed_output.cc
gold/compressed_output.h
gold/dwarf_reader.cc
gold/layout.cc
gold/layout.h
gold/merge.cc
gold/object.cc
gold/object.h
gold/output.cc
gold/reloc.cc

index 69e420f97e0de698b823551498ad5ed8e349af5e..14c5386dc05d8691fb4ca47175363e02239b8927 100644 (file)
@@ -1,3 +1,36 @@
+2010-07-12  Cary Coutant  <ccoutant@google.com>
+
+       * compressed_output.cc (zlib_decompress): New function.
+       (get_uncompressed_size): New function.
+       (decompress_input_section): New function.
+       * compressed_output.h (get_uncompressed_size): New function.
+       (decompress_input_section): New function.
+       * dwarf_reader.cc (Sized_dwarf_line_info::Sized_dwarf_line_info)
+       Handle compressed debug sections.
+       * layout.cc (is_compressed_debug_section): New function.
+       (Layout::output_section_name): Map compressed section names to
+       canonical names.
+       * layout.h (is_compressed_debug_section): New function.
+       (is_debug_info_section): Recognize compressed debug sections.
+       * merge.cc: Include compressed_output.h.
+       (Output_merge_data::do_add_input_section): Handle compressed
+       debug sections.
+       (Output_merge_string::do_add_input_section): Handle compressed
+       debug sections.
+       * object.cc: Include compressed_output.h.
+       (Sized_relobj::Sized_relobj): Initialize new data members.
+       (build_compressed_section_map): New function.
+       (Sized_relobj::do_read_symbols): Handle compressed debug sections.
+       * object.h (Object::section_is_compressed): New method.
+       (Object::do_section_is_compressed): New method.
+       (Sized_relobj::Compressed_section_map): New type.
+       (Sized_relobj::do_section_is_compressed): New method.
+       (Sized_relobj::compressed_sections_): New data member.
+       * output.cc (Output_section::add_input_section): Handle compressed
+       debug sections.
+       * reloc.cc: Include compressed_output.h.
+       (Sized_relobj::write_sections): Handle compressed debug sections.
+
 2010-07-08  Cary Coutant  <ccoutant@google.com>
 
        * resolve.cc (Symbol_table::resolve): Remember whether undef was
index a0f8ed181ace455f9db37188f9fa3a1822561ed4..16d91295e2beb1b75a7f5b387893f30958124d27 100644 (file)
@@ -1,6 +1,6 @@
-// compressed_output.cc -- manage compressed output sections for gold
+// compressed_output.cc -- manage compressed debug sections for gold
 
-// Copyright 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2007, 2008, 2010 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -33,6 +33,8 @@
 namespace gold
 {
 
+#ifdef HAVE_ZLIB_H
+
 // Compress UNCOMPRESSED_DATA of size UNCOMPRESSED_SIZE.  Returns true
 // if it successfully compressed, false if it failed for any reason
 // (including not having zlib support in the library).  If it returns
@@ -42,8 +44,6 @@ namespace gold
 // "ZLIB", and 8 bytes indicating the uncompressed size, in big-endian
 // order.
 
-#ifdef HAVE_ZLIB_H
-
 static bool
 zlib_compress(const unsigned char* uncompressed_data,
               unsigned long uncompressed_size,
@@ -81,6 +81,49 @@ zlib_compress(const unsigned char* uncompressed_data,
     }
 }
 
+// Decompress COMPRESSED_DATA of size COMPRESSED_SIZE, into a buffer
+// UNCOMPRESSED_DATA of size UNCOMPRESSED_SIZE.  Returns TRUE if it
+// decompressed successfully, false if it failed.  The buffer, of
+// appropriate size, is provided by the caller, and is typically part
+// of the memory-mapped output file.
+
+static bool
+zlib_decompress(const unsigned char* compressed_data,
+               unsigned long compressed_size,
+               unsigned char* uncompressed_data,
+               unsigned long uncompressed_size)
+{
+  z_stream strm;
+  int rc;
+
+  /* It is possible the section consists of several compressed
+     buffers concatenated together, so we uncompress in a loop.  */
+  strm.zalloc = NULL;
+  strm.zfree = NULL;
+  strm.opaque = NULL;
+  strm.avail_in = compressed_size;
+  strm.next_in = const_cast<Bytef*>(compressed_data);
+  strm.avail_out = uncompressed_size;
+
+  rc = inflateInit(&strm);
+  while (strm.avail_in > 0)
+    {
+      if (rc != Z_OK)
+        return false;
+      strm.next_out = ((Bytef*) uncompressed_data
+                       + (uncompressed_size - strm.avail_out));
+      rc = inflate(&strm, Z_FINISH);
+      if (rc != Z_STREAM_END)
+        return false;
+      rc = inflateReset(&strm);
+    }
+  rc = inflateEnd(&strm);
+  if (rc != Z_OK || strm.avail_out != 0)
+    return false;
+
+  return true;
+}
+
 #else // !defined(HAVE_ZLIB_H)
 
 static bool
@@ -90,8 +133,62 @@ zlib_compress(const unsigned char*, unsigned long,
   return false;
 }
 
+static bool
+zlib_decompress(const unsigned char*, unsigned long,
+               unsigned char**, unsigned long*)
+{
+  return false;
+}
+
 #endif // !defined(HAVE_ZLIB_H)
 
+// Read the compression header of a compressed debug section and return
+// the uncompressed size.
+
+uint64_t
+get_uncompressed_size(const unsigned char* compressed_data,
+                     section_size_type compressed_size)
+{
+  const unsigned int zlib_header_size = 12;
+
+  /* Verify the compression header.  Currently, we support only zlib
+     compression, so it should be "ZLIB" followed by the uncompressed
+     section size, 8 bytes in big-endian order.  */
+  if (compressed_size >= zlib_header_size
+      && strncmp(reinterpret_cast<const char*>(compressed_data),
+                "ZLIB", 4) == 0)
+    return elfcpp::Swap_unaligned<64, true>::readval(compressed_data + 4);
+  return -1ULL;
+}
+
+// Decompress a compressed debug section directly into the output file.
+
+bool
+decompress_input_section(const unsigned char* compressed_data,
+                        unsigned long compressed_size,
+                        unsigned char* uncompressed_data,
+                        unsigned long uncompressed_size)
+{
+  const unsigned int zlib_header_size = 12;
+
+  /* Verify the compression header.  Currently, we support only zlib
+     compression, so it should be "ZLIB" followed by the uncompressed
+     section size, 8 bytes in big-endian order.  */
+  if (compressed_size >= zlib_header_size
+      && strncmp(reinterpret_cast<const char*>(compressed_data),
+                "ZLIB", 4) == 0)
+    {
+      unsigned long uncompressed_size_check =
+         elfcpp::Swap_unaligned<64, true>::readval(compressed_data + 4);
+      gold_assert(uncompressed_size_check == uncompressed_size);
+      return zlib_decompress(compressed_data + zlib_header_size,
+                            compressed_size - zlib_header_size,
+                            uncompressed_data,
+                            uncompressed_size);
+    }
+  return false;
+}
+
 // Class Output_compressed_section.
 
 // Set the final data size of a compressed section.  This is where
index 11b2762288f58b09b69b6de81ba7686a3657f250..2d6ebd95f46ad358eb9d13a6bfe9d200c2515fa0 100644 (file)
@@ -1,6 +1,6 @@
 // compressed_output.h -- compressed output sections for gold  -*- C++ -*-
 
-// Copyright 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2007, 2008, 2010 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -37,6 +37,18 @@ namespace gold
 
 class General_options;
 
+// Read the compression header of a compressed debug section and return
+// the uncompressed size.
+
+extern uint64_t
+get_uncompressed_size(const unsigned char*, section_size_type);
+
+// Decompress a compressed debug section directly into the output file.
+
+extern bool
+decompress_input_section(const unsigned char*, unsigned long, unsigned char*,
+                        unsigned long);
+
 // This is used for a section whose data should be compressed.  It is
 // a regular Output_section which computes its contents into a buffer
 // and then postprocesses it.
index 4062fe67c3157377b5429f53244b8b3f2ee67923..c0188fbd435b5bac060a2017f581abba00bc2274 100644 (file)
@@ -32,6 +32,7 @@
 #include "reloc.h"
 #include "dwarf_reader.h"
 #include "int_encoding.h"
+#include "compressed_output.h"
 
 namespace gold {
 
@@ -80,6 +81,21 @@ Sized_dwarf_line_info<size, big_endian>::Sized_dwarf_line_info(Object* object,
   if (this->buffer_ == NULL)
     return;
 
+  section_size_type uncompressed_size = 0;
+  unsigned char* uncompressed_data = NULL;
+  if (object->section_is_compressed(debug_shndx, &uncompressed_size))
+    {
+      uncompressed_data = new unsigned char[uncompressed_size];
+      if (!decompress_input_section(this->buffer_,
+                                   this->buffer_end_ - this->buffer_,
+                                   uncompressed_data,
+                                   uncompressed_size))
+       object->error(_("could not decompress section %s"),
+                     object->section_name(debug_shndx).c_str());
+      this->buffer_ = uncompressed_data;
+      this->buffer_end_ = this->buffer_ + uncompressed_size;
+    }
+
   // Find the relocation section for ".debug_line".
   // We expect these for relobjs (.o's) but not dynobjs (.so's).
   bool got_relocs = false;
index 9310961e8e95e6fe5423559a9caa5f317bcfb019..b22a3bd7d6e680d92a2d9d57d58fa419401ac2dc 100644 (file)
@@ -944,7 +944,16 @@ Layout::section_flags_to_segment(elfcpp::Elf_Xword flags)
 static bool
 is_compressible_debug_section(const char* secname)
 {
-  return (strncmp(secname, ".debug", sizeof(".debug") - 1) == 0);
+  return (is_prefix_of(".debug", secname));
+}
+
+// We may see compressed debug sections in input files.  Return TRUE
+// if this is the name of a compressed debug section.
+
+bool
+is_compressed_debug_section(const char* secname)
+{
+  return (is_prefix_of(".zdebug", secname));
 }
 
 // Make a new Output_section, and attach it to segments as
@@ -3772,6 +3781,20 @@ Layout::output_section_name(const char* name, size_t* plen)
        }
     }
 
+  // Compressed debug sections should be mapped to the corresponding
+  // uncompressed section.
+  if (is_compressed_debug_section(name))
+    {
+      size_t len = strlen(name);
+      char *uncompressed_name = new char[len];
+      uncompressed_name[0] = '.';
+      gold_assert(name[0] == '.' && name[1] == 'z');
+      strncpy(&uncompressed_name[1], &name[2], len - 2);
+      uncompressed_name[len - 1] = '\0';
+      *plen = len - 1;
+      return uncompressed_name;
+    }
+
   return name;
 }
 
index 55ad7a6a3a57e11f8019bd051d4d3754b17e5c87..60ae114a338f407dd9b5cff458387bbc9c962ec6 100644 (file)
@@ -59,6 +59,10 @@ class Output_reduced_debug_info_section;
 class Eh_frame;
 class Target;
 
+// Return TRUE if SECNAME is the name of a compressed debug section.
+extern bool
+is_compressed_debug_section(const char* secname);
+
 // This task function handles mapping the input sections to output
 // sections and laying them out in memory.
 
@@ -451,6 +455,7 @@ class Layout
   {
     // Debugging sections can only be recognized by name.
     return (strncmp(name, ".debug", sizeof(".debug") - 1) == 0
+            || strncmp(name, ".zdebug", sizeof(".zdebug") - 1) == 0
             || strncmp(name, ".gnu.linkonce.wi.",
                        sizeof(".gnu.linkonce.wi.") - 1) == 0
             || strncmp(name, ".line", sizeof(".line") - 1) == 0
index 6e44ddd44711440aa3b385a0c8531ea80c00bb5a..ec11c90baffbabcfcdf555d1626574b007c3eb27 100644 (file)
@@ -1,6 +1,6 @@
 // merge.cc -- handle section merging for gold
 
-// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -26,6 +26,7 @@
 #include <algorithm>
 
 #include "merge.h"
+#include "compressed_output.h"
 
 namespace gold
 {
@@ -404,12 +405,29 @@ bool
 Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx)
 {
   section_size_type len;
+  section_size_type uncompressed_size = 0;
+  unsigned char* uncompressed_data = NULL;
   const unsigned char* p = object->section_contents(shndx, &len, false);
 
+  if (object->section_is_compressed(shndx, &uncompressed_size))
+    {
+      uncompressed_data = new unsigned char[uncompressed_size];
+      if (!decompress_input_section(p, len, uncompressed_data,
+                                   uncompressed_size))
+       object->error(_("could not decompress section %s"),
+                     object->section_name(shndx).c_str());
+      p = uncompressed_data;
+      len = uncompressed_size;
+    }
+
   section_size_type entsize = convert_to_section_size_type(this->entsize());
 
   if (len % entsize != 0)
-    return false;
+    {
+      if (uncompressed_data != NULL)
+       delete[] uncompressed_data;
+      return false;
+    }
 
   this->input_count_ += len / entsize;
 
@@ -438,6 +456,9 @@ Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx)
   if (this->keeps_input_sections())
     record_input_section(object, shndx);
 
+  if (uncompressed_data != NULL)
+    delete[] uncompressed_data;
+
   return true;
 }
 
@@ -495,8 +516,21 @@ Output_merge_string<Char_type>::do_add_input_section(Relobj* object,
                                                     unsigned int shndx)
 {
   section_size_type len;
+  section_size_type uncompressed_size = 0;
+  unsigned char* uncompressed_data = NULL;
   const unsigned char* pdata = object->section_contents(shndx, &len, false);
 
+  if (object->section_is_compressed(shndx, &uncompressed_size))
+    {
+      uncompressed_data = new unsigned char[uncompressed_size];
+      if (!decompress_input_section(pdata, len, uncompressed_data,
+                                   uncompressed_size))
+       object->error(_("could not decompress section %s"),
+                     object->section_name(shndx).c_str());
+      pdata = uncompressed_data;
+      len = uncompressed_size;
+    }
+
   const Char_type* p = reinterpret_cast<const Char_type*>(pdata);
   const Char_type* pend = p + len / sizeof(Char_type);
 
@@ -504,6 +538,8 @@ Output_merge_string<Char_type>::do_add_input_section(Relobj* object,
     {
       object->error(_("mergeable string section length not multiple of "
                      "character size"));
+      if (uncompressed_data != NULL)
+       delete[] uncompressed_data;
       return false;
     }
 
@@ -545,6 +581,9 @@ Output_merge_string<Char_type>::do_add_input_section(Relobj* object,
   if (this->keeps_input_sections())
     record_input_section(object, shndx);
 
+  if (uncompressed_data != NULL)
+    delete[] uncompressed_data;
+
   return true;
 }
 
index b034ee209acc311f13516cbaec15c1cc6a538b3f..ed87b1aef143911e23e63d9af73f5c1b2b750c93 100644 (file)
@@ -39,6 +39,7 @@
 #include "object.h"
 #include "dynobj.h"
 #include "plugin.h"
+#include "compressed_output.h"
 
 namespace gold
 {
@@ -367,7 +368,10 @@ Sized_relobj<size, big_endian>::Sized_relobj(
     local_got_offsets_(),
     kept_comdat_sections_(),
     has_eh_frame_(false),
-    discarded_eh_frame_shndx_(-1U)
+    discarded_eh_frame_shndx_(-1U),
+    deferred_layout_(),
+    deferred_layout_relocs_(),
+    compressed_sections_()
 {
 }
 
@@ -495,6 +499,50 @@ Sized_relobj<size, big_endian>::find_eh_frame(
   return false;
 }
 
+// Build a table for any compressed debug sections, mapping each section index
+// to the uncompressed size.
+
+template<int size, bool big_endian>
+Compressed_section_map*
+build_compressed_section_map(
+    const unsigned char* pshdrs,
+    unsigned int shnum,
+    const char* names,
+    section_size_type names_size,
+    Sized_relobj<size, big_endian>* obj)
+{
+  Compressed_section_map* uncompressed_sizes = new Compressed_section_map();
+  const unsigned int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+  const unsigned char* p = pshdrs + shdr_size;
+  for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
+    {
+      typename elfcpp::Shdr<size, big_endian> shdr(p);
+      if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
+         && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
+       {
+         if (shdr.get_sh_name() >= names_size)
+           {
+             obj->error(_("bad section name offset for section %u: %lu"),
+                        i, static_cast<unsigned long>(shdr.get_sh_name()));
+             continue;
+           }
+
+         const char* name = names + shdr.get_sh_name();
+         if (is_compressed_debug_section(name))
+           {
+             section_size_type len;
+             const unsigned char* contents =
+                 obj->section_contents(i, &len, false);
+             uint64_t uncompressed_size = get_uncompressed_size(contents, len);
+             if (uncompressed_size != -1ULL)
+               (*uncompressed_sizes)[i] =
+                   convert_to_section_size_type(uncompressed_size);
+           }
+       }
+    }
+  return uncompressed_sizes;
+}
+
 // Read the sections and symbols from an object file.
 
 template<int size, bool big_endian>
@@ -514,6 +562,10 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
       if (this->find_eh_frame(pshdrs, names, sd->section_names_size))
         this->has_eh_frame_ = true;
     }
+  if (memmem(names, sd->section_names_size, ".zdebug_", 8) != NULL)
+    this->compressed_sections_ =
+        build_compressed_section_map(pshdrs, this->shnum(), names,
+                                    sd->section_names_size, this);
 
   sd->symbols = NULL;
   sd->symbols_size = 0;
index bc2b558f57fbb68af9dfe7c6e19894913e4b01c6..f60d90972e78d85474e1b02da6dd92fc02506358 100644 (file)
@@ -518,6 +518,13 @@ class Object
   set_no_export(bool value)
   { this->no_export_ = value; }
 
+  // Return TRUE if the section is a compressed debug section, and set
+  // *UNCOMPRESSED_SIZE to the size of the uncompressed data.
+  bool
+  section_is_compressed(unsigned int shndx,
+                       section_size_type* uncompressed_size) const
+  { return this->do_section_is_compressed(shndx, uncompressed_size); }
+
  protected:
   // Returns NULL for Objects that are not plugin objects.  This method
   // is overridden in the Pluginobj class.
@@ -628,6 +635,12 @@ class Object
   bool
   handle_split_stack_section(const char* name);
 
+  // Return TRUE if the section is a compressed debug section, and set
+  // *UNCOMPRESSED_SIZE to the size of the uncompressed data.
+  virtual bool
+  do_section_is_compressed(unsigned int, section_size_type*) const
+  { return false; }
+
  private:
   // This class may not be copied.
   Object(const Object&);
@@ -1406,6 +1419,10 @@ class Reloc_symbol_changes
   std::vector<Symbol*> vec_;
 };
 
+// Type for mapping section index to uncompressed size.
+
+typedef std::map<unsigned int, section_size_type> Compressed_section_map;
+
 // A regular object file.  This is size and endian specific.
 
 template<int size, bool big_endian>
@@ -1781,7 +1798,26 @@ class Sized_relobj : public Relobj
   void
   set_output_local_symbol_count(unsigned int value)
   { this->output_local_symbol_count_ = value; }
-   
+
+  // Return TRUE if the section is a compressed debug section, and set
+  // *UNCOMPRESSED_SIZE to the size of the uncompressed data.
+  bool
+  do_section_is_compressed(unsigned int shndx,
+                          section_size_type* uncompressed_size) const
+  {
+    if (this->compressed_sections_ == NULL)
+      return false;
+    Compressed_section_map::const_iterator p =
+        this->compressed_sections_->find(shndx);
+    if (p != this->compressed_sections_->end())
+      {
+       if (uncompressed_size != NULL)
+         *uncompressed_size = p->second;
+       return true;
+      }
+    return false;
+  }
+
  private:
   // For convenience.
   typedef Sized_relobj<size, big_endian> This;
@@ -2024,6 +2060,8 @@ class Sized_relobj : public Relobj
   std::vector<Deferred_layout> deferred_layout_;
   // The list of relocation sections whose layout was deferred.
   std::vector<Deferred_layout> deferred_layout_relocs_;
+  // For compressed debug sections, map section index to uncompressed size.
+  Compressed_section_map* compressed_sections_;
 };
 
 // A class to manage the list of all objects.
index 329a4beda7699397433793a640305fd0a5609123..3ac8a3ddcc6e44ff0be8d76374001b8c3181317b 100644 (file)
@@ -2086,8 +2086,13 @@ Output_section::add_input_section(Layout* layout,
         }
     }
 
+  section_size_type input_section_size = shdr.get_sh_size();
+  section_size_type uncompressed_size;
+  if (object->section_is_compressed(shndx, &uncompressed_size))
+    input_section_size = uncompressed_size;
+
   this->set_current_data_size_for_child(aligned_offset_in_section
-                                       + shdr.get_sh_size());
+                                       + input_section_size);
 
   // We need to keep track of this section if we are already keeping
   // track of sections, or if we are relaxing.  Also, if this is a
index 8879f0a5042703d72256ee82eb792e1d134e520d..9f7355e441cd91196fadf24980330b6fe00e9335 100644 (file)
@@ -32,6 +32,7 @@
 #include "target-reloc.h"
 #include "reloc.h"
 #include "icf.h"
+#include "compressed_output.h"
 
 namespace gold
 {
@@ -732,10 +733,17 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
 
       off_t view_start;
       section_size_type view_size;
+      bool must_decompress = false;
       if (output_offset != invalid_address)
        {
          view_start = output_section_offset + output_offset;
          view_size = convert_to_section_size_type(shdr.get_sh_size());
+         section_size_type uncompressed_size;
+         if (this->section_is_compressed(i, &uncompressed_size))
+           {
+             view_size = uncompressed_size;
+             must_decompress = true;
+           }
        }
       else
        {
@@ -754,7 +762,7 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
        {
          unsigned char* buffer = os->postprocessing_buffer();
          view = buffer + view_start;
-         if (output_offset != invalid_address)
+         if (output_offset != invalid_address && !must_decompress)
            {
              off_t sh_offset = shdr.get_sh_offset();
              if (!rm.empty() && rm.back().file_offset > sh_offset)
@@ -770,14 +778,27 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
          else
            {
              view = of->get_output_view(view_start, view_size);
-             off_t sh_offset = shdr.get_sh_offset();
-             if (!rm.empty() && rm.back().file_offset > sh_offset)
-               is_sorted = false;
-             rm.push_back(File_read::Read_multiple_entry(sh_offset,
-                                                         view_size, view));
+             if (!must_decompress)
+               {
+                 off_t sh_offset = shdr.get_sh_offset();
+                 if (!rm.empty() && rm.back().file_offset > sh_offset)
+                   is_sorted = false;
+                 rm.push_back(File_read::Read_multiple_entry(sh_offset,
+                                                             view_size, view));
+               }
            }
        }
 
+      if (must_decompress)
+        {
+         // Read and decompress the section.
+          section_size_type len;
+         const unsigned char* p = this->section_contents(i, &len, false);
+         if (!decompress_input_section(p, len, view, view_size))
+           this->error(_("could not decompress section %s"),
+                       this->section_name(i).c_str());
+        }
+
       pvs->view = view;
       pvs->address = os->address();
       if (output_offset != invalid_address)