This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Add SHF_COMPRESSED support to gas and objcopy


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=151411f8af16723a12e0e0eedc1ecdbea648c1b0

commit 151411f8af16723a12e0e0eedc1ecdbea648c1b0
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Apr 8 07:53:54 2015 -0700

    Add SHF_COMPRESSED support to gas and objcopy
    
    This patch adds --compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}
    options to gas and objcopy for ELF files. They control how DWARF debug
    sections are compressed.  --compress-debug-sections=none is equivalent to
    --nocompress-debug-sections.  --compress-debug-sections=zlib and
    --compress-debug-sections=zlib-gnu are equivalent to
    --compress-debug-sections.  --compress-debug-sections=zlib-gabi compresses
    DWARF debug sections with SHF_COMPRESSED from the ELF ABI.  No linker
    changes are required to support SHF_COMPRESSED.
    
    bfd/
    
    	* archive.c (_bfd_get_elt_at_filepos): Also copy BFD_COMPRESS_GABI
    	bit.
    	* bfd.c (bfd::flags): Increase size to 18 bits.
    	(BFD_COMPRESS_GABI): New.
    	(BFD_FLAGS_SAVED): Add BFD_COMPRESS_GABI.
    	(BFD_FLAGS_FOR_BFD_USE_MASK): Likewise.
    	(bfd_update_compression_header): New fuction.
    	(bfd_check_compression_header): Likewise.
    	(bfd_get_compression_header_size): Likewise.
    	(bfd_is_section_compressed_with_header): Likewise.
    	* compress.c (MAX_COMPRESSION_HEADER_SIZE): New.
    	(bfd_compress_section_contents): Return the uncompressed size if
    	the full section contents is compressed successfully.  Support
    	converting from/to .zdebug* sections.
    	(bfd_get_full_section_contents): Call
    	bfd_get_compression_header_size to get compression header size.
    	(bfd_is_section_compressed): Renamed to ...
    	(bfd_is_section_compressed_with_header): This.  Add a pointer
    	argument to return compression header size.
    	(bfd_is_section_compressed): Use it.
    	(bfd_init_section_decompress_status): Call
    	bfd_get_compression_header_size to get compression header size.
    	Return FALSE if uncompressed section size is 0.
    	* elf.c (_bfd_elf_make_section_from_shdr): Support converting
    	from/to .zdebug* sections.
    	* bfd-in2.h: Regenerated.
    
    binutils/
    
    	* objcopy.c (do_debug_sections): Add compress_zlib,
    	compress_gnu_zlib and compress_gabi_zlib.
    	(copy_options): Use optional_argument on compress-debug-sections.
    	(copy_usage): Update --compress-debug-sections.
    	(copy_file): Handle compress_zlib, compress_gnu_zlib and
    	compress_gabi_zlib.
    	(copy_main): Handle
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    	* doc/binutils.texi: Document
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    
    binutils/testsuite/
    
    	* compress.exp: Add tests for
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    	* binutils-all/dw2-3.rS: New file.
    	* binutils-all/dw2-3.rt: Likewise.
    	* binutils-all/libdw2-compressedgabi.out: Likewise.
    
    gas/
    
    	* as.c (show_usage): Update --compress-debug-sections.
    	(std_longopts): Use optional_argument on compress-debug-sections.
    	(parse_args): Handle
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    	* as.h (compressed_debug_section_type): New.
    	(flag_compress_debug): Change type to compressed_debug_section_type.
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    	* write.c (compress_debug): Set BFD_COMPRESS_GABI for
    	--compress-debug-sections=zlib-gabi.  Call
    	bfd_get_compression_header_size to get compression header size.
    	Don't rename section name for --compress-debug-sections=zlib-gabi.
    	* config/tc-i386.c (compressed_debug_section_type): Set to
    	COMPRESS_DEBUG_ZLIB.
    	* doc/as.texinfo: Document
    	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
    
    gas/testsuite/
    
    	* gas/i386/dw2-compressed-1.d: New file.
    	* gas/i386/dw2-compressed-2.d: Likewise.
    	* gas/i386/dw2-compressed-3.d: Likewise.
    	* gas/i386/x86-64-dw2-compressed-2.d: Likewise.
    	* gas/i386/i386.exp: Run dw2-compressed-2, dw2-compressed-1,
    	dw2-compressed-3 and x86-64-dw2-compressed-2.
    
    ld/testsuite/
    
    	* ld-elf/compress.exp: Add a test for
    	--compress-debug-sections=zlib-gabi.
    	(build_tests): Add 2 tests for --compress-debug-sections=zlib-gabi.
    	(run_tests): Likewise.
    	Verify linker output with zlib-gabi compressed debug input.
    	* ld-elf/compressed1a.d: New file.
    	* ld-elf/compressed1b.d: Likewise.
    	* ld-elf/compressed1c.d: Likewise.

Diff:
---
 bfd/ChangeLog                                      |  29 ++
 bfd/archive.c                                      |   6 +-
 bfd/bfd-in2.h                                      |  24 +-
 bfd/bfd.c                                          | 153 ++++++++-
 bfd/compress.c                                     | 265 +++++++++++----
 bfd/elf.c                                          |  75 +++--
 binutils/ChangeLog                                 |  13 +
 binutils/doc/binutils.texi                         |  13 +
 binutils/objcopy.c                                 |  36 ++-
 binutils/testsuite/ChangeLog                       |   8 +
 binutils/testsuite/binutils-all/compress.exp       | 354 +++++++++++++++++++++
 binutils/testsuite/binutils-all/dw2-3.rS           |   3 +
 binutils/testsuite/binutils-all/dw2-3.rt           |   6 +
 .../binutils-all/libdw2-compressedgabi.out         |   3 +
 gas/ChangeLog                                      |  18 ++
 gas/as.c                                           |  28 +-
 gas/as.h                                           |  14 +-
 gas/config/tc-i386.c                               |   3 +-
 gas/doc/as.texinfo                                 |  16 +
 gas/testsuite/ChangeLog                            |   9 +
 gas/testsuite/gas/i386/dw2-compressed-1.d          | 105 ++++++
 gas/testsuite/gas/i386/dw2-compressed-2.d          |   7 +
 gas/testsuite/gas/i386/dw2-compressed-3.d          | 104 ++++++
 gas/testsuite/gas/i386/i386.exp                    |   4 +
 gas/testsuite/gas/i386/x86-64-dw2-compressed-2.d   |   7 +
 gas/write.c                                        |  43 ++-
 ld/testsuite/ChangeLog                             |  11 +
 ld/testsuite/ld-elf/compress.exp                   |  26 +-
 ld/testsuite/ld-elf/compressed1a.d                 |  10 +
 ld/testsuite/ld-elf/compressed1b.d                 |   9 +
 ld/testsuite/ld-elf/compressed1c.d                 |  10 +
 31 files changed, 1292 insertions(+), 120 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 192d9aa..68e13c5 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,32 @@
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* archive.c (_bfd_get_elt_at_filepos): Also copy BFD_COMPRESS_GABI
+	bit.
+	* bfd.c (bfd::flags): Increase size to 18 bits.
+	(BFD_COMPRESS_GABI): New.
+	(BFD_FLAGS_SAVED): Add BFD_COMPRESS_GABI.
+	(BFD_FLAGS_FOR_BFD_USE_MASK): Likewise.
+	(bfd_update_compression_header): New fuction.
+	(bfd_check_compression_header): Likewise.
+	(bfd_get_compression_header_size): Likewise.
+	(bfd_is_section_compressed_with_header): Likewise.
+	* compress.c (MAX_COMPRESSION_HEADER_SIZE): New.
+	(bfd_compress_section_contents): Return the uncompressed size if
+	the full section contents is compressed successfully.  Support
+	converting from/to .zdebug* sections.
+	(bfd_get_full_section_contents): Call
+	bfd_get_compression_header_size to get compression header size.
+	(bfd_is_section_compressed): Renamed to ...
+	(bfd_is_section_compressed_with_header): This.  Add a pointer
+	argument to return compression header size.
+	(bfd_is_section_compressed): Use it.
+	(bfd_init_section_decompress_status): Call
+	bfd_get_compression_header_size to get compression header size.
+	Return FALSE if uncompressed section size is 0.
+	* elf.c (_bfd_elf_make_section_from_shdr): Support converting
+	from/to .zdebug* sections.
+	* bfd-in2.h: Regenerated.
+
 2015-04-07  Alan Modra  <amodra@gmail.com>
 
 	* elf.c (_bfd_elf_get_reloc_section): Allow for .got.plt being
diff --git a/bfd/archive.c b/bfd/archive.c
index 3899d84..31d86f3 100644
--- a/bfd/archive.c
+++ b/bfd/archive.c
@@ -725,8 +725,10 @@ _bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos)
 
   n_bfd->arelt_data = new_areldata;
 
-  /* Copy BFD_COMPRESS and BFD_DECOMPRESS flags.  */
-  n_bfd->flags |= archive->flags & (BFD_COMPRESS | BFD_DECOMPRESS);
+  /* Copy BFD_COMPRESS, BFD_DECOMPRESS and BFD_COMPRESS_GABI flags.  */
+  n_bfd->flags |= archive->flags & (BFD_COMPRESS
+				    | BFD_DECOMPRESS
+				    | BFD_COMPRESS_GABI);
 
   if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_bfd))
     return n_bfd;
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 81def3f..679595e 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -6323,7 +6323,7 @@ struct bfd
   ENUM_BITFIELD (bfd_direction) direction : 2;
 
   /* Format_specific flags.  */
-  flagword flags : 17;
+  flagword flags : 18;
 
   /* Values that may appear in the flags field of a BFD.  These also
      appear in the object_flags field of the bfd_target structure, where
@@ -6400,14 +6400,19 @@ struct bfd
   /* BFD is a dummy, for plugins.  */
 #define BFD_PLUGIN 0x10000
 
+  /* Compress sections in this BFD with SHF_COMPRESSED from gABI.  */
+#define BFD_COMPRESS_GABI 0x20000
+
   /* Flags bits to be saved in bfd_preserve_save.  */
 #define BFD_FLAGS_SAVED \
-  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN)
+  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN \
+   | BFD_COMPRESS_GABI)
 
   /* Flags bits which are for BFD use only.  */
 #define BFD_FLAGS_FOR_BFD_USE_MASK \
   (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \
-   | BFD_PLUGIN | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT)
+   | BFD_PLUGIN | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT \
+   | BFD_COMPRESS_GABI)
 
   /* Is the file descriptor being cached?  That is, can it be closed as
      needed, and re-opened when accessed later?  */
@@ -6777,6 +6782,15 @@ void bfd_emul_set_commonpagesize (const char *, bfd_vma);
 
 char *bfd_demangle (bfd *, const char *, int);
 
+void bfd_update_compression_header
+   (bfd *abfd, bfd_byte *contents, asection *sec);
+
+bfd_boolean bfd_check_compression_header
+   (bfd *abfd, bfd_byte *contents, asection *sec,
+    bfd_size_type uncompressed_size);
+
+int bfd_get_compression_header_size (bfd *abfd, asection *sec);
+
 /* Extracted from archive.c.  */
 symindex bfd_get_next_mapent
    (bfd *abfd, symindex previous, carsym **sym);
@@ -7289,6 +7303,10 @@ bfd_boolean bfd_get_full_section_contents
 void bfd_cache_section_contents
    (asection *sec, void *contents);
 
+bfd_boolean bfd_is_section_compressed_with_header
+   (bfd *abfd, asection *section,
+    int *compression_header_size_p);
+
 bfd_boolean bfd_is_section_compressed
    (bfd *abfd, asection *section);
 
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 5ae5eca..ba78cf3 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -85,7 +85,7 @@ CODE_FRAGMENT
 .  ENUM_BITFIELD (bfd_direction) direction : 2;
 .
 .  {* Format_specific flags.  *}
-.  flagword flags : 17;
+.  flagword flags : 18;
 .
 .  {* Values that may appear in the flags field of a BFD.  These also
 .     appear in the object_flags field of the bfd_target structure, where
@@ -162,14 +162,19 @@ CODE_FRAGMENT
 .  {* BFD is a dummy, for plugins.  *}
 .#define BFD_PLUGIN 0x10000
 .
+.  {* Compress sections in this BFD with SHF_COMPRESSED from gABI.  *}
+.#define BFD_COMPRESS_GABI 0x20000
+.
 .  {* Flags bits to be saved in bfd_preserve_save.  *}
 .#define BFD_FLAGS_SAVED \
-.  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN)
+.  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_PLUGIN \
+.   | BFD_COMPRESS_GABI)
 .
 .  {* Flags bits which are for BFD use only.  *}
 .#define BFD_FLAGS_FOR_BFD_USE_MASK \
 .  (BFD_IN_MEMORY | BFD_COMPRESS | BFD_DECOMPRESS | BFD_LINKER_CREATED \
-.   | BFD_PLUGIN | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT)
+.   | BFD_PLUGIN | BFD_TRADITIONAL_FORMAT | BFD_DETERMINISTIC_OUTPUT \
+.   | BFD_COMPRESS_GABI)
 .
 .  {* Is the file descriptor being cached?  That is, can it be closed as
 .     needed, and re-opened when accessed later?  *}
@@ -1940,3 +1945,145 @@ bfd_demangle (bfd *abfd, const char *name, int options)
 
   return res;
 }
+
+/*
+FUNCTION
+	bfd_update_compression_header
+
+SYNOPSIS
+	void bfd_update_compression_header
+	  (bfd *abfd, bfd_byte *contents, asection *sec);
+
+DESCRIPTION
+	Set the compression header at CONTENTS of SEC in ABFD and update
+	elf_section_flags for compression.
+*/
+
+void
+bfd_update_compression_header (bfd *abfd, bfd_byte *contents,
+			       asection *sec)
+{
+  if ((abfd->flags & BFD_COMPRESS) != 0)
+    {
+      if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+	{
+	  if ((abfd->flags & BFD_COMPRESS_GABI) != 0)
+	    {
+	      const struct elf_backend_data *bed
+		= get_elf_backend_data (abfd);
+
+	      /* Set the SHF_COMPRESSED bit.  */
+	      elf_section_flags (sec) |= SHF_COMPRESSED;
+
+	      if (bed->s->elfclass == ELFCLASS32)
+		{
+		  Elf32_External_Chdr *echdr
+		    = (Elf32_External_Chdr *) contents;
+		  bfd_put_32 (abfd, ELFCOMPRESS_ZLIB, &echdr->ch_type);
+		  bfd_put_32 (abfd, sec->size, &echdr->ch_size);
+		  bfd_put_32 (abfd, 1 << sec->alignment_power,
+			      &echdr->ch_addralign);
+		}
+	      else
+		{
+		  Elf64_External_Chdr *echdr
+		    = (Elf64_External_Chdr *) contents;
+		  bfd_put_64 (abfd, ELFCOMPRESS_ZLIB, &echdr->ch_type);
+		  bfd_put_64 (abfd, sec->size, &echdr->ch_size);
+		  bfd_put_64 (abfd, 1 << sec->alignment_power,
+			      &echdr->ch_addralign);
+		}
+	    }
+	  else
+	    /* Clear the SHF_COMPRESSED bit.  */
+	    elf_section_flags (sec) &= ~SHF_COMPRESSED;
+	}
+    }
+  else
+    abort ();
+}
+
+/*
+   FUNCTION
+   bfd_check_compression_header
+
+   SYNOPSIS
+	bfd_boolean bfd_check_compression_header
+	  (bfd *abfd, bfd_byte *contents, asection *sec,
+	   bfd_size_type uncompressed_size);
+
+DESCRIPTION
+	Check the compression header at CONTENTS of SEC in ABFD with
+	the uncompressed size UNCOMPRESSED_SIZE.
+
+RETURNS
+	Return TRUE if the compression header is valid.
+*/
+
+bfd_boolean
+bfd_check_compression_header (bfd *abfd, bfd_byte *contents,
+			      asection *sec,
+			      bfd_size_type uncompressed_size)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+      && (elf_section_flags (sec) & SHF_COMPRESSED) != 0)
+    {
+      Elf_Internal_Chdr chdr;
+      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      if (bed->s->elfclass == ELFCLASS32)
+	{
+	  Elf32_External_Chdr *echdr = (Elf32_External_Chdr *) contents;
+	  chdr.ch_type = bfd_get_32 (abfd, &echdr->ch_type);
+	  chdr.ch_size = bfd_get_32 (abfd, &echdr->ch_size);
+	  chdr.ch_addralign = bfd_get_32 (abfd, &echdr->ch_addralign);
+	}
+      else
+	{
+	  Elf64_External_Chdr *echdr = (Elf64_External_Chdr *) contents;
+	  chdr.ch_type = bfd_get_64 (abfd, &echdr->ch_type);
+	  chdr.ch_size = bfd_get_64 (abfd, &echdr->ch_size);
+	  chdr.ch_addralign = bfd_get_64 (abfd, &echdr->ch_addralign);
+	}
+      return (chdr.ch_type == ELFCOMPRESS_ZLIB
+	      && chdr.ch_size == uncompressed_size
+	      && chdr.ch_addralign == 1U << sec->alignment_power);
+    }
+
+  return FALSE;
+}
+
+/*
+FUNCTION
+	bfd_get_compression_header_size
+
+SYNOPSIS
+	int bfd_get_compression_header_size (bfd *abfd, asection *sec);
+
+DESCRIPTION
+	Return the size of the compression header of SEC in ABFD.
+
+RETURNS
+	Return the size of the compression header in bytes.
+*/
+
+int
+bfd_get_compression_header_size (bfd *abfd, asection *sec)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+    {
+      if (sec == NULL)
+	{
+	  if (!(abfd->flags & BFD_COMPRESS_GABI))
+	    return 0;
+	}
+      else if (!(elf_section_flags (sec) & SHF_COMPRESSED))
+	return 0;
+
+      if (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS32)
+	return sizeof (Elf32_External_Chdr);
+      else
+	return sizeof (Elf64_External_Chdr);
+    }
+
+  return 0;
+}
diff --git a/bfd/compress.c b/bfd/compress.c
index b57650f..ba9fc96 100644
--- a/bfd/compress.c
+++ b/bfd/compress.c
@@ -24,6 +24,8 @@
 #include "libbfd.h"
 #include "safe-ctype.h"
 
+#define MAX_COMPRESSION_HEADER_SIZE 24
+
 static bfd_boolean
 decompress_contents (bfd_byte *compressed_buffer,
 		     bfd_size_type compressed_size,
@@ -65,55 +67,136 @@ decompress_contents (bfd_byte *compressed_buffer,
    field was allocated using bfd_malloc() or equivalent.  If zlib
    is not installed on this machine, the input is unmodified.
 
-   Return @code{TRUE} if the full section contents is compressed
-   successfully.  */
+   Return the uncompressed size if the full section contents is
+   compressed successfully.  Otherwise return 0.  */
 
-static bfd_boolean
-bfd_compress_section_contents (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec,
+static bfd_size_type
+bfd_compress_section_contents (bfd *abfd, sec_ptr sec,
 			       bfd_byte *uncompressed_buffer,
 			       bfd_size_type uncompressed_size)
 {
   uLong compressed_size;
-  bfd_byte *compressed_buffer;
-
-  compressed_size = compressBound (uncompressed_size) + 12;
-  compressed_buffer = (bfd_byte *) bfd_malloc (compressed_size);
+  bfd_byte *buffer;
+  bfd_size_type buffer_size;
+  bfd_boolean decompress;
+  int zlib_size;
+  int orig_compression_header_size;
+  int compression_header_size
+    = bfd_get_compression_header_size (abfd, NULL);
+  bfd_boolean compressed
+    = bfd_is_section_compressed_with_header (abfd, sec,
+					     &orig_compression_header_size);
+
+  if (compressed)
+    {
+      /* We shouldn't decompress unsupported compressed section.  */
+      if (orig_compression_header_size < 0)
+	abort ();
 
-  if (compressed_buffer == NULL)
-    return FALSE;
+      /* Different compression schemes.  Just move the compressed section
+	 contents to the right position. */
+      if (orig_compression_header_size == 0)
+	{
+	  /* Convert it from .zdebug* section.  Get the uncompressed
+	     size first.  */
+	  zlib_size = uncompressed_size;
+	  compressed_size = zlib_size + compression_header_size;
+	  uncompressed_size = bfd_getb64 (uncompressed_buffer + 4);
+	}
+      else
+	{
+	  /* Convert it to .zdebug* section. */
+	  zlib_size = uncompressed_size - orig_compression_header_size;
+	  compressed_size = zlib_size;
+	}
+    }
+  else
+    compressed_size = compressBound (uncompressed_size) + 12;
 
-  if (compress ((Bytef*) compressed_buffer + 12,
-		&compressed_size,
-		(const Bytef*) uncompressed_buffer,
-		uncompressed_size) != Z_OK)
+  /* When converting from .zdebug* section, uncompress if it leads to
+     smaller size.  */
+  if (compressed
+      && orig_compression_header_size == 0
+      && compressed_size > uncompressed_size)
     {
-      free (compressed_buffer);
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
+      decompress = TRUE;
+      buffer_size = uncompressed_size;
     }
+  else
+    {
+      decompress = FALSE;
+      buffer_size = compressed_size + compression_header_size;
+    }
+  buffer = (bfd_byte *) bfd_malloc (buffer_size);
+  if (buffer == NULL)
+    return 0;
 
-  compressed_size += 12;
-
-  /* PR binutils/18087: If compression didn't make the section smaller,
-     just keep it uncompressed.  */
-  if (compressed_size < uncompressed_size)
+  if (compressed)
     {
-      /* Write the zlib header.  In this case, it should be "ZLIB" followed
-	 by the uncompressed section size, 8 bytes in big-endian order.  */
-      memcpy (compressed_buffer, "ZLIB", 4);
-      bfd_putb64 (uncompressed_size, compressed_buffer + 4);
-      free (uncompressed_buffer);
-      sec->contents = compressed_buffer;
-      sec->size = compressed_size;
-      sec->compress_status = COMPRESS_SECTION_DONE;
+      sec->size = uncompressed_size;
+      if (decompress)
+	{
+	  if (!decompress_contents (uncompressed_buffer, zlib_size,
+				    buffer, uncompressed_size))
+	    {
+	      bfd_set_error (bfd_error_bad_value);
+	      free (buffer);
+	      return 0;
+	    }
+	  free (uncompressed_buffer);
+	  sec->contents = buffer;
+	  sec->compress_status = COMPRESS_SECTION_DONE;
+	  return uncompressed_size;
+	}
+      else
+	{
+	  bfd_update_compression_header (abfd, buffer, sec);
+	  memmove (buffer + compression_header_size,
+		   uncompressed_buffer + orig_compression_header_size,
+		   zlib_size);
+	}
     }
   else
     {
-      sec->contents = uncompressed_buffer;
-      sec->compress_status = COMPRESS_SECTION_NONE;
+      bfd_size_type size = uncompressed_size;
+      int header_size = 12 + compression_header_size;
+      if (compress ((Bytef*) buffer + header_size,
+		    &compressed_size,
+		    (const Bytef*) uncompressed_buffer,
+		    uncompressed_size) != Z_OK)
+	{
+	  free (buffer);
+	  bfd_set_error (bfd_error_bad_value);
+	  return 0;
+	}
+
+      compressed_size += header_size;
+      /* PR binutils/18087: If compression didn't make the section smaller,
+	 just keep it uncompressed.  */
+      if (compressed_size < uncompressed_size)
+	{
+	  bfd_update_compression_header (abfd, buffer, sec);
+
+	  /* Write the zlib header.  In this case, it should be "ZLIB"
+	     followed by the uncompressed section size, 8 bytes in
+	     big-endian order.  */
+	  memcpy (buffer + compression_header_size, "ZLIB", 4);
+	  bfd_putb64 (size, buffer + compression_header_size + 4);
+	}
+      else
+	{
+	  sec->contents = uncompressed_buffer;
+	  sec->compress_status = COMPRESS_SECTION_NONE;
+	  return uncompressed_size;
+	}
     }
 
-  return TRUE;
+  free (uncompressed_buffer);
+  sec->contents = buffer;
+  sec->size = compressed_size;
+  sec->compress_status = COMPRESS_SECTION_DONE;
+
+  return uncompressed_size;
 }
 
 /*
@@ -143,6 +226,7 @@ bfd_get_full_section_contents (bfd *abfd, sec_ptr sec, bfd_byte **ptr)
   bfd_size_type save_size;
   bfd_size_type save_rawsize;
   bfd_byte *compressed_buffer;
+  unsigned int compression_header_size;
 
   if (abfd->direction != write_direction && sec->rawsize != 0)
     sz = sec->rawsize;
@@ -200,7 +284,9 @@ bfd_get_full_section_contents (bfd *abfd, sec_ptr sec, bfd_byte **ptr)
       if (p == NULL)
 	goto fail_compressed;
 
-      if (!decompress_contents (compressed_buffer, sec->compressed_size, p, sz))
+      compression_header_size = bfd_get_compression_header_size (abfd, sec);
+      if (!decompress_contents (compressed_buffer + compression_header_size,
+				sec->compressed_size, p, sz))
 	{
 	  bfd_set_error (bfd_error_bad_value);
 	  if (p != *ptr)
@@ -256,50 +342,96 @@ bfd_cache_section_contents (asection *sec, void *contents)
   sec->flags |= SEC_IN_MEMORY;
 }
 
-
 /*
 FUNCTION
-	bfd_is_section_compressed
+	bfd_is_section_compressed_with_header
 
 SYNOPSIS
-	bfd_boolean bfd_is_section_compressed
-	  (bfd *abfd, asection *section);
+	bfd_boolean bfd_is_section_compressed_with_header
+	  (bfd *abfd, asection *section,
+	  int *compression_header_size_p);
 
 DESCRIPTION
-	Return @code{TRUE} if @var{section} is compressed.
+	Return @code{TRUE} if @var{section} is compressed.  Compression
+	header size is returned in @var{compression_header_size_p}.  If
+	compression is unsupported, compression header size is returned
+	with -1.
 */
 
 bfd_boolean
-bfd_is_section_compressed (bfd *abfd, sec_ptr sec)
+bfd_is_section_compressed_with_header (bfd *abfd, sec_ptr sec,
+				       int *compression_header_size_p)
 {
-  bfd_byte compressed_buffer [12];
+  bfd_byte header[MAX_COMPRESSION_HEADER_SIZE + 12];
+  int compression_header_size;
+  int header_size = 12;
   unsigned int saved = sec->compress_status;
   bfd_boolean compressed;
 
+  compression_header_size = bfd_get_compression_header_size (abfd, sec);
+  if (compression_header_size > MAX_COMPRESSION_HEADER_SIZE)
+    abort ();
+  header_size += compression_header_size;
+
   /* Don't decompress the section.  */
   sec->compress_status = COMPRESS_SECTION_NONE;
 
   /* Read the zlib header.  In this case, it should be "ZLIB" followed
      by the uncompressed section size, 8 bytes in big-endian order.  */
-  compressed = (bfd_get_section_contents (abfd, sec, compressed_buffer, 0, 12)
-		&& CONST_STRNEQ ((char*) compressed_buffer, "ZLIB"));
+  compressed = bfd_get_section_contents (abfd, sec, header, 0,
+					 header_size)
+		&& CONST_STRNEQ ((char*) header + compression_header_size,
+				 "ZLIB");
 
-  /* Check for the pathalogical case of a debug string section that
-     contains the string ZLIB.... as the first entry.  We assume that
-     no uncompressed .debug_str section would ever be big enough to
-     have the first byte of its (big-endian) size be non-zero.  */
-  if (compressed
-      && strcmp (sec->name, ".debug_str") == 0
-      && ISPRINT (compressed_buffer[4]))
-    compressed = FALSE;
+  if (compressed)
+    {
+      if (compression_header_size != 0)
+	{
+	  bfd_size_type uncompressed_size
+	    = bfd_getb64 ((bfd_byte *) header
+			  + compression_header_size + 4);
+	  if (!bfd_check_compression_header (abfd, header, sec,
+					     uncompressed_size))
+	    compression_header_size = -1;
+	}
+      /* Check for the pathalogical case of a debug string section that
+	 contains the string ZLIB.... as the first entry.  We assume that
+	 no uncompressed .debug_str section would ever be big enough to
+	 have the first byte of its (big-endian) size be non-zero.  */
+      else if (strcmp (sec->name, ".debug_str") == 0
+	       && ISPRINT (header[compression_header_size + 4]))
+	compressed = FALSE;
+    }
 
   /* Restore compress_status.  */
   sec->compress_status = saved;
+  *compression_header_size_p = compression_header_size;
   return compressed;
 }
 
 /*
 FUNCTION
+	bfd_is_section_compressed
+
+SYNOPSIS
+	bfd_boolean bfd_is_section_compressed
+	  (bfd *abfd, asection *section);
+
+DESCRIPTION
+	Return @code{TRUE} if @var{section} is compressed.
+*/
+
+bfd_boolean
+bfd_is_section_compressed (bfd *abfd, sec_ptr sec)
+{
+  int compression_header_size;
+  return (bfd_is_section_compressed_with_header (abfd, sec,
+						 &compression_header_size)
+	  && compression_header_size >= 0);
+}
+
+/*
+FUNCTION
 	bfd_init_section_decompress_status
 
 SYNOPSIS
@@ -319,13 +451,20 @@ DESCRIPTION
 bfd_boolean
 bfd_init_section_decompress_status (bfd *abfd, sec_ptr sec)
 {
-  bfd_byte compressed_buffer [12];
+  bfd_byte header[MAX_COMPRESSION_HEADER_SIZE + 12];
+  int compression_header_size;
+  int header_size = 12;
   bfd_size_type uncompressed_size;
 
+  compression_header_size = bfd_get_compression_header_size (abfd, sec);
+  if (compression_header_size > MAX_COMPRESSION_HEADER_SIZE)
+    abort ();
+  header_size += compression_header_size;
+
   if (sec->rawsize != 0
       || sec->contents != NULL
       || sec->compress_status != COMPRESS_SECTION_NONE
-      || !bfd_get_section_contents (abfd, sec, compressed_buffer, 0, 12))
+      || !bfd_get_section_contents (abfd, sec, header, 0, header_size))
     {
       bfd_set_error (bfd_error_invalid_operation);
       return FALSE;
@@ -333,13 +472,20 @@ bfd_init_section_decompress_status (bfd *abfd, sec_ptr sec)
 
   /* Read the zlib header.  In this case, it should be "ZLIB" followed
      by the uncompressed section size, 8 bytes in big-endian order.  */
-  if (! CONST_STRNEQ ((char*) compressed_buffer, "ZLIB"))
+  if (! CONST_STRNEQ ((char*) header + compression_header_size, "ZLIB"))
     {
       bfd_set_error (bfd_error_wrong_format);
       return FALSE;
     }
 
-  uncompressed_size = bfd_getb64 (compressed_buffer + 4);
+  uncompressed_size = bfd_getb64 (header + compression_header_size + 4);
+  if (compression_header_size != 0
+      && !bfd_check_compression_header (abfd, header, sec,
+					uncompressed_size))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
+    }
   sec->compressed_size = sec->size;
   sec->size = uncompressed_size;
   sec->compress_status = DECOMPRESS_SECTION_SIZED;
@@ -389,9 +535,12 @@ bfd_init_section_compress_status (bfd *abfd, sec_ptr sec)
 				 0, uncompressed_size))
     ret = FALSE;
   else
-    ret = bfd_compress_section_contents (abfd, sec,
-					 uncompressed_buffer,
-					 uncompressed_size);
+    {
+      uncompressed_size = bfd_compress_section_contents (abfd, sec,
+							 uncompressed_buffer,
+							 uncompressed_size);
+      ret = uncompressed_size != 0;
+    }
 
   return ret;
 }
diff --git a/bfd/elf.c b/bfd/elf.c
index 5fad4f1..a031b9e 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1042,26 +1042,35 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
     {
       enum { nothing, compress, decompress } action = nothing;
       char *new_name;
+      int compression_header_size;
+      bfd_boolean compressed
+	= bfd_is_section_compressed_with_header (abfd, newsect,
+						 &compression_header_size);
 
-      if (bfd_is_section_compressed (abfd, newsect))
+      if (compressed)
 	{
 	  /* Compressed section.  Check if we should decompress.  */
 	  if ((abfd->flags & BFD_DECOMPRESS))
 	    action = decompress;
 	}
-      else
+
+      /* Compress the uncompressed section or convert from/to .zdebug*
+	 section.  Check if we should compress.  */
+      if (action == nothing)
 	{
-	  /* Normal section.  Check if we should compress.  */
-	  if ((abfd->flags & BFD_COMPRESS) && newsect->size != 0)
+	  if (newsect->size != 0
+	      && (abfd->flags & BFD_COMPRESS)
+	      && compression_header_size >= 0
+	      && (!compressed
+		  || ((compression_header_size > 0)
+		      != ((abfd->flags & BFD_COMPRESS_GABI) != 0))))
 	    action = compress;
+	  else
+	    return TRUE;
 	}
 
-      new_name = NULL;
-      switch (action)
+      if (action == compress)
 	{
-	case nothing:
-	  break;
-	case compress:
 	  if (!bfd_init_section_compress_status (abfd, newsect))
 	    {
 	      (*_bfd_error_handler)
@@ -1069,25 +1078,9 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
 		 abfd, name);
 	      return FALSE;
 	    }
-	  /* PR binutils/18087: Compression does not always make a section
-	     smaller.  So only rename the section when compression has
-	     actually taken place.  */
-	  if (newsect->compress_status == COMPRESS_SECTION_DONE)
-	    {
-	      if (name[1] != 'z')
-		{
-		  unsigned int len = strlen (name);
-
-		  new_name = bfd_alloc (abfd, len + 2);
-		  if (new_name == NULL)
-		    return FALSE;
-		  new_name[0] = '.';
-		  new_name[1] = 'z';
-		  memcpy (new_name + 2, name + 1, len);
-		}
-	    }
-	  break;
-	case decompress:
+	}
+      else
+	{
 	  if (!bfd_init_section_decompress_status (abfd, newsect))
 	    {
 	      (*_bfd_error_handler)
@@ -1095,6 +1088,13 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
 		 abfd, name);
 	      return FALSE;
 	    }
+	}
+
+      new_name = NULL;
+      if (action == decompress
+	   || (action == compress
+	       && (abfd->flags & BFD_COMPRESS_GABI) != 0))
+	{
 	  if (name[1] == 'z')
 	    {
 	      unsigned int len = strlen (name);
@@ -1105,7 +1105,24 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
 	      new_name[0] = '.';
 	      memcpy (new_name + 1, name + 2, len - 1);
 	    }
-	  break;
+	}
+      else if (action == compress
+	       && newsect->compress_status == COMPRESS_SECTION_DONE)
+	{
+	  /* PR binutils/18087: Compression does not always make a section
+	     smaller.  So only rename the section when compression has
+	     actually taken place.  */
+	  if (name[1] != 'z')
+	    {
+	      unsigned int len = strlen (name);
+
+	      new_name = bfd_alloc (abfd, len + 2);
+	      if (new_name == NULL)
+		return FALSE;
+	      new_name[0] = '.';
+	      new_name[1] = 'z';
+	      memcpy (new_name + 2, name + 1, len);
+	    }
 	}
       if (new_name != NULL)
 	bfd_rename_section (abfd, newsect, new_name);
diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index bbc00b9..7d5b1a0 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -1,5 +1,18 @@
 2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
 
+	* objcopy.c (do_debug_sections): Add compress_zlib,
+	compress_gnu_zlib and compress_gabi_zlib.
+	(copy_options): Use optional_argument on compress-debug-sections.
+	(copy_usage): Update --compress-debug-sections.
+	(copy_file): Handle compress_zlib, compress_gnu_zlib and
+	compress_gabi_zlib.
+	(copy_main): Handle
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+	* doc/binutils.texi: Document
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
 	* objcopy.c (copy_usage): Replace "--interleave [<number>]" with
 	--interleave[=<number>].
 
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index e0527aa..dbf44c9 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -1849,6 +1849,19 @@ renamed to begin with @samp{.zdebug} instead of @samp{.debug}.  Note -
 if compression would actually make a section @emph{larger} then it is
 not compressed or renamed.
 
+@item --compress-debug-sections=none
+@itemx --compress-debug-sections=zlib
+@itemx --compress-debug-sections=zlib-gnu
+@itemx --compress-debug-sections=zlib-gabi
+For ELF files, these options control how DWARF debug sections are
+compressed.  @option{--compress-debug-sections=none} is equivalent
+to @option{--nocompress-debug-sections}.
+@option{--compress-debug-sections=zlib} and
+@option{--compress-debug-sections=zlib-gnu} are equivalent to
+@option{--compress-debug-sections}.
+@option{--compress-debug-sections=zlib-gabi} compresses
+DWARF debug sections with SHF_COMPRESSED from the ELF ABI.
+
 @item --decompress-debug-sections
 Decompress DWARF debug sections using zlib.  The original section
 names of the compressed sections are restored.
diff --git a/binutils/objcopy.c b/binutils/objcopy.c
index a21006a..a0452c9 100644
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -204,6 +204,9 @@ static enum
 {
   nothing,
   compress,
+  compress_zlib,
+  compress_gnu_zlib,
+  compress_gabi_zlib,
   decompress
 } do_debug_sections = nothing;
 
@@ -380,7 +383,7 @@ static struct option copy_options[] =
   {"change-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_VMA},
   {"change-start", required_argument, 0, OPTION_CHANGE_START},
   {"change-warnings", no_argument, 0, OPTION_CHANGE_WARNINGS},
-  {"compress-debug-sections", no_argument, 0, OPTION_COMPRESS_DEBUG_SECTIONS},
+  {"compress-debug-sections", optional_argument, 0, OPTION_COMPRESS_DEBUG_SECTIONS},
   {"debugging", no_argument, 0, OPTION_DEBUGGING},
   {"decompress-debug-sections", no_argument, 0, OPTION_DECOMPRESS_DEBUG_SECTIONS},
   {"disable-deterministic-archives", no_argument, 0, 'U'},
@@ -601,7 +604,8 @@ copy_usage (FILE *stream, int exit_status)
                                    <commit>\n\
      --subsystem <name>[:<version>]\n\
                                    Set PE subsystem to <name> [& <version>]\n\
-     --compress-debug-sections     Compress DWARF debug sections using zlib\n\
+     --compress-debug-sections[={none|zlib|zlib-gnu|zlib-gabi}]\n\
+                                   Compress DWARF debug sections using zlib\n\
      --decompress-debug-sections   Decompress DWARF debug sections using zlib\n\
   -v --verbose                     List all object files modified\n\
   @<file>                          Read options from <file>\n\
@@ -2588,7 +2592,18 @@ copy_file (const char *input_filename, const char *output_filename,
   switch (do_debug_sections)
     {
     case compress:
+    case compress_zlib:
+    case compress_gnu_zlib:
+    case compress_gabi_zlib:
       ibfd->flags |= BFD_COMPRESS;
+      if (do_debug_sections != compress)
+	{
+	  if (ibfd->xvec->flavour != bfd_target_elf_flavour)
+	    fatal (_("--compress-debug-sections=[zlib|zlib-gnu|zlib-gabi] is unsupported for `%s'"),
+		   bfd_get_target (ibfd));
+	  if (do_debug_sections == compress_gabi_zlib)
+	    ibfd->flags |= BFD_COMPRESS_GABI;
+	}
       break;
     case decompress:
       ibfd->flags |= BFD_DECOMPRESS;
@@ -3998,7 +4013,22 @@ copy_main (int argc, char *argv[])
 	  break;
 
 	case OPTION_COMPRESS_DEBUG_SECTIONS:
-	  do_debug_sections = compress;
+	  if (optarg)
+	    {
+	      if (strcasecmp (optarg, "none") == 0)
+		do_debug_sections = decompress;
+	      else if (strcasecmp (optarg, "zlib") == 0)
+		do_debug_sections = compress_zlib;
+	      else if (strcasecmp (optarg, "zlib-gnu") == 0)
+		do_debug_sections = compress_gnu_zlib;
+	      else if (strcasecmp (optarg, "zlib-gabi") == 0)
+		do_debug_sections = compress_gabi_zlib;
+	      else
+		fatal (_("unrecognized --compress-debug-sections type `%s'"),
+		       optarg);
+	    }
+	  else
+	    do_debug_sections = compress;
 	  break;
 
 	case OPTION_DEBUGGING:
diff --git a/binutils/testsuite/ChangeLog b/binutils/testsuite/ChangeLog
index ece81a6..b97f412 100644
--- a/binutils/testsuite/ChangeLog
+++ b/binutils/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* compress.exp: Add tests for
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+	* binutils-all/dw2-3.rS: New file.
+	* binutils-all/dw2-3.rt: Likewise.
+	* binutils-all/libdw2-compressedgabi.out: Likewise.
+
 2015-04-06  H.J. Lu  <hongjiu.lu@intel.com>
 
 	* binutils-all/compress.exp: Remove is_zlib_supported check.
diff --git a/binutils/testsuite/binutils-all/compress.exp b/binutils/testsuite/binutils-all/compress.exp
index 36b2a26..4d3b71c 100644
--- a/binutils/testsuite/binutils-all/compress.exp
+++ b/binutils/testsuite/binutils-all/compress.exp
@@ -217,3 +217,357 @@ if ![is_remote host] {
 }
 
 run_dump_test "debug_str"
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-1.S ${compressedfile}gabi.o --compress-debug-sections=zlib-gabi] } then {
+    fail "compressed debug sections with zlib-gabi"
+    return
+}
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-1.S ${compressedfile}gnu.o --compress-debug-sections=zlib-gnu] } then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+set src1 ${compressedfile}gnu.o
+set src2 ${compressedfile}.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-2.S ${compressedfile2}gabi.o --compress-debug-sections=zlib-gabi] } then {
+    fail "compressed debug sections with zlib-gabi"
+    return
+}
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-2.S ${compressedfile2}gnu.o --compress-debug-sections=zlib-gnu] } then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+set src1 ${compressedfile2}gnu.o
+set src2 ${compressedfile2}.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-3.S ${compressedfile3}gabi.o --compress-debug-sections=zlib-gabi] } then {
+    fail "compressed debug sections with zlib-gabi"
+    return
+}
+
+set testname "readelf -t zlib-gabi compress debug sections"
+set got [remote_exec host "$READELF -t --wide ${compressedfile3}gabi.o" "" "/dev/null" "tmpdir/dw2-3.rt"]
+if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+    fail "$testname (reason: unexpected output)"
+    send_log "$got\n"
+}
+if { [regexp_diff tmpdir/dw2-3.rt $srcdir/$subdir/dw2-3.rt] } then {
+    fail "$testname"
+} else {
+    pass "$testname"
+}
+
+set testname "readelf -S zlib-gabi compress debug sections"
+set got [remote_exec host "$READELF -S --wide ${compressedfile3}gabi.o" "" "/dev/null" "tmpdir/dw2-3.rS"]
+if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+    fail "$testname (reason: unexpected output)"
+    send_log "$got\n"
+}
+if { [regexp_diff tmpdir/dw2-3.rS $srcdir/$subdir/dw2-3.rS] } then {
+    fail "$testname"
+} else {
+    pass "$testname"
+}
+
+if { ![binutils_assemble_flags $srcdir/$subdir/dw2-3.S ${compressedfile3}gnu.o --compress-debug-sections=zlib-gnu] } then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+set src1 ${compressedfile3}gnu.o
+set src2 ${compressedfile3}.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    fail "compressed debug sections with zlib-gnu"
+    return
+}
+
+remote_file host delete ${libfile}gabi.a
+set got [binutils_run $AR "rc ${libfile}gabi.a ${compressedfile}gabi.o ${compressedfile2}gabi.o ${compressedfile3}gabi.o"]
+if ![string match "" $got] then {
+    fail "compressed debug sections"
+    return
+}
+
+set testname "objcopy compress debug sections with zlib-gabi"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gabi ${testfile}.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile}gabi.o ${copyfile}gabi.o\n"
+verbose "cmp ${compressedfile}gabi.o ${copyfile}gabi.o"
+set src1 ${compressedfile}gabi.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy decompress compressed debug sections with zlib-gabi"
+set got [binutils_run $OBJCOPY "--decompress-debug-sections ${compressedfile}gabi.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${testfile}.o ${copyfile}gabi.o\n"
+verbose "cmp ${testfile}.o ${copyfile}gabi.o"
+set src1 ${testfile}.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gnu compress debug sections with zlib-gabi"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gabi ${compressedfile}.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile}gabi.o ${copyfile}gabi.o\n"
+verbose "cmp ${compressedfile}gabi.o ${copyfile}gabi.o"
+set src1 ${compressedfile}gabi.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gabi compress debug sections with zlib-gnu"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gnu ${compressedfile}gabi.o ${copyfile}gnu.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile}gnu.o ${copyfile}gnu.o\n"
+verbose "cmp ${compressedfile}gnu.o ${copyfile}gnu.o"
+set src1 ${compressedfile}gnu.o
+set src2 ${copyfile}gnu.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy compress debug sections 3 with zlib-gabi"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gabi ${testfile3}.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile3}gabi.o ${copyfile}gabi.o\n"
+verbose "cmp ${compressedfile3}gabi.o ${copyfile}gabi.o"
+set src1 ${compressedfile3}gabi.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy decompress compressed debug sections 3 with zlib-gabi"
+set got [binutils_run $OBJCOPY "--decompress-debug-sections ${compressedfile3}gabi.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${testfile3}.o ${copyfile}gabi.o\n"
+verbose "cmp ${testfile3}.o ${copyfile}gabi.o"
+set src1 ${testfile3}.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gnu compress debug sections 3 with zlib-gabi"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gabi ${compressedfile3}.o ${copyfile}gabi.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile3}gabi.o ${copyfile}gabi.o\n"
+verbose "cmp ${compressedfile3}gabi.o ${copyfile}gabi.o"
+set src1 ${compressedfile3}gabi.o
+set src2 ${copyfile}gabi.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gabi compress debug sections 3 with zlib-gnu"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gnu ${compressedfile3}gabi.o ${copyfile}gnu.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o\n"
+verbose "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o"
+set src1 ${compressedfile3}gnu.o
+set src2 ${copyfile}gnu.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gnu compress debug sections 3"
+set got [binutils_run $OBJCOPY "${compressedfile3}gnu.o ${copyfile}gnu.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o\n"
+verbose "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o"
+set src1 ${compressedfile3}gnu.o
+set src2 ${copyfile}gnu.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy zlib-gnu compress debug sections 3"
+set got [binutils_run $OBJCOPY "${compressedfile3}gnu.o ${copyfile}gnu.o"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+    return
+}
+send_log "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o\n"
+verbose "cmp ${compressedfile3}gnu.o ${copyfile}gnu.o"
+set src1 ${compressedfile3}gnu.o
+set src2 ${copyfile}gnu.o
+set status [remote_exec build cmp "${src1} ${src2}"]
+set exec_output [lindex $status 1]
+set exec_output [prune_warnings $exec_output]
+if ![string match "" $exec_output] then {
+    send_log "$exec_output\n"
+    verbose "$exec_output" 1
+    fail "objcopy ($testname)"
+} else {
+    pass "objcopy ($testname)"
+}
+
+set testname "objcopy decompress debug sections in archive with zlib-gabi"
+set got [binutils_run $OBJCOPY "--decompress-debug-sections ${libfile}gabi.a ${copyfile}gabi.a"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+} else {
+    set got [remote_exec host "$READELF -S --wide ${copyfile}gabi.a" "" "/dev/null" "tmpdir/libdw2.out"]
+
+    if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+	fail "$testname (reason: unexpected output)"
+	send_log $got
+	send_log "\n"
+    }
+
+    if { [regexp_diff tmpdir/libdw2.out $srcdir/$subdir/libdw2.out] } then {
+	fail "$testname"
+    } else {
+	pass "$testname"
+    }
+}
+
+set testname "objcopy compress debug sections in archive with zlib-gabi"
+set got [binutils_run $OBJCOPY "--compress-debug-sections=zlib-gabi ${copyfile}gabi.a ${compressedcopyfile}gabi.a"]
+if ![string match "" $got] then {
+    fail "objcopy ($testname)"
+} else {
+    set got [remote_exec host "$OBJDUMP -s -j .debug_info
+    ${compressedcopyfile}gabi.a" "" "/dev/null" "tmpdir/libdw2-compressedgabi.out"]
+
+    if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+	fail "$testname (reason: unexpected output)"
+	send_log $got
+	send_log "\n"
+    }
+
+    if { [regexp_diff tmpdir/libdw2-compressedgabi.out $srcdir/$subdir/libdw2-compressedgabi.out] } then {
+	fail "$testname"
+    } else {
+	pass "$testname"
+    }
+}
+
+set testname "objdump compress debug sections 3 with zlib-gabi"
+set got [remote_exec host "$OBJDUMP -W ${compressedfile3}gabi.o" "" "/dev/null" "objdump.out"]
+if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+    fail "$testname"
+    send_log "$got\n"
+}
+if { [regexp_diff objdump.out $srcdir/$subdir/dw2-3.W] } then {
+    fail "$testname"
+} else {
+    pass "$testname"
+}
diff --git a/binutils/testsuite/binutils-all/dw2-3.rS b/binutils/testsuite/binutils-all/dw2-3.rS
new file mode 100644
index 0000000..f1637e9
--- /dev/null
+++ b/binutils/testsuite/binutils-all/dw2-3.rS
@@ -0,0 +1,3 @@
+#...
+ +\[[ 0-9]+\] .debug_info +(PROGBITS|MIPS_DWARF) +0+ +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +C +0 +0 +1
+#pass
diff --git a/binutils/testsuite/binutils-all/dw2-3.rt b/binutils/testsuite/binutils-all/dw2-3.rt
new file mode 100644
index 0000000..f59cbaa
--- /dev/null
+++ b/binutils/testsuite/binutils-all/dw2-3.rt
@@ -0,0 +1,6 @@
+#...
+ +\[[ 0-9]+\] .debug_info
+ +(PROGBITS|MIPS_DWARF) +0+ +[0-9a-f]+ +[0-9a-f]+ +[0-9a-f]+ +0 +0 +1
+ +\[0+800\]: COMPRESSED
+ +ZLIB, 0+9d, 1
+#pass
diff --git a/binutils/testsuite/binutils-all/libdw2-compressedgabi.out b/binutils/testsuite/binutils-all/libdw2-compressedgabi.out
new file mode 100644
index 0000000..3d395e4
--- /dev/null
+++ b/binutils/testsuite/binutils-all/libdw2-compressedgabi.out
@@ -0,0 +1,3 @@
+#...
+ .*ZLIB.*
+#pass
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 6f9118da..7ff56c1 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,21 @@
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* as.c (show_usage): Update --compress-debug-sections.
+	(std_longopts): Use optional_argument on compress-debug-sections.
+	(parse_args): Handle
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+	* as.h (compressed_debug_section_type): New.
+	(flag_compress_debug): Change type to compressed_debug_section_type.
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+	* write.c (compress_debug): Set BFD_COMPRESS_GABI for
+	--compress-debug-sections=zlib-gabi.  Call
+	bfd_get_compression_header_size to get compression header size.
+	Don't rename section name for --compress-debug-sections=zlib-gabi.
+	* config/tc-i386.c (compressed_debug_section_type): Set to
+	COMPRESS_DEBUG_ZLIB.
+	* doc/as.texinfo: Document
+	--compress-debug-sections={none|zlib|zlib-gnu|zlib-gabi}.
+
 2015-04-07  Renlin Li  <renlin.li@arm.com>
 
 	* config/tc-aarch64.c (mapping_state): Use subseg_text_p.
diff --git a/gas/as.c b/gas/as.c
index a670d3e..2a8923f 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -246,7 +246,7 @@ Options:\n\
   fprintf (stream, _("\
   --alternate             initially turn on alternate macro syntax\n"));
   fprintf (stream, _("\
-  --compress-debug-sections\n\
+  --compress-debug-sections[={none|zlib|zlib-gnu|zlib-gabi}]\n\
                           compress DWARF debug sections using zlib\n"));
   fprintf (stream, _("\
   --nocompress-debug-sections\n\
@@ -471,7 +471,7 @@ parse_args (int * pargc, char *** pargv)
     ,{"a", optional_argument, NULL, 'a'}
     /* Handle -al=<FILE>.  */
     ,{"al", optional_argument, NULL, OPTION_AL}
-    ,{"compress-debug-sections", no_argument, NULL, OPTION_COMPRESS_DEBUG}
+    ,{"compress-debug-sections", optional_argument, NULL, OPTION_COMPRESS_DEBUG}
     ,{"nocompress-debug-sections", no_argument, NULL, OPTION_NOCOMPRESS_DEBUG}
     ,{"debug-prefix-map", required_argument, NULL, OPTION_DEBUG_PREFIX_MAP}
     ,{"defsym", required_argument, NULL, OPTION_DEFSYM}
@@ -655,11 +655,31 @@ This program has absolutely no warranty.\n"));
 	  exit (EXIT_SUCCESS);
 
 	case OPTION_COMPRESS_DEBUG:
-	  flag_compress_debug = 1;
+	  if (optarg)
+	    {
+#if defined OBJ_ELF || defined OBJ_MAYBE_ELF
+	      if (strcasecmp (optarg, "none") == 0)
+		flag_compress_debug = COMPRESS_DEBUG_NONE;
+	      else if (strcasecmp (optarg, "zlib") == 0)
+		flag_compress_debug = COMPRESS_DEBUG_ZLIB;
+	      else if (strcasecmp (optarg, "zlib-gnu") == 0)
+		flag_compress_debug = COMPRESS_DEBUG_GNU_ZLIB;
+	      else if (strcasecmp (optarg, "zlib-gabi") == 0)
+		flag_compress_debug = COMPRESS_DEBUG_GABI_ZLIB;
+	      else
+		as_fatal (_("Invalid --compress-debug-sections option: `%s'"),
+			  optarg);
+#else
+	      as_fatal (_("--compress-debug-sections=%s is unsupported"),
+			optarg);
+#endif
+	    }
+	  else
+	    flag_compress_debug = COMPRESS_DEBUG;
 	  break;
 
 	case OPTION_NOCOMPRESS_DEBUG:
-	  flag_compress_debug = 0;
+	  flag_compress_debug = COMPRESS_DEBUG_NONE;
 	  break;
 
 	case OPTION_DEBUG_PREFIX_MAP:
diff --git a/gas/as.h b/gas/as.h
index 2f05745..e04cc0f 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -370,8 +370,18 @@ COMMON int flag_strip_local_absolute;
 /* True if we should generate a traditional format object file.  */
 COMMON int flag_traditional_format;
 
-/* TRUE if debug sections should be compressed.  */
-COMMON int flag_compress_debug;
+/* Types of compressed debug sections.  We currently support zlib.  */
+enum compressed_debug_section_type
+{
+  COMPRESS_DEBUG_NONE = 0,
+  COMPRESS_DEBUG,
+  COMPRESS_DEBUG_ZLIB,
+  COMPRESS_DEBUG_GNU_ZLIB,
+  COMPRESS_DEBUG_GABI_ZLIB
+};
+
+/* Type of compressed debug sections we should generate.   */
+COMMON enum compressed_debug_section_type flag_compress_debug;
 
 /* TRUE if .note.GNU-stack section with SEC_CODE should be created */
 COMMON int flag_execstack;
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 8266134..1ab1252 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -35,7 +35,8 @@
 
 #ifdef TE_LINUX
 /* Default to compress debug sections for Linux.  */
-int flag_compress_debug = 1;
+enum compressed_debug_section_type flag_compress_debug
+  = COMPRESS_DEBUG_ZLIB;
 #endif
 
 #ifndef REGISTER_WARNINGS
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index bedb4d5..95d6c38 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -630,6 +630,22 @@ to begin with @samp{.zdebug}, and the resulting object file may not be
 compatible with older linkers and object file utilities.  Note if compression
 would make a given section @emph{larger} then it is not compressed or renamed.
 
+@ifset ELF
+@cindex @samp{--compress-debug-sections=} option
+@item --compress-debug-sections=none
+@itemx --compress-debug-sections=zlib
+@itemx --compress-debug-sections=zlib-gnu
+@itemx --compress-debug-sections=zlib-gabi
+These options control how DWARF debug sections are compressed.
+@option{--compress-debug-sections=none} is equivalent to
+@option{--nocompress-debug-sections}.
+@option{--compress-debug-sections=zlib} and
+@option{--compress-debug-sections=zlib-gnu} are equivalent to
+@option{--compress-debug-sections}.
+@option{--compress-debug-sections=zlib-gabi} compresses
+DWARF debug sections with SHF_COMPRESSED from the ELF ABI.
+@end ifset
+
 @item --nocompress-debug-sections
 Do not compress DWARF debug sections.  This is the default.
 
diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog
index c896ec5..fc2b934 100644
--- a/gas/testsuite/ChangeLog
+++ b/gas/testsuite/ChangeLog
@@ -1,3 +1,12 @@
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* gas/i386/dw2-compressed-1.d: New file.
+	* gas/i386/dw2-compressed-2.d: Likewise.
+	* gas/i386/dw2-compressed-3.d: Likewise.
+	* gas/i386/x86-64-dw2-compressed-2.d: Likewise.
+	* gas/i386/i386.exp: Run dw2-compressed-2, dw2-compressed-1,
+	dw2-compressed-3 and x86-64-dw2-compressed-2.
+
 2015-04-06  H.J. Lu  <hongjiu.lu@intel.com>
 
 	* lib/gas-defs.exp (run_dump_test): Remove is_zlib_supported check.
diff --git a/gas/testsuite/gas/i386/dw2-compressed-1.d b/gas/testsuite/gas/i386/dw2-compressed-1.d
new file mode 100644
index 0000000..f4c110d
--- /dev/null
+++ b/gas/testsuite/gas/i386/dw2-compressed-1.d
@@ -0,0 +1,105 @@
+#source: dw2-compress-1.s
+#as: --compress-debug-sections=zlib-gabi
+#readelf: -w
+#name: DWARF2 debugging information 1 with SHF_COMPRESSED
+
+Contents of the .debug_info section:
+
+  Compilation Unit @ offset 0x0:
+   Length:        0x4e \(32-bit\)
+   Version:       2
+   Abbrev Offset: 0x0
+   Pointer Size:  4
+ <0><b>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+    <c>   DW_AT_stmt_list   : 0x0
+    <10>   DW_AT_high_pc     : 0x4
+    <14>   DW_AT_low_pc      : 0x0
+    <18>   DW_AT_name        : file1.txt
+    <22>   DW_AT_producer    : GNU C 3.3.3
+    <2e>   DW_AT_language    : 1	\(ANSI C\)
+ <1><2f>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+    <30>   DW_AT_external    : 1
+    <31>   DW_AT_decl_file   : 1
+    <32>   DW_AT_decl_line   : 2
+    <33>   DW_AT_name        : func_cu1
+    <3c>   DW_AT_type        : <0x4a>
+    <40>   DW_AT_low_pc      : 0x0
+    <44>   DW_AT_high_pc     : 0x4
+    <48>   DW_AT_frame_base  : 1 byte block: 55 	\(DW_OP_reg5 \([^()]*\)\)
+ <1><4a>: Abbrev Number: 3 \(DW_TAG_base_type\)
+    <4b>   DW_AT_name        : int
+    <4f>   DW_AT_byte_size   : 4
+    <50>   DW_AT_encoding    : 5	\(signed\)
+ <1><51>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+  Number TAG \(0x0\)
+   1      DW_TAG_compile_unit    \[has children\]
+    DW_AT_stmt_list    DW_FORM_data4
+    DW_AT_high_pc      DW_FORM_addr
+    DW_AT_low_pc       DW_FORM_addr
+    DW_AT_name         DW_FORM_string
+    DW_AT_producer     DW_FORM_string
+    DW_AT_language     DW_FORM_data1
+    DW_AT value: 0     DW_FORM value: 0
+   2      DW_TAG_subprogram    \[no children\]
+    DW_AT_external     DW_FORM_flag
+    DW_AT_decl_file    DW_FORM_data1
+    DW_AT_decl_line    DW_FORM_data1
+    DW_AT_name         DW_FORM_string
+    DW_AT_type         DW_FORM_ref4
+    DW_AT_low_pc       DW_FORM_addr
+    DW_AT_high_pc      DW_FORM_addr
+    DW_AT_frame_base   DW_FORM_block1
+    DW_AT value: 0     DW_FORM value: 0
+   3      DW_TAG_base_type    \[no children\]
+    DW_AT_name         DW_FORM_string
+    DW_AT_byte_size    DW_FORM_data1
+    DW_AT_encoding     DW_FORM_data1
+    DW_AT value: 0     DW_FORM value: 0
+
+Raw dump of debug contents of section .debug_line:
+
+  Offset:                      0x0
+  Length:                      62
+  DWARF Version:               2
+  Prologue Length:             35
+  Minimum Instruction Length:  1
+  Initial value of 'is_stmt':  1
+  Line Base:                   1
+  Line Range:                  1
+  Opcode Base:                 16
+
+ Opcodes:
+  Opcode 1 has 0 args
+  Opcode 2 has 1 args
+  Opcode 3 has 1 args
+  Opcode 4 has 1 args
+  Opcode 5 has 1 args
+  Opcode 6 has 0 args
+  Opcode 7 has 0 args
+  Opcode 8 has 0 args
+  Opcode 9 has 1 args
+  Opcode 10 has 0 args
+  Opcode 11 has 0 args
+  Opcode 12 has 1 args
+  Opcode 13 has 0 args
+  Opcode 14 has 0 args
+  Opcode 15 has 0 args
+
+ The Directory Table is empty.
+
+ The File Name Table \(offset 0x.*\):
+  Entry	Dir	Time	Size	Name
+  1	0	0	0	file1.txt
+
+ Line Number Statements:
+  \[0x.*\]  Extended opcode 2: set Address to 0x0
+  \[0x.*\]  Advance Line by 3 to 4
+  \[0x.*\]  Copy
+  \[0x.*\]  Copy
+  \[0x.*\]  Extended opcode 2: set Address to 0x4
+  \[0x.*\]  Extended opcode 1: End of Sequence
+
+
diff --git a/gas/testsuite/gas/i386/dw2-compressed-2.d b/gas/testsuite/gas/i386/dw2-compressed-2.d
new file mode 100644
index 0000000..c62c02f
--- /dev/null
+++ b/gas/testsuite/gas/i386/dw2-compressed-2.d
@@ -0,0 +1,7 @@
+#source: dw2-compress-2.s
+#as: --compress-debug-sections=zlib-gabi
+#addr2line: 0x0 0x10 -e
+#name: DWARF2 debugging information 2 with SHF_COMPRESSED
+
+./dw2-compress-2.c:12
+./dw2-compress-2.c:5
diff --git a/gas/testsuite/gas/i386/dw2-compressed-3.d b/gas/testsuite/gas/i386/dw2-compressed-3.d
new file mode 100644
index 0000000..bd2818b
--- /dev/null
+++ b/gas/testsuite/gas/i386/dw2-compressed-3.d
@@ -0,0 +1,104 @@
+#source: dw2-compress-3.s
+#as: --compress-debug-sections=zlib-gabi
+#readelf: -w
+#name: DWARF2 debugging information 3 with SHF_COMPRESSED
+
+Contents of the .debug_info section:
+
+  Compilation Unit @ offset 0x0:
+   Length:        0x32 \(32-bit\)
+   Version:       4
+   Abbrev Offset: 0x0
+   Pointer Size:  4
+ <0><b>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+    <c>   DW_AT_producer    : \(indirect string, offset: 0x2\): GNU C 4.8.3
+    <10>   DW_AT_language    : 1	\(ANSI C\)
+    <11>   DW_AT_name        : \(indirect string, offset: 0xe\): dw2-compress-3.c
+    <15>   DW_AT_comp_dir    : \(indirect string, offset: 0x0\): .
+    <19>   DW_AT_stmt_list   : 0x0
+ <1><1d>: Abbrev Number: 2 \(DW_TAG_variable\)
+    <1e>   DW_AT_name        : foo
+    <22>   DW_AT_decl_file   : 1
+    <23>   DW_AT_decl_line   : 1
+    <24>   DW_AT_type        : <0x2e>
+    <28>   DW_AT_external    : 1
+    <28>   DW_AT_location    : 5 byte block: 3 4 0 0 0 	\(DW_OP_addr: 4\)
+ <1><2e>: Abbrev Number: 3 \(DW_TAG_base_type\)
+    <2f>   DW_AT_byte_size   : 4
+    <30>   DW_AT_encoding    : 5	\(signed\)
+    <31>   DW_AT_name        : int
+ <1><35>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+  Number TAG \(0x0\)
+   1      DW_TAG_compile_unit    \[has children\]
+    DW_AT_producer     DW_FORM_strp
+    DW_AT_language     DW_FORM_data1
+    DW_AT_name         DW_FORM_strp
+    DW_AT_comp_dir     DW_FORM_strp
+    DW_AT_stmt_list    DW_FORM_sec_offset
+    DW_AT value: 0     DW_FORM value: 0
+   2      DW_TAG_variable    \[no children\]
+    DW_AT_name         DW_FORM_string
+    DW_AT_decl_file    DW_FORM_data1
+    DW_AT_decl_line    DW_FORM_data1
+    DW_AT_type         DW_FORM_ref4
+    DW_AT_external     DW_FORM_flag_present
+    DW_AT_location     DW_FORM_exprloc
+    DW_AT value: 0     DW_FORM value: 0
+   3      DW_TAG_base_type    \[no children\]
+    DW_AT_byte_size    DW_FORM_data1
+    DW_AT_encoding     DW_FORM_data1
+    DW_AT_name         DW_FORM_string
+    DW_AT value: 0     DW_FORM value: 0
+
+Contents of the .debug_aranges section:
+
+  Length:                   20
+  Version:                  2
+  Offset into .debug_info:  0x0
+  Pointer Size:             4
+  Segment Size:             0
+
+    Address    Length
+    00000000 00000000 
+
+Raw dump of debug contents of section .debug_line:
+
+  Offset:                      0x0
+  Length:                      45
+  DWARF Version:               2
+  Prologue Length:             39
+  Minimum Instruction Length:  1
+  Initial value of 'is_stmt':  1
+  Line Base:                   -5
+  Line Range:                  14
+  Opcode Base:                 13
+
+ Opcodes:
+  Opcode 1 has 0 args
+  Opcode 2 has 1 args
+  Opcode 3 has 1 args
+  Opcode 4 has 1 args
+  Opcode 5 has 1 args
+  Opcode 6 has 0 args
+  Opcode 7 has 0 args
+  Opcode 8 has 0 args
+  Opcode 9 has 1 args
+  Opcode 10 has 0 args
+  Opcode 11 has 0 args
+  Opcode 12 has 1 args
+
+ The Directory Table is empty.
+
+ The File Name Table \(offset 0x1c\):
+  Entry	Dir	Time	Size	Name
+  1	0	0	0	dw2-compress-3.c
+
+ No Line Number Statements.
+Contents of the .debug_str section:
+
+  0x00000000 2e00474e 55204320 342e382e 33006477 ..GNU C 4.8.3.dw
+  0x00000010 322d636f 6d707265 73732d33 2e6300   2-compress-3.c.
+
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index 92b4424..26cb7ac 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -384,6 +384,7 @@ if [expr ([istarget "i*86-*-*"] ||  [istarget "x86_64-*-*"]) && [gas_32_check]]
 	run_dump_test "debug1"
 
 	run_dump_test "dw2-compress-2"
+	run_dump_test "dw2-compressed-2"
 
 	run_dump_test "bad-size"
 
@@ -420,6 +421,8 @@ if [expr [istarget "i*86-*-*"] || [istarget "x86_64-*-*"]] then {
 	run_list_test_stdin "list-3" "-al"
 	run_dump_test "dw2-compress-1"
 	run_dump_test "dw2-compress-3"
+	run_dump_test "dw2-compressed-1"
+	run_dump_test "dw2-compressed-3"
     }
 }
 
@@ -735,6 +738,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
 	run_dump_test "x86-64-mpx-branch-2"
 
 	run_dump_test "x86-64-dw2-compress-2"
+	run_dump_test "x86-64-dw2-compressed-2"
 
 	run_dump_test "x86-64-size-1"
 	run_dump_test "x86-64-size-2"
diff --git a/gas/testsuite/gas/i386/x86-64-dw2-compressed-2.d b/gas/testsuite/gas/i386/x86-64-dw2-compressed-2.d
new file mode 100644
index 0000000..1200aec
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-dw2-compressed-2.d
@@ -0,0 +1,7 @@
+#source: x86-64-dw2-compress-2.s
+#as: --compress-debug-sections
+#addr2line: 0x0 0x10 -e
+#name: 64bit DWARF2 debugging information 2 with SHF_COMPRESSED
+
+./dw2-compress-2.c:12
+./dw2-compress-2.c:6
diff --git a/gas/write.c b/gas/write.c
index 1ae47a9..bc76962 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -1413,6 +1413,9 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   struct z_stream_s *strm;
   int x;
   flagword flags = bfd_get_section_flags (abfd, sec);
+  unsigned int header_size, compression_header_size;
+  /* Maximimum compression header is 24 bytes.  */
+  bfd_byte compression_header[24];
 
   if (seginfo == NULL
       || sec->size < 32
@@ -1427,18 +1430,26 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   if (strm == NULL)
     return;
 
+  if (flag_compress_debug == COMPRESS_DEBUG_GABI_ZLIB)
+    stdoutput->flags |= BFD_COMPRESS | BFD_COMPRESS_GABI;
+  else
+    stdoutput->flags |= BFD_COMPRESS;
+  compression_header_size
+    = bfd_get_compression_header_size (stdoutput, NULL);
+
   /* Create a new frag to contain the "ZLIB" header.  */
+  header_size = 12 + compression_header_size;
   first_newf = frag_alloc (ob);
-  if (obstack_room (ob) < 12)
+  if (obstack_room (ob) < header_size)
     first_newf = frag_alloc (ob);
-  if (obstack_room (ob) < 12)
-    as_fatal (_("can't extend frag %u chars"), 12);
+  if (obstack_room (ob) < header_size)
+    as_fatal (_("can't extend frag %u chars"), header_size);
   last_newf = first_newf;
-  obstack_blank_fast (ob, 12);
+  obstack_blank_fast (ob, header_size);
   last_newf->fr_type = rs_fill;
-  last_newf->fr_fix = 12;
+  last_newf->fr_fix = header_size;
   header = last_newf->fr_literal;
-  compressed_size = 12;
+  compressed_size = header_size;
 
   /* Stream the frags through the compression engine, adding new frags
      as necessary to accomodate the compressed output.  */
@@ -1522,21 +1533,27 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   if (compressed_size >= uncompressed_size)
     return;
 
-  memcpy (header, "ZLIB", 4);
-  bfd_putb64 (uncompressed_size, header + 4);
+  if (compression_header_size)
+    memcpy (header, compression_header, compression_header_size);
+  memcpy (header + compression_header_size, "ZLIB", 4);
+  bfd_putb64 (uncompressed_size, header + compression_header_size + 4);
 
   /* 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.  */
+  bfd_update_compression_header (abfd, (bfd_byte *) header, sec);
   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;
+  if (!compression_header_size)
+    {
+      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
diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog
index ef54e67..cfa22f4 100644
--- a/ld/testsuite/ChangeLog
+++ b/ld/testsuite/ChangeLog
@@ -1,3 +1,14 @@
+2015-04-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* ld-elf/compress.exp: Add a test for
+	--compress-debug-sections=zlib-gabi.
+	(build_tests): Add 2 tests for --compress-debug-sections=zlib-gabi.
+	(run_tests): Likewise.
+	Verify linker output with zlib-gabi compressed debug input.
+	* ld-elf/compressed1a.d: New file.
+	* ld-elf/compressed1b.d: Likewise.
+	* ld-elf/compressed1c.d: Likewise.
+
 2015-04-07  Alan Modra  <amodra@gmail.com>
 
 	* ld-arm/tls-gdesc-nlazy.g: Adjust for readelf note.
diff --git a/ld/testsuite/ld-elf/compress.exp b/ld/testsuite/ld-elf/compress.exp
index 682811a..adb7fc2 100644
--- a/ld/testsuite/ld-elf/compress.exp
+++ b/ld/testsuite/ld-elf/compress.exp
@@ -40,6 +40,10 @@ if { ![ld_assemble $as "--compress-debug-sections $srcdir/$subdir/empty.s" tmpdi
     fail "linker compressed debug sections"
 }
 
+if { ![ld_assemble $as "--compress-debug-sections=zlib-gabi $srcdir/$subdir/empty.s" tmpdir/emptyzlib.o ] } {
+    fail "linker compressed debug sections"
+}
+
 set build_tests {
   {"Build libfoo.so with compressed debug sections"
    "-shared" "-fPIC -g -Wa,--compress-debug-sections"
@@ -47,13 +51,31 @@ set build_tests {
   {"Build libbar.so with compressed debug sections"
    "-shared" "-fPIC -g -Wa,--compress-debug-sections"
    {begin.c end.c} {} "libbar.so"}
+  {"Build libfoozlib.so with compressed debug sections with zlib-gabi"
+   "-shared" "-fPIC -g -Wa,--compress-debug-sections=zlib-gabi"
+   {foo.c} {} "libfoozlib.so"}
+  {"Build libbarzlib.so with compressed debug sections with zlib-gabi"
+   "-shared" "-fPIC -g -Wa,--compress-debug-sections=zlib-gabi"
+   {begin.c end.c} {} "libbarzlib.so"}
 }
 
 set run_tests {
     {"Run normal with libfoo.so with compressed debug sections"
-     "tmpdir/begin.o tmpdir/libfoo.so tmpdir/end.o" "-Wa,--compress-debug-sections"
-     {main.c} "normal" "normal.out"}
+     "tmpdir/begin.o tmpdir/libfoo.so tmpdir/end.o" ""
+     {main.c} "normal" "normal.out" "-Wa,--compress-debug-sections"}
+    {"Run normal with libfoo.so with compressed debug sections with zlib-gabi"
+     "tmpdir/begin.o tmpdir/libfoozlib.so tmpdir/end.o" ""
+     {main.c} "normal" "normal.out" "-Wa,--compress-debug-sections=zlib-gabi"}
 }
 
 run_cc_link_tests $build_tests
 run_ld_link_exec_tests [] $run_tests
+
+set test_name "Link with zlib-gabi compressed debug input"
+send_log "cmp tmpdir/libfoo.so tmpdir/libfoozlib.so\n"
+if { [catch {exec cmp tmpdir/libfoo.so tmpdir/libfoozlib.so}] } then {
+    send_log "tmpdir/libfoo.so tmpdir/libfoozlib.so differ.\n"
+    fail "$test_name"
+} else {
+    pass "$test_name"
+}
diff --git a/ld/testsuite/ld-elf/compressed1a.d b/ld/testsuite/ld-elf/compressed1a.d
new file mode 100644
index 0000000..653c203
--- /dev/null
+++ b/ld/testsuite/ld-elf/compressed1a.d
@@ -0,0 +1,10 @@
+#source: compress1.s
+#as: --compress-debug-sections=zlib-gabi
+#ld: -e func_cu2
+#readelf: -t
+#notarget: alpha-*
+
+#failif
+#...
+  .*COMPRESSED.*
+#...
diff --git a/ld/testsuite/ld-elf/compressed1b.d b/ld/testsuite/ld-elf/compressed1b.d
new file mode 100644
index 0000000..83dc60f
--- /dev/null
+++ b/ld/testsuite/ld-elf/compressed1b.d
@@ -0,0 +1,9 @@
+#source: compress1.s
+#as: --compress-debug-sections=zlib-gabi
+#ld: -r
+#readelf: -t
+
+#failif
+#...
+  .*COMPRESSED.*
+#...
diff --git a/ld/testsuite/ld-elf/compressed1c.d b/ld/testsuite/ld-elf/compressed1c.d
new file mode 100644
index 0000000..64f75be
--- /dev/null
+++ b/ld/testsuite/ld-elf/compressed1c.d
@@ -0,0 +1,10 @@
+#source: compress1.s
+#as: --compress-debug-sections=zlib-gabi
+#ld: -shared
+#readelf: -t
+#target: *-*-linux* *-*-gnu*
+
+#failif
+#...
+  .*COMPRESSED.*
+#...


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]