Add --decompress option to readelf to decompress sections before they are dumped.
authorNick Clifton <nickc@redhat.com>
Fri, 15 May 2015 16:16:31 +0000 (17:16 +0100)
committerNick Clifton <nickc@redhat.com>
Fri, 15 May 2015 16:16:31 +0000 (17:16 +0100)
bin * readelf.c (options): Add "decompress".
(usage): Mention -z/--decompress.
(parse_args): Handle -z.
(uncompress_section_contents): Move to earlier in the file.
(dump_section_as_strings): If requested, decompress the section
before dumping.
(dump_section_as_bytes): Likewise.
* doc/binutils.texi: Document the new option.

tests * binutils-all/z.s: New test.  Checks the --decompress option to
readelf.
* binutils-all/readelf.exp: Run the test.
* binutils-all/readelf.z: Expected output from readelf.

binutils/ChangeLog
binutils/doc/binutils.texi
binutils/readelf.c
binutils/testsuite/ChangeLog
binutils/testsuite/binutils-all/readelf.exp
binutils/testsuite/binutils-all/readelf.z [new file with mode: 0644]
binutils/testsuite/binutils-all/z.s [new file with mode: 0644]

index 5d017a1..ee2ddf0 100644 (file)
@@ -1,3 +1,14 @@
+2015-05-15  Nick Clifton  <nickc@redhat.com>
+
+       * readelf.c (options): Add "decompress".
+       (usage): Mention -z/--decompress.
+       (parse_args): Handle -z.
+       (uncompress_section_contents): Move to earlier in the file.
+       (dump_section_as_strings): If requested, decompress the section
+       before dumping.
+       (dump_section_as_bytes): Likewise.
+       * doc/binutils.texi: Document the new option.
+
 2015-05-14  Peter Bergner  <bergner@vnet.ibm.com>
 
        * MAINTAINERS: Add myself as PPC maintainer.
index 601de48..619c28e 100644 (file)
@@ -4344,6 +4344,7 @@ readelf [@option{-a}|@option{--all}]
         [@option{-x} <number or name>|@option{--hex-dump=}<number or name>]
         [@option{-p} <number or name>|@option{--string-dump=}<number or name>]
         [@option{-R} <number or name>|@option{--relocated-dump=}<number or name>]
+        [@option{-z}|@option{--decompress}]
         [@option{-c}|@option{--archive-index}]
         [@option{-w[lLiaprmfFsoRt]}|
          @option{--debug-dump}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]]
@@ -4492,6 +4493,12 @@ Displays the contents of the indicated section as printable strings.
 A number identifies a particular section by index in the section table;
 any other string identifies all sections with that name in the object file.
 
+@item -z
+@itemx --decompress
+Requests that the section(s) being dumped by @option{x}, @option{R} or
+@option{p} options are decompressed before being displayed.  If the
+section(s) are not compressed then they are displayed as is.
+
 @item -c
 @itemx --archive-index
 @cindex Archive file symbol index information
index e299e1b..4bb31eb 100644 (file)
@@ -209,6 +209,7 @@ static int do_arch;
 static int do_notes;
 static int do_archive_index;
 static int is_32bit_elf;
+static int decompress_dumps;
 
 struct group_list
 {
@@ -3960,6 +3961,7 @@ static struct option options[] =
   {"hex-dump",        required_argument, 0, 'x'},
   {"relocated-dump",   required_argument, 0, 'R'},
   {"string-dump",      required_argument, 0, 'p'},
+  {"decompress",       no_argument, 0, 'z'},
 #ifdef SUPPORT_DISASSEMBLY
   {"instruction-dump", required_argument, 0, 'i'},
 #endif
@@ -4007,6 +4009,7 @@ usage (FILE * stream)
                          Dump the contents of section <number|name> as strings\n\
   -R --relocated-dump=<number|name>\n\
                          Dump the contents of section <number|name> as relocated bytes\n\
+  -z --decompress        Decompress section before dumping it\n\
   -w[lLiaprmfFsoRt] or\n\
   --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,\n\
                =frames-interp,=str,=loc,=Ranges,=pubtypes,\n\
@@ -4117,7 +4120,7 @@ parse_args (int argc, char ** argv)
     usage (stderr);
 
   while ((c = getopt_long
-         (argc, argv, "ADHINR:SVWacdeghi:lnp:rstuvw::x:", options, NULL)) != EOF)
+         (argc, argv, "ADHINR:SVWacdeghi:lnp:rstuvw::x:z", options, NULL)) != EOF)
     {
       switch (c)
        {
@@ -4200,6 +4203,9 @@ parse_args (int argc, char ** argv)
        case 'R':
          request_dump (RELOC_DUMP);
          break;
+       case 'z':
+         decompress_dumps++;
+         break;
        case 'w':
          do_dump++;
          if (optarg == 0)
@@ -11939,23 +11945,120 @@ get_section_contents (Elf_Internal_Shdr * section, FILE * file)
                              _("section contents"));
 }
 
+/* Uncompresses a section that was compressed using zlib, in place.  */
+
+static bfd_boolean
+uncompress_section_contents (unsigned char **buffer,
+                            dwarf_size_type uncompressed_size,
+                            dwarf_size_type *size)
+{
+  dwarf_size_type compressed_size = *size;
+  unsigned char * compressed_buffer = *buffer;
+  unsigned char * uncompressed_buffer;
+  z_stream strm;
+  int rc;
+
+  /* It is possible the section consists of several compressed
+     buffers concatenated together, so we uncompress in a loop.  */
+  /* PR 18313: The state field in the z_stream structure is supposed
+     to be invisible to the user (ie us), but some compilers will
+     still complain about it being used without initialisation.  So
+     we first zero the entire z_stream structure and then set the fields
+     that we need.  */
+  memset (& strm, 0, sizeof strm);
+  strm.avail_in = compressed_size;
+  strm.next_in = (Bytef *) compressed_buffer;
+  strm.avail_out = uncompressed_size;
+  uncompressed_buffer = (unsigned char *) xmalloc (uncompressed_size);
+
+  rc = inflateInit (& strm);
+  while (strm.avail_in > 0)
+    {
+      if (rc != Z_OK)
+        goto fail;
+      strm.next_out = ((Bytef *) uncompressed_buffer
+                       + (uncompressed_size - strm.avail_out));
+      rc = inflate (&strm, Z_FINISH);
+      if (rc != Z_STREAM_END)
+        goto fail;
+      rc = inflateReset (& strm);
+    }
+  rc = inflateEnd (& strm);
+  if (rc != Z_OK
+      || strm.avail_out != 0)
+    goto fail;
+
+  *buffer = uncompressed_buffer;
+  *size = uncompressed_size;
+  return TRUE;
+
+ fail:
+  free (uncompressed_buffer);
+  /* Indicate decompression failure.  */
+  *buffer = NULL;
+  return FALSE;
+}
 
 static void
 dump_section_as_strings (Elf_Internal_Shdr * section, FILE * file)
 {
-  Elf_Internal_Shdr * relsec;
-  bfd_size_type num_bytes;
-  char * data;
-  char * end;
-  char * start;
-  bfd_boolean some_strings_shown;
-
-  start = get_section_contents (section, file);
+  Elf_Internal_Shdr *  relsec;
+  bfd_size_type        num_bytes;
+  char *               data;
+  char *               end;
+  char *               real_start;
+  char *               start;
+  bfd_boolean          some_strings_shown;
+
+  real_start = start = get_section_contents (section, file);
   if (start == NULL)
     return;
+  num_bytes = section->sh_size;
 
   printf (_("\nString dump of section '%s':\n"), printable_section_name (section));
 
+  if (decompress_dumps)
+    {
+      dwarf_size_type new_size = num_bytes;
+      dwarf_size_type uncompressed_size = 0;
+
+      if ((section->sh_flags & SHF_COMPRESSED) != 0)
+       {
+         Elf_Internal_Chdr chdr;
+         unsigned int compression_header_size
+           = get_compression_header (& chdr, (unsigned char *) start);
+
+         if (chdr.ch_type == ELFCOMPRESS_ZLIB
+             && chdr.ch_addralign == section->sh_addralign)
+           {
+             uncompressed_size = chdr.ch_size;
+             start += compression_header_size;
+             new_size -= compression_header_size;
+           }
+       }
+      else if (new_size > 12 && streq ((char *) start, "ZLIB"))
+       {
+         /* Read the zlib header.  In this case, it should be "ZLIB"
+            followed by the uncompressed section size, 8 bytes in
+            big-endian order.  */
+         uncompressed_size = start[4]; uncompressed_size <<= 8;
+         uncompressed_size += start[5]; uncompressed_size <<= 8;
+         uncompressed_size += start[6]; uncompressed_size <<= 8;
+         uncompressed_size += start[7]; uncompressed_size <<= 8;
+         uncompressed_size += start[8]; uncompressed_size <<= 8;
+         uncompressed_size += start[9]; uncompressed_size <<= 8;
+         uncompressed_size += start[10]; uncompressed_size <<= 8;
+         uncompressed_size += start[11];
+         start += 12;
+         new_size -= 12;
+       }
+
+      if (uncompressed_size
+         && uncompress_section_contents ((unsigned char **) & start,
+                                         uncompressed_size, & new_size))
+       num_bytes = new_size;
+    }
+  
   /* If the section being dumped has relocations against it the user might
      be expecting these relocations to have been applied.  Check for this
      case and issue a warning message in order to avoid confusion.
@@ -11976,7 +12079,6 @@ dump_section_as_strings (Elf_Internal_Shdr * section, FILE * file)
       break;
     }
 
-  num_bytes = section->sh_size;
   data = start;
   end  = start + num_bytes;
   some_strings_shown = FALSE;
@@ -12016,7 +12118,7 @@ dump_section_as_strings (Elf_Internal_Shdr * section, FILE * file)
   if (! some_strings_shown)
     printf (_("  No strings found in this section."));
 
-  free (start);
+  free (real_start);
 
   putchar ('\n');
 }
@@ -12027,20 +12129,65 @@ dump_section_as_bytes (Elf_Internal_Shdr * section,
                       bfd_boolean relocate)
 {
   Elf_Internal_Shdr * relsec;
-  bfd_size_type bytes;
-  bfd_vma addr;
-  unsigned char * data;
-  unsigned char * start;
-
-  start = (unsigned char *) get_section_contents (section, file);
+  bfd_size_type       bytes;
+  bfd_size_type       section_size;
+  bfd_vma             addr;
+  unsigned char *     data;
+  unsigned char *     real_start;
+  unsigned char *     start;
+
+  real_start = start = (unsigned char *) get_section_contents (section, file);
   if (start == NULL)
     return;
+  section_size = section->sh_size;
 
   printf (_("\nHex dump of section '%s':\n"), printable_section_name (section));
 
+  if (decompress_dumps)
+    {
+      dwarf_size_type new_size = section_size;
+      dwarf_size_type uncompressed_size = 0;
+
+      if ((section->sh_flags & SHF_COMPRESSED) != 0)
+       {
+         Elf_Internal_Chdr chdr;
+         unsigned int compression_header_size
+           = get_compression_header (& chdr, start);
+
+         if (chdr.ch_type == ELFCOMPRESS_ZLIB
+             && chdr.ch_addralign == section->sh_addralign)
+           {
+             uncompressed_size = chdr.ch_size;
+             start += compression_header_size;
+             new_size -= compression_header_size;
+           }
+       }
+      else if (new_size > 12 && streq ((char *) start, "ZLIB"))
+       {
+         /* Read the zlib header.  In this case, it should be "ZLIB"
+            followed by the uncompressed section size, 8 bytes in
+            big-endian order.  */
+         uncompressed_size = start[4]; uncompressed_size <<= 8;
+         uncompressed_size += start[5]; uncompressed_size <<= 8;
+         uncompressed_size += start[6]; uncompressed_size <<= 8;
+         uncompressed_size += start[7]; uncompressed_size <<= 8;
+         uncompressed_size += start[8]; uncompressed_size <<= 8;
+         uncompressed_size += start[9]; uncompressed_size <<= 8;
+         uncompressed_size += start[10]; uncompressed_size <<= 8;
+         uncompressed_size += start[11];
+         start += 12;
+         new_size -= 12;
+       }
+
+      if (uncompressed_size
+         && uncompress_section_contents (& start, uncompressed_size,
+                                         & new_size))
+       section_size = new_size;
+    }
+  
   if (relocate)
     {
-      apply_relocations (file, section, start, section->sh_size, NULL, NULL);
+      apply_relocations (file, section, start, section_size, NULL, NULL);
     }
   else
     {
@@ -12066,7 +12213,7 @@ dump_section_as_bytes (Elf_Internal_Shdr * section,
     }
 
   addr = section->sh_addr;
-  bytes = section->sh_size;
+  bytes = section_size;
   data = start;
 
   while (bytes)
@@ -12106,65 +12253,11 @@ dump_section_as_bytes (Elf_Internal_Shdr * section,
       bytes -= lbytes;
     }
 
-  free (start);
+  free (real_start);
 
   putchar ('\n');
 }
 
-/* Uncompresses a section that was compressed using zlib, in place.  */
-
-static int
-uncompress_section_contents (unsigned char **buffer,
-                            dwarf_size_type uncompressed_size,
-                            dwarf_size_type *size)
-{
-  dwarf_size_type compressed_size = *size;
-  unsigned char * compressed_buffer = *buffer;
-  unsigned char * uncompressed_buffer;
-  z_stream strm;
-  int rc;
-
-  /* It is possible the section consists of several compressed
-     buffers concatenated together, so we uncompress in a loop.  */
-  /* PR 18313: The state field in the z_stream structure is supposed
-     to be invisible to the user (ie us), but some compilers will
-     still complain about it being used without initialisation.  So
-     we first zero the entire z_stream structure and then set the fields
-     that we need.  */
-  memset (& strm, 0, sizeof strm);
-  strm.avail_in = compressed_size;
-  strm.next_in = (Bytef *) compressed_buffer;
-  strm.avail_out = uncompressed_size;
-  uncompressed_buffer = (unsigned char *) xmalloc (uncompressed_size);
-
-  rc = inflateInit (& strm);
-  while (strm.avail_in > 0)
-    {
-      if (rc != Z_OK)
-        goto fail;
-      strm.next_out = ((Bytef *) uncompressed_buffer
-                       + (uncompressed_size - strm.avail_out));
-      rc = inflate (&strm, Z_FINISH);
-      if (rc != Z_STREAM_END)
-        goto fail;
-      rc = inflateReset (& strm);
-    }
-  rc = inflateEnd (& strm);
-  if (rc != Z_OK
-      || strm.avail_out != 0)
-    goto fail;
-
-  *buffer = uncompressed_buffer;
-  *size = uncompressed_size;
-  return 1;
-
- fail:
-  free (uncompressed_buffer);
-  /* Indicate decompression failure.  */
-  *buffer = NULL;
-  return 0;
-}
-
 static int
 load_specific_debug_section (enum dwarf_section_display_enum debug,
                             const Elf_Internal_Shdr * sec, void * file)
index ad8d8ad..99801e2 100644 (file)
@@ -1,3 +1,10 @@
+2015-05-15  Nick Clifton  <nickc@redhat.com>
+
+       * binutils-all/z.s: New test.  Checks the --decompress option to
+       readelf.
+       * binutils-all/readelf.exp: Run the test.
+       * binutils-all/readelf.z: Expected output from readelf.
+
 2015-05-14  H.J. Lu  <hongjiu.lu@intel.com>
 
        * binutils-all/compress.exp: Replace "$OBJDUMP -s -j .debug_info"
index 58e140c..59fd556 100644 (file)
@@ -346,29 +346,49 @@ readelf_dump_test
 if {![binutils_assemble $srcdir/$subdir/version.s tmpdir/version.o]} then {
     perror "could not assemble version note test file"
     unresolved "readelf - failed to assemble"
-    return
-}
-
-if ![is_remote host] {
-    set tempfile tmpdir/version.o
+    fail "readelf -n"
 } else {
-    set tempfile [remote_download host tmpdir/version.o]
+
+    if ![is_remote host] {
+       set tempfile tmpdir/version.o
+    } else {
+       set tempfile [remote_download host tmpdir/version.o]
+    }
+
+    readelf_test -n $tempfile readelf.n  {}
 }
 
-readelf_test -n $tempfile readelf.n  {}
 
 # PR 18374 - Check that relocations against the .debug_loc section
 # do not prevent readelf from displaying all the location lists.
 if {![binutils_assemble $srcdir/$subdir/pr18374.s tmpdir/pr18374.o]} then {
     perror "could not assemble PR18374 test file"
     unresolved "readelf - failed to assemble"
-    return
+    fail "readelf --debug-loc"
+} else {
+
+    if ![is_remote host] {
+       set tempfile tmpdir/pr18374.o
+    } else {
+       set tempfile [remote_download host tmpdir/pr18374.o]
+    }
+
+    readelf_test --debug-dump=loc $tempfile readelf.pr18374  {}
 }
 
-if ![is_remote host] {
-    set tempfile tmpdir/pr18374.o
+
+# Check that decompressed dumps work.
+if {![binutils_assemble $srcdir/$subdir/z.s tmpdir/z.o]} then {
+    perror "could not assemble decompress dump test file"
+    unresolved "readelf - failed to assemble"
+    fail "readelf -z"
 } else {
-    set tempfile [remote_download host tmpdir/pr18374.o]
-}
 
-readelf_test --debug-dump=loc $tempfile readelf.pr18374  {}
+    if ![is_remote host] {
+       set tempfile tmpdir/z.o
+    } else {
+       set tempfile [remote_download host tmpdir/z.o]
+    }
+
+    readelf_test {--decompress --hex-dump .debug_loc} $tempfile readelf.z  {}
+}
diff --git a/binutils/testsuite/binutils-all/readelf.z b/binutils/testsuite/binutils-all/readelf.z
new file mode 100644 (file)
index 0000000..0843444
--- /dev/null
@@ -0,0 +1,8 @@
+Hex dump of section '.debug_loc':
+  0x00000000 00000000 00000000 01005000 00000000 ..........P.....
+  0x00000010 00000004 00f30150 9f000000 00000000 .......P........
+  0x00000020 00000000 00000000 00010051 00000000 ...........Q....
+  0x00000030 00000000 0300717f 9f000000 00000000 ......q.........
+  0x00000040 000b0070 0020f301 51227000 229f0000 ...p. ..Q"p."...
+  0x00000050 00000000 00000b00 70002070 0022f301 ........p. p."..
+  0x00000060 51229f00 00000000 000000            Q".........
diff --git a/binutils/testsuite/binutils-all/z.s b/binutils/testsuite/binutils-all/z.s
new file mode 100644 (file)
index 0000000..2d24f33
--- /dev/null
@@ -0,0 +1,70 @@
+       .section        .debug_loc,"",%progbits
+
+       .byte 0x5a
+       .byte 0x4c
+       .byte 0x49
+       .byte 0x42
+       .byte 0x00
+       .byte 0x00
+       .byte 0x00
+       .byte 0x00
+       .byte 0x00
+       .byte 0x00
+       .byte 0x00
+       .byte 0x6b
+       .byte 0x78
+       .byte 0x9c
+       .byte 0x63
+       .byte 0x60
+       .byte 0x80
+       .byte 0x00
+       .byte 0x46
+       .byte 0x86
+       .byte 0x00
+       .byte 0x28
+       .byte 0x8b
+       .byte 0x81
+       .byte 0x85
+       .byte 0xe1
+       .byte 0x33
+       .byte 0x63
+       .byte 0xc0
+       .byte 0x7c
+       .byte 0x06
+       .byte 0x34
+       .byte 0xc0
+       .byte 0xc8
+       .byte 0x10
+       .byte 0x08
+       .byte 0x63
+       .byte 0x32
+       .byte 0x33
+       .byte 0x14
+       .byte 0xd6
+       .byte 0xc3
+       .byte 0xe5
+       .byte 0xb9
+       .byte 0x19
+       .byte 0x0a
+       .byte 0x18
+       .byte 0x14
+       .byte 0x3e
+       .byte 0x33
+       .byte 0x06
+       .byte 0x2a
+       .byte 0x15
+       .byte 0x30
+       .byte 0x28
+       .byte 0xa1
+       .byte 0x0a
+       .byte 0x02
+       .byte 0x05
+       .byte 0x40
+       .byte 0xe2
+       .byte 0x70
+       .byte 0x41
+       .byte 0x00
+       .byte 0xc1
+       .byte 0x6a
+       .byte 0x0a
+       .byte 0x83