[patch] Add --compress-debug-sections option to gas

Cary Coutant ccoutant@google.com
Mon Jun 28 21:39:00 GMT 2010


A couple of years ago, Craig Silverstein submitted a patch to add
support for compressed debug sections generated by the linker:

  http://sourceware.org/ml/binutils/2008-06/msg00203.html

The attached patch extends this support to the assembler, adding a new
--compress-debug-sections option to gas, and adding the necessary
support (and fixing a couple of bugs in the original patch) for
reading compressed sections in relocatables to bfd, objdump, and
readelf.

Tested on x86_64 by compiling with and without
-Wa,--compress-debug-sections, and comparing readelf -w output and
objdump -W output. No regressions in the test suite.

A patch for gold to read files with compressed debug sections is in progress.

I have to admit that I'm not happy with a couple of things:

(1) In both bfd/dwarf2.c and binutils/objdump.c, in order to
uncompress the section before relocating it, I call
bfd_get_section_contents() to get the unrelocated section contents,
then decompress the contents and put a pointer to the decompressed
contents in sec->contents, setting the SEC_IN_MEMORY flag so that
bfd_simple_get_relocated_section_contents() would not go read the
section again. I couldn't find a simpler way to apply relocations
after decompression.

(2) In gas/write.c, I used a similar trick to signal write_contents()
that the frags had already been collected and compressed. An
alternative I considered was to simply replace the frag list with a
single frag containing the entire compressed section contents, but
that seemed a bit more complicated.

Advice on these issues is welcome.

-cary

p.s. I'm not sure why my regenerated libbfd.h is different from the
original -- nothing changed except for parameters to
bfd_generic_get_relocated_section_contents() that used to be unnamed.
Did "make headers" change behavior since the last time that file was
regenerated? Should I just leave this out of the patch?


bfd/ChangeLog:

        * bfd-in2.h: Regenerate.
        * compress.c (bfd_compress_section_contents): New function.
        * dwarf2.c (read_and_uncompress_section): New function.
        (read_section): Call it.
        (find_line): Likewise.
        * libbfd.h: Regenerate.

binutils/ChangeLog:

        * objdump.c (load_specific_debug_section): Decompress section contents
        before applying relocations.
        * readelf.c (load_specific_debug_section): Update section size after
        decompression.

gas/ChangeLog:

        * as.c (parse_args): Add --compress-debug-sections and
        --nocompress-debug-sections.
        * as.h (flag_compress_debug): New variable.
        * write.c (compress_debug): New function.
        (write_contents): Check for uncompressed section.
        (write_object_file): Compress debug sections if requested.
-------------- next part --------------
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 5c42128..bd648ce 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5821,6 +5821,9 @@ bfd_byte *bfd_simple_get_relocated_section_contents
 bfd_boolean bfd_uncompress_section_contents
    (bfd_byte **buffer, bfd_size_type *size);
 
+bfd_boolean bfd_compress_section_contents
+   (bfd_byte **buffer, bfd_size_type *size);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/bfd/compress.c b/bfd/compress.c
index 5b6ee0e..7b334a0 100644
--- a/bfd/compress.c
+++ b/bfd/compress.c
@@ -1,4 +1,4 @@
-/* ELF attributes support (based on ARM EABI attributes).
+/* Compressed section support (intended for debug sections).
    Copyright 2008
    Free Software Foundation, Inc.
 
@@ -120,3 +120,73 @@ bfd_uncompress_section_contents (bfd_byte **buffer, bfd_size_type *size)
   return FALSE;
 #endif  /* HAVE_ZLIB_H */
 }
+
+/*
+FUNCTION
+	bfd_compress_section_contents
+
+SYNOPSIS
+	bfd_boolean bfd_compress_section_contents
+	  (bfd_byte **buffer, bfd_size_type *size);
+
+DESCRIPTION
+
+	Compresses a section using zlib, in place.  At the call to this
+	function, *@var{buffer} and *@var{size} should point to the section
+	contents to be compressed.  At the end of the function, *@var{buffer}
+	and *@var{size} will point to the compressed contents.  This function
+	assumes *BUFFER was allocated using bfd_malloc() or equivalent.  If
+	zlib is not installed on this machine, the input is unmodified.
+
+        Returns @code{FALSE} if unable to compress successfully; in that case
+        the input is unmodified.  Otherwise, returns @code{TRUE}.
+*/
+
+bfd_boolean
+bfd_compress_section_contents (bfd_byte **buffer, bfd_size_type *size)
+{
+#ifndef HAVE_ZLIB_H
+  /* These are just to quiet gcc.  */
+  buffer = 0;
+  size = 0;
+  return FALSE;
+#else
+  bfd_size_type uncompressed_size = *size;
+  bfd_byte *uncompressed_buffer = *buffer;
+  bfd_size_type compressed_size;
+  bfd_byte *compressed_buffer;
+  int rc;
+  bfd_size_type header_size = 12;
+
+  compressed_size = compressBound (uncompressed_size) + header_size;
+  compressed_buffer = (bfd_byte *) bfd_malloc (compressed_size);
+
+  rc = compress ((Bytef*) compressed_buffer + header_size,
+                 &compressed_size,
+                 (const Bytef*) uncompressed_buffer,
+                 uncompressed_size);
+  if (rc != Z_OK)
+    {
+      free (compressed_buffer);
+      return FALSE;
+    }
+
+  /* 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);
+  compressed_buffer[4] = uncompressed_size >> 56;
+  compressed_buffer[5] = uncompressed_size >> 48;
+  compressed_buffer[6] = uncompressed_size >> 40;
+  compressed_buffer[7] = uncompressed_size >> 32;
+  compressed_buffer[8] = uncompressed_size >> 24;
+  compressed_buffer[9] = uncompressed_size >> 16;
+  compressed_buffer[10] = uncompressed_size >> 8;
+  compressed_buffer[11] = uncompressed_size;
+  compressed_size += header_size;
+
+  free (uncompressed_buffer);
+  *buffer = compressed_buffer;
+  *size = compressed_size;
+  return TRUE;
+#endif  /* HAVE_ZLIB_H */
+}
diff --git a/bfd/dwarf2.c b/bfd/dwarf2.c
index 9b194aa..ffe1108 100644
--- a/bfd/dwarf2.c
+++ b/bfd/dwarf2.c
@@ -405,6 +405,54 @@ lookup_info_hash_table (struct info_hash_table *hash_table, const char *key)
   return entry ? entry->head : NULL;
 }
 
+/* Read a section, uncompress it if necessary, and relocate it.  */
+
+static bfd_boolean
+read_and_uncompress_section (bfd *           abfd,
+			     asection *      msec,
+			     bfd_boolean     section_is_compressed,
+			     asymbol **      syms,
+			     bfd_byte **     section_buffer,
+			     bfd_size_type * section_size)
+{
+  /* Get the unrelocated contents of the section.  */
+  *section_buffer = (bfd_byte *) bfd_malloc (*section_size);
+  if (! *section_buffer)
+    return FALSE;
+  if (! bfd_get_section_contents (abfd, msec, *section_buffer,
+				  0, *section_size))
+    return FALSE;
+
+  if (section_is_compressed)
+    {
+      if (! bfd_uncompress_section_contents (section_buffer, section_size))
+	{
+	  (*_bfd_error_handler) (_("Dwarf Error: unable to decompress %s section."),
+				 bfd_get_section_name (abfd, msec));
+	  bfd_set_error (bfd_error_bad_value);
+	  return FALSE;
+	}
+    }
+
+  if (syms)
+    {
+      /* We want to relocate the data we've already read (and
+	 decompressed), so we store a pointer to the data in
+	 the bfd_section, and tell it that the contents are
+	 already in memory.  */
+      BFD_ASSERT (msec->contents == NULL && (msec->flags & SEC_IN_MEMORY) == 0);
+      msec->contents = *section_buffer;
+      msec->flags |= SEC_IN_MEMORY;
+      msec->size = *section_size;
+      *section_buffer
+	  = bfd_simple_get_relocated_section_contents (abfd, msec, NULL, syms);
+      if (! *section_buffer)
+	return FALSE;
+    }
+
+  return TRUE;
+}
+
 /* Read a section into its appropriate place in the dwarf2_debug
    struct (indicated by SECTION_BUFFER and SECTION_SIZE).  If SYMS is
    not NULL, use bfd_simple_get_relocated_section_contents to read the
@@ -440,32 +488,10 @@ read_section (bfd *           abfd,
 	}
 
       *section_size = msec->rawsize ? msec->rawsize : msec->size;
-      if (syms)
-	{
-	  *section_buffer
-	      = bfd_simple_get_relocated_section_contents (abfd, msec, NULL, syms);
-	  if (! *section_buffer)
-	    return FALSE;
-	}
-      else
-	{
-	  *section_buffer = (bfd_byte *) bfd_malloc (*section_size);
-	  if (! *section_buffer)
-	    return FALSE;
-	  if (! bfd_get_section_contents (abfd, msec, *section_buffer,
-					  0, *section_size))
-	    return FALSE;
-	}
 
-      if (section_is_compressed)
-	{
-	  if (! bfd_uncompress_section_contents (section_buffer, section_size))
-	    {
-	      (*_bfd_error_handler) (_("Dwarf Error: unable to decompress %s section."), compressed_section_name);
-	      bfd_set_error (bfd_error_bad_value);
-	      return FALSE;
-	    }
-	}
+      if (! read_and_uncompress_section (abfd, msec, section_is_compressed,
+					 syms, section_buffer, section_size))
+	return FALSE;
     }
 
   /* It is possible to get a bad value for the offset into the section
@@ -3242,23 +3268,17 @@ find_line (bfd *abfd,
 		{
 		  bfd_size_type size = msec->size;
 		  bfd_byte *buffer, *tmp;
+		  bfd_boolean is_compressed =
+		      strcmp (msec->name, DWARF2_COMPRESSED_DEBUG_INFO) == 0;
 
 		  if (size == 0)
 		    continue;
 
-		  buffer = (bfd_simple_get_relocated_section_contents
-			    (debug_bfd, msec, NULL, symbols));
-		  if (! buffer)
+		  if (! read_and_uncompress_section (debug_bfd, msec,
+		  				     is_compressed, symbols,
+		  				     &buffer, &size))
 		    goto done;
 
-		  if (strcmp (msec->name, DWARF2_COMPRESSED_DEBUG_INFO) == 0)
-		    {
-		      if (! bfd_uncompress_section_contents (&buffer, &size))
-			{
-			  free (buffer);
-			  goto done;
-			}
-		    }
 		  tmp = (bfd_byte *) bfd_realloc (stash->info_ptr_memory,
 						  total_size + size);
 		  if (tmp == NULL)
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 233891c..3da79d9 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2243,12 +2243,12 @@ bfd_boolean bfd_generic_merge_sections
    (bfd *, struct bfd_link_info *);
 
 bfd_byte *bfd_generic_get_relocated_section_contents
-   (bfd *,
-    struct bfd_link_info *,
-    struct bfd_link_order *,
-    bfd_byte *,
-    bfd_boolean,
-    asymbol **);
+   (bfd *abfd,
+    struct bfd_link_info *link_info,
+    struct bfd_link_order *link_order,
+    bfd_byte *data,
+    bfd_boolean relocatable,
+    asymbol **symbols);
 
 /* Extracted from archures.c.  */
 extern const bfd_arch_info_type bfd_default_arch_struct;
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 2a419b7..f94dee9 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -2205,14 +2205,8 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
   section->size = bfd_get_section_size (sec);
   section->start = (unsigned char *) xmalloc (section->size);
 
-  if (is_relocatable && debug_displays [debug].relocate)
-    ret = bfd_simple_get_relocated_section_contents (abfd,
-						     sec,
-						     section->start,
-						     syms) != NULL;
-  else
-    ret = bfd_get_section_contents (abfd, sec, section->start, 0,
-				    section->size);
+  ret = bfd_get_section_contents (abfd, sec, section->start, 0,
+				  section->size);
 
   if (! ret)
     {
@@ -2234,6 +2228,30 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
       section->size = size;
     }
 
+  if (is_relocatable && debug_displays [debug].relocate)
+    {
+      /* We want to relocate the data we've already read (and
+         decompressed), so we store a pointer to the data in
+         the bfd_section, and tell it that the contents are
+         already in memory.  */
+      sec->contents = section->start;
+      sec->flags |= SEC_IN_MEMORY;
+      sec->size = section->size;
+
+      ret = bfd_simple_get_relocated_section_contents (abfd,
+						       sec,
+						       section->start,
+						       syms) != NULL;
+
+      if (! ret)
+        {
+          free_debug_section (debug);
+          printf (_("\nCan't get contents for section '%s'.\n"),
+	          section->name);
+          return 0;
+        }
+    }
+
   return 1;
 }
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index d4f47ca..1c3cb8b 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -9962,8 +9962,11 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
     return 0;
 
   if (section_is_compressed)
-    if (! uncompress_section_contents (&section->start, &section->size))
-      return 0;
+    {
+      if (! uncompress_section_contents (&section->start, &section->size))
+        return 0;
+      sec->sh_size = section->size;
+    }
 
   if (debug_displays [debug].relocate)
     apply_relocations ((FILE *) file, sec, section->start);
diff --git a/gas/as.c b/gas/as.c
index d617b33..e494fa5 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -437,7 +437,9 @@ parse_args (int * pargc, char *** pargv)
       OPTION_AL,
       OPTION_HASH_TABLE_SIZE,
       OPTION_REDUCE_MEMORY_OVERHEADS,
-      OPTION_WARN_FATAL
+      OPTION_WARN_FATAL,
+      OPTION_COMPRESS_DEBUG,
+      OPTION_NOCOMPRESS_DEBUG
     /* When you add options here, check that they do
        not collide with OPTION_MD_BASE.  See as.h.  */
     };
@@ -455,6 +457,8 @@ 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}
+    ,{"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}
     ,{"dump-config", no_argument, NULL, OPTION_DUMPCONFIG}
@@ -634,6 +638,14 @@ This program has absolutely no warranty.\n"));
 #endif
 	  exit (EXIT_SUCCESS);
 
+	case OPTION_COMPRESS_DEBUG:
+	  flag_compress_debug = 1;
+	  break;
+
+	case OPTION_NOCOMPRESS_DEBUG:
+	  flag_compress_debug = 0;
+	  break;
+
 	case OPTION_DEBUG_PREFIX_MAP:
 	  add_debug_prefix_map (optarg);
 	  break;
diff --git a/gas/as.h b/gas/as.h
index 2b2562e..7c16382 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -365,6 +365,9 @@ 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;
+
 /* TRUE if .note.GNU-stack section with SEC_CODE should be created */
 COMMON int flag_execstack;
 
diff --git a/gas/write.c b/gas/write.c
index a148b24..4fc27fc 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -1289,6 +1289,77 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 }
 
 static void
+compress_debug (bfd *abfd ATTRIBUTE_UNUSED,
+		asection *sec,
+		void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  addressT offset = 0;
+  fragS *f;
+  bfd_size_type section_size = (bfd_size_type) sec->size;
+  bfd_byte *contents;
+  const char *section_name;
+  char *compressed_name;
+  int x;
+
+  if (seginfo == NULL
+      || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS)
+      || (bfd_get_section_flags (abfd, sec) & SEC_ALLOC))
+    return;
+
+  section_name = bfd_get_section_name (stdoutput, sec);
+  if (strncmp (section_name, ".debug_", 7) != 0)
+    return;
+
+  /* Gather the frags into a contiguous buffer.  */
+  contents = (bfd_byte *) xmalloc (section_size);
+  for (f = seginfo->frchainP->frch_root;
+       f;
+       f = f->fr_next)
+    {
+      addressT fill_size;
+      char *fill_literal;
+      offsetT count;
+
+      gas_assert (f->fr_type == rs_fill);
+      if (f->fr_fix)
+	{
+	  memcpy (contents + offset, f->fr_literal, (size_t) f->fr_fix);
+	  offset += f->fr_fix;
+	}
+      fill_literal = f->fr_literal + f->fr_fix;
+      fill_size = f->fr_var;
+      count = f->fr_offset;
+      gas_assert (count >= 0);
+      if (fill_size && count)
+	{
+	  while (count--)
+	    {
+	      memcpy (contents + offset, fill_literal, (size_t) fill_size);
+	      offset += fill_size;
+	    }
+	}
+    }
+
+  /* Compress the buffer.  */
+  if (!bfd_compress_section_contents (&contents, &section_size))
+    return;
+
+  /* Store the compressed contents in the section.  */
+  sec->contents = contents;
+  sec->flags |= SEC_IN_MEMORY;
+  x = bfd_set_section_size (abfd, sec, section_size);
+  gas_assert (x);
+
+  /* Change the section name.  */
+  compressed_name = (char *) xmalloc (strlen (section_name) + 2);
+  compressed_name[0] = '.';
+  compressed_name[1] = 'z';
+  strcpy (compressed_name + 2, section_name + 1);
+  bfd_section_name (stdoutput, sec) = compressed_name;
+}
+
+static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		asection *sec,
 		void *xxx ATTRIBUTE_UNUSED)
@@ -1297,6 +1368,20 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
   addressT offset = 0;
   fragS *f;
 
+  if (bfd_get_section_flags (abfd, sec) & SEC_IN_MEMORY)
+    {
+      /* We already gathered the frags and compressed the data.  */
+      int x;
+
+      x = bfd_set_section_contents (stdoutput, sec, sec->contents,
+				    (file_ptr) offset,
+				    (bfd_size_type) sec->size);
+      if (!x)
+	as_fatal (_("can't write %s: %s"), stdoutput->filename,
+		  bfd_errmsg (bfd_get_error ()));
+      return;
+    }
+
   /* Write out the frags.  */
   if (seginfo == NULL
       || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS))
@@ -1912,6 +1997,13 @@ write_object_file (void)
   obj_frob_file_after_relocs ();
 #endif
 
+  /* Once all relocations have been written, we can compress the
+     contents of the debug sections.  This needs to be done before
+     we start writing any sections, because it will affect the file
+     layout, which is fixed once we start writing contents.  */
+  if (flag_compress_debug)
+    bfd_map_over_sections (stdoutput, compress_debug, (char *) 0);
+
   bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
 }
 


More information about the Binutils mailing list