This is the mail archive of the binutils@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]

Commit: Add --decompress option to readelf


Hi Guys,

  Whilst working on PR 18734 I ran across a problem with readelf's
  section dumping ability: it does not decompress sections before
  dumping them.  This makes a binary inspection of the contents of a
  compressed debug section very difficult.  So I have am applying a
  patch (attached) to add a new command line option to readelf:
  -z/--decompress.  This makes the -x, -p and -R dumping options
  decompress the section before it is displayed.  The patch also
  includes a testcase to make sure that the option works.

  Tested with no regressions on lots of different targets.

Cheers
  Nick

binutils/ChangeLog
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.

binutils/testsuite/ChangeLog
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.

diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 601de48..619c28e 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -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
diff --git a/binutils/readelf.c b/binutils/readelf.c
index e299e1b..4bb31eb 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -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)
diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp
index 58e140c..59fd556 100644
--- a/binutils/testsuite/binutils-all/readelf.exp
+++ b/binutils/testsuite/binutils-all/readelf.exp
@@ -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  {}
+}
--- /dev/null	2015-05-15 08:10:55.119645277 +0100
+++ binutils/testsuite/binutils-all/z.s	2015-05-15 15:47:20.749038510 +0100
@@ -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
--- /dev/null	2015-05-15 08:10:55.119645277 +0100
+++ binutils/testsuite/binutils-all/readelf.z	2015-05-15 16:04:47.050031412 +0100
@@ -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".........

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