Improve objdump's handling of compressed sections.
authorNick Clifton <nickc@redhat.com>
Tue, 14 Nov 2023 10:57:58 +0000 (10:57 +0000)
committerNick Clifton <nickc@redhat.com>
Tue, 14 Nov 2023 10:57:58 +0000 (10:57 +0000)
  PR 31062
  * objdump.c (decompressed_dumps): New local variable. (usage): Mention the -z/--decompress option. (long_options): Add --decompress. (dump_section_header): Add "COMPRESSED" to the Flags field of any compressed section. (dump_section): Warn users when dumping a compressed section. (display_any_bfd): Decompress the section if decompressed_dumps is true. (main): Handle the -z/--decompress option.
  * NEWS: Mention the new feature.
  * doc/binutils.texi: Document the new feature.
  * testsuite/binutils-all/objdump.s: Update expected output.
  * testsuite/binutils-all/objdump.exp: Add test of -Z -s.
  * testsuite/binutils-all/objdump.Zs: New file.
  * readelf.c (maybe_expand_or_relocate_section): New function. Contains common code found in dump functions.  Adds a note message if a compressed section is not being decompressed. (dump_section_as_strings): Use new function. (dump_section_as_bytes): Likewise.

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

index 57a8fa73ee33b1428b25a0fa26314493eea1f896..d695d3b74968c649902dc27cab13fc99440b5bb3 100644 (file)
@@ -1,3 +1,27 @@
+2023-11-14  Nick Clifton  <nickc@redhat.com>
+
+       PR 31062
+       * objdump.c (decompressed_dumps): New local variable.
+       (usage): Mention the -z/--decompress option.
+       (long_options): Add --decompress.
+       (dump_section_header): Add "COMPRESSED" to the Flags field of any
+       compressed section.
+       (dump_section): Warn users when dumping a compressed section.
+       (display_any_bfd): Decompress the section if decompressed_dumps is
+       true.
+       (main): Handle the -z/--decompress option.
+       * NEWS: Mention the new feature.
+       * doc/binutils.texi: Document the new feature.
+       * testsuite/binutils-all/objdump.s: Update expected output.
+       * testsuite/binutils-all/objdump.exp: Add test of -Z -s.
+       * testsuite/binutils-all/objdump.Zs: New file.
+
+       * readelf.c (maybe_expand_or_relocate_section): New function.
+       Contains common code found in dump functions.  Adds a note message
+       if a compressed section is not being decompressed.
+       (dump_section_as_strings): Use new function.
+       (dump_section_as_bytes): Likewise.
+
 2023-11-10  Simon Marchi  <simon.marchi@efficios.com>
 
        * readelf.c (decode_AMDGPU_machine_flags): Handle gfx1100,
index 1aae340ea2a8c6670392013992fe826218fbc8ee..3bf3b568179358c45b40602ba5f4e800b6ebb506 100644 (file)
@@ -1,5 +1,13 @@
 -*- text -*-
 
+* The objdump program has a new command line option -Z/--decompress which
+  changes the behaviour of the -s/--full-contents option, forcing it to
+  decompress the contents of any compressed section before they are displayed.
+
+  In addition when objdump is displaying sections headers (via the -h/--headers
+  command line option) it will now display "COMPRESSED" in the Flags field of
+  any compressed section.
+
 * The readelf program has a new command line option --extra-sym-info which
   extends the information displayed by the --symbols option.  When enabled
   the display will include the name of the section referenced by a symbol's
index f94693631c70e606dee379d2e9cd732e0a9def9f..ec23a78470e3138a582319f4940cfc1125b72b7f 100644 (file)
@@ -2269,6 +2269,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{-r}|@option{--reloc}]
         [@option{-R}|@option{--dynamic-reloc}]
         [@option{-s}|@option{--full-contents}]
+        [@option{-Z}|@option{--decompress}]
         [@option{-W[lLiaprmfFsoORtUuTgAck]}|
          @option{--dwarf}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=str-offsets,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links]]
         [@option{-WK}|@option{--dwarf=follow-links}]
@@ -2800,7 +2801,10 @@ disassembly.
 @cindex object file sections
 Display the full contents of sections, often used in combination with
 @option{-j} to request specific sections.  By default all non-empty
-non-bss sections are displayed.
+non-bss sections are displayed.  By default any compressed section
+will be displayed in its compressed form.  In order to see the
+contents in a decompressed form add the @option{-Z} option to the
+command line.
 
 @item -S
 @itemx --source
@@ -3056,6 +3060,15 @@ Also do not truncate symbol names when they are displayed.
 Normally the disassembly output will skip blocks of zeroes.  This
 option directs the disassembler to disassemble those blocks, just like
 any other data.
+
+@item -Z
+@itemx --decompress
+@cindex sections, full contents
+@cindex object file sections
+@cindex compressed section contents
+The @option{-Z} option is meant to be used in conunction with the
+@option{-s} option.  It instructs @command{objdump} to decompress any
+compressed sections before displaying their contents.
 @end table
 
 @c man end
index 608134265669d724cca54c605c553abc9fc58944..640ccb5dbc1d9294abce990c32593eb016a3188c 100644 (file)
@@ -137,6 +137,7 @@ static bool color_output = false;   /* --visualize-jumps=color.  */
 static bool extended_color_output = false; /* --visualize-jumps=extended-color.  */
 static int process_links = false;       /* --process-links.  */
 static int show_all_symbols;            /* --show-all-symbols.  */
+static bool decompressed_dumps = false; /* -Z, --decompress.  */
 
 static enum color_selection
   {
@@ -278,6 +279,8 @@ usage (FILE *stream, int status)
   fprintf (stream, _("\
   -s, --full-contents      Display the full contents of all sections requested\n"));
   fprintf (stream, _("\
+  -Z, --decompress         Decompress section(s) before displaying their contents\n"));
+  fprintf (stream, _("\
   -g, --debugging          Display debug information in object file\n"));
   fprintf (stream, _("\
   -e, --debugging-tags     Display debug information using ctags style\n"));
@@ -500,6 +503,7 @@ static struct option long_options[]=
 #endif
   {"debugging", no_argument, NULL, 'g'},
   {"debugging-tags", no_argument, NULL, 'e'},
+  {"decompress", no_argument, NULL, 'Z'},
   {"demangle", optional_argument, NULL, 'C'},
   {"disassemble", optional_argument, NULL, 'd'},
   {"disassemble-all", no_argument, NULL, 'D'},
@@ -930,6 +934,9 @@ dump_section_header (bfd *abfd, asection *section, void *data)
       comma = ", ";
     }
 
+  if (bfd_is_section_compressed (abfd, section))
+    printf ("%sCOMPRESSED", comma);
+
   printf ("\n");
 #undef PF
 }
@@ -5024,6 +5031,9 @@ dump_section (bfd *abfd, asection *section, void *dummy ATTRIBUTE_UNUSED)
            (unsigned long) (section->filepos + start_offset));
   printf ("\n");
 
+  if (bfd_is_section_compressed (abfd, section) && ! decompressed_dumps)
+    printf (_(" NOTE: This section is compressed, but its contents have NOT been expanded for this dump.\n"));
+
   if (!bfd_get_full_section_contents (abfd, section, &data))
     {
       non_fatal (_("Reading section %s failed because: %s"),
@@ -5780,7 +5790,7 @@ static void
 display_any_bfd (bfd *file, int level)
 {
   /* Decompress sections unless dumping the section contents.  */
-  if (!dump_section_contents)
+  if (!dump_section_contents || decompressed_dumps)
     file->flags |= BFD_DECOMPRESS;
 
   /* If the file is an archive, process all of its elements.  */
@@ -5897,7 +5907,7 @@ main (int argc, char **argv)
   set_default_bfd_target ();
 
   while ((c = getopt_long (argc, argv,
-                          "CDE:FGHI:LM:P:RSTU:VW::ab:defghij:lm:prstvwxz",
+                          "CDE:FGHI:LM:P:RSTU:VW::Zab:defghij:lm:prstvwxz",
                           long_options, (int *) 0))
         != EOF)
     {
@@ -5908,6 +5918,9 @@ main (int argc, char **argv)
        case 'm':
          machine = optarg;
          break;
+       case 'Z':
+         decompressed_dumps = true;
+         break;
        case 'M':
          {
            char *options;
index 661ef0aab18cafb2eb23dee4f793741b9cd6eff0..775106fb99c5d8926c5e10a6e4e1f8662d60e8d4 100644 (file)
@@ -15961,35 +15961,18 @@ uncompress_section_contents (bool              is_zstd,
   return false;
 }
 
-static bool
-dump_section_as_strings (Elf_Internal_Shdr * section, Filedata * filedata)
+static uint64_t
+maybe_expand_or_relocate_section (Elf_Internal_Shdr *  section,
+                                 Filedata *           filedata,
+                                 unsigned char **     start_ptr,
+                                 bool                 relocate)
 {
-  Elf_Internal_Shdr *relsec;
-  uint64_t num_bytes;
-  unsigned char *data;
-  unsigned char *end;
-  unsigned char *real_start;
-  unsigned char *start;
-  bool some_strings_shown;
-
-  real_start = start = (unsigned char *) get_section_contents (section, filedata);
-  if (start == NULL)
-    /* PR 21820: Do not fail if the section was empty.  */
-    return section->sh_size == 0 || section->sh_type == SHT_NOBITS;
-
-  num_bytes = section->sh_size;
-
-  if (filedata->is_separate)
-    printf (_("\nString dump of section '%s' in linked file %s:\n"),
-           printable_section_name (filedata, section),
-           filedata->file_name);
-  else
-    printf (_("\nString dump of section '%s':\n"),
-           printable_section_name (filedata, section));
-
+  uint64_t         section_size = section->sh_size;
+  unsigned char *  start = * start_ptr;
+  
   if (decompress_dumps)
     {
-      uint64_t new_size = num_bytes;
+      uint64_t new_size = section_size;
       uint64_t uncompressed_size = 0;
       bool is_zstd = false;
 
@@ -15997,12 +15980,12 @@ dump_section_as_strings (Elf_Internal_Shdr * section, Filedata * filedata)
        {
          Elf_Internal_Chdr chdr;
          unsigned int compression_header_size
-           = get_compression_header (& chdr, (unsigned char *) start,
-                                     num_bytes);
+           = get_compression_header (& chdr, start, section_size);
+
          if (compression_header_size == 0)
            /* An error message will have already been generated
               by get_compression_header.  */
-           goto error_out;
+           return (uint64_t) -1;
 
          if (chdr.ch_type == ch_compress_zlib)
            ;
@@ -16014,8 +15997,9 @@ dump_section_as_strings (Elf_Internal_Shdr * section, Filedata * filedata)
            {
              warn (_("section '%s' has unsupported compress type: %d\n"),
                    printable_section_name (filedata, section), chdr.ch_type);
-             goto error_out;
+             return (uint64_t) -1;
            }
+
          uncompressed_size = chdr.ch_size;
          start += compression_header_size;
          new_size -= compression_header_size;
@@ -16041,38 +16025,86 @@ dump_section_as_strings (Elf_Internal_Shdr * section, Filedata * filedata)
        {
          if (uncompress_section_contents (is_zstd, &start, uncompressed_size,
                                           &new_size, filedata->file_size))
-           num_bytes = new_size;
+           section_size = new_size;
          else
            {
              error (_("Unable to decompress section %s\n"),
                     printable_section_name (filedata, section));
-             goto error_out;
+             return (uint64_t) -1;
            }
        }
       else
-       start = real_start;
+       start = * start_ptr;
+    }
+  else if (((section->sh_flags & SHF_COMPRESSED) != 0)
+          || (section_size > 12 && streq ((char *) start, "ZLIB")))
+    {
+      printf (_(" NOTE: This section is compressed, but its contents have NOT been expanded for this dump.\n"));
     }
 
-  /* 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.
-     FIXME: Maybe we ought to have an option that dumps a section with
-     relocs applied ?  */
-  for (relsec = filedata->section_headers;
-       relsec < filedata->section_headers + filedata->file_header.e_shnum;
-       ++relsec)
+  if (relocate)
     {
-      if ((relsec->sh_type != SHT_RELA && relsec->sh_type != SHT_REL)
-         || relsec->sh_info >= filedata->file_header.e_shnum
-         || filedata->section_headers + relsec->sh_info != section
-         || relsec->sh_size == 0
-         || relsec->sh_link >= filedata->file_header.e_shnum)
-       continue;
+      if (! apply_relocations (filedata, section, start, section_size, NULL, NULL))
+       return (uint64_t) -1;
+    }
+  else
+    {
+      Elf_Internal_Shdr *relsec;
 
-      printf (_("  Note: This section has relocations against it, but these have NOT been applied to this dump.\n"));
-      break;
+      /* 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.
+        FIXME: Maybe we ought to have an option that dumps a section with
+        relocs applied ?  */
+      for (relsec = filedata->section_headers;
+          relsec < filedata->section_headers + filedata->file_header.e_shnum;
+          ++relsec)
+       {
+         if ((relsec->sh_type != SHT_RELA && relsec->sh_type != SHT_REL)
+             || relsec->sh_info >= filedata->file_header.e_shnum
+             || filedata->section_headers + relsec->sh_info != section
+             || relsec->sh_size == 0
+             || relsec->sh_link >= filedata->file_header.e_shnum)
+           continue;
+
+         printf (_(" NOTE: This section has relocations against it, but these have NOT been applied to this dump.\n"));
+         break;
+       }
     }
 
+  * start_ptr = start;
+  return section_size;
+}
+
+static bool
+dump_section_as_strings (Elf_Internal_Shdr * section, Filedata * filedata)
+{
+  uint64_t num_bytes;
+  unsigned char *data;
+  unsigned char *end;
+  unsigned char *real_start;
+  unsigned char *start;
+  bool some_strings_shown;
+
+  real_start = start = (unsigned char *) get_section_contents (section, filedata);
+  if (start == NULL)
+    /* PR 21820: Do not fail if the section was empty.  */
+    return section->sh_size == 0 || section->sh_type == SHT_NOBITS;
+
+  num_bytes = section->sh_size;
+
+  if (filedata->is_separate)
+    printf (_("\nString dump of section '%s' in linked file %s:\n"),
+           printable_section_name (filedata, section),
+           filedata->file_name);
+  else
+    printf (_("\nString dump of section '%s':\n"),
+           printable_section_name (filedata, section));
+
+  num_bytes = maybe_expand_or_relocate_section (section, filedata, & start, false);
+  if (num_bytes == (uint64_t) -1)
+    goto error_out;
+
   data = start;
   end  = start + num_bytes;
   some_strings_shown = false;
@@ -16187,7 +16219,6 @@ dump_section_as_bytes (Elf_Internal_Shdr *section,
                       Filedata *filedata,
                       bool relocate)
 {
-  Elf_Internal_Shdr *relsec;
   size_t bytes;
   uint64_t section_size;
   uint64_t addr;
@@ -16210,102 +16241,9 @@ dump_section_as_bytes (Elf_Internal_Shdr *section,
     printf (_("\nHex dump of section '%s':\n"),
            printable_section_name (filedata, section));
 
-  if (decompress_dumps)
-    {
-      uint64_t new_size = section_size;
-      uint64_t uncompressed_size = 0;
-      bool is_zstd = false;
-
-      if ((section->sh_flags & SHF_COMPRESSED) != 0)
-       {
-         Elf_Internal_Chdr chdr;
-         unsigned int compression_header_size
-           = get_compression_header (& chdr, start, section_size);
-
-         if (compression_header_size == 0)
-           /* An error message will have already been generated
-              by get_compression_header.  */
-           goto error_out;
-
-         if (chdr.ch_type == ch_compress_zlib)
-           ;
-#ifdef HAVE_ZSTD
-         else if (chdr.ch_type == ch_compress_zstd)
-           is_zstd = true;
-#endif
-         else
-           {
-             warn (_("section '%s' has unsupported compress type: %d\n"),
-                   printable_section_name (filedata, section), chdr.ch_type);
-             goto error_out;
-           }
-         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)
-       {
-         if (uncompress_section_contents (is_zstd, &start, uncompressed_size,
-                                          &new_size, filedata->file_size))
-           {
-             section_size = new_size;
-           }
-         else
-           {
-             error (_("Unable to decompress section %s\n"),
-                    printable_section_name (filedata, section));
-             /* FIXME: Print the section anyway ?  */
-             goto error_out;
-           }
-       }
-      else
-       start = real_start;
-    }
-
-  if (relocate)
-    {
-      if (! apply_relocations (filedata, section, start, section_size, NULL, NULL))
-       goto error_out;
-    }
-  else
-    {
-      /* 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.
-        FIXME: Maybe we ought to have an option that dumps a section with
-        relocs applied ?  */
-      for (relsec = filedata->section_headers;
-          relsec < filedata->section_headers + filedata->file_header.e_shnum;
-          ++relsec)
-       {
-         if ((relsec->sh_type != SHT_RELA && relsec->sh_type != SHT_REL)
-             || relsec->sh_info >= filedata->file_header.e_shnum
-             || filedata->section_headers + relsec->sh_info != section
-             || relsec->sh_size == 0
-             || relsec->sh_link >= filedata->file_header.e_shnum)
-           continue;
-
-         printf (_(" NOTE: This section has relocations against it, but these have NOT been applied to this dump.\n"));
-         break;
-       }
-    }
+  section_size = maybe_expand_or_relocate_section (section, filedata, & start, relocate);
+  if (section_size == (uint64_t) -1)
+    goto error_out;
 
   addr = section->sh_addr;
   bytes = section_size;
diff --git a/binutils/testsuite/binutils-all/objdump.Zs b/binutils/testsuite/binutils-all/objdump.Zs
new file mode 100644 (file)
index 0000000..32905b5
--- /dev/null
@@ -0,0 +1,9 @@
+
+.*dw2-compressed.o:     file format .*
+
+Contents of section .zdebug_abbrev:
+ 0000 01110110 06120111 01030825 08130b00  ...........%....
+ 0010 00022e00 3f0c3a0b 3b0b0308 49131101  ....\?.:.;...I...
+ 0020 1201400a 00000324 0003080b 0b3e0b00  ..@....\$.....>..
+ 0030 000000                               ...             
+#pass
index 4fe9e499d49f49f0d64016bd424257581f6806ac..7071e965ca3abb427a3b4ea78f7c72806a6ce9ee 100644 (file)
@@ -466,6 +466,22 @@ if { ![is_elf_format] } then {
     } else {
        pass "objdump -W"
     }
+
+    # Test objdump -Z -s on a file that contains some compressed .debug sections
+
+    set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Z -s -j .zdebug_abbrev $compressed_testfile" "" "/dev/null" "objdump.out"]
+
+    if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then {
+       fail "objdump -Z -s (reason: unexpected output)"
+       send_log $got
+       send_log "\n"
+    }
+
+    if { [regexp_diff objdump.out $srcdir/$subdir/objdump.Zs] } then {
+       fail "objdump -Z -s"
+    } else {
+       pass "objdump -Z -s"
+    }
 }
 
 # Test objdump -WL on a file that contains line information for multiple files and search directories.
index aea35dfa5a6b4ebca1a1684f089fe9d47f4ab742..f80f8c54a6114c2be41771950798dba22c9ce39d 100644 (file)
@@ -2,6 +2,7 @@
 .*dw2-compressed.o:     file format .*
 
 Contents of section .zdebug_abbrev:
+ NOTE: This section is compressed, but its contents have NOT been expanded for this dump.
  0000 5a4c4942 00000000 00000033 785e6314  ZLIB.......3x\^c.
  0010 64146013 62146464 e650e510 e6666060  d.`.b.dd.P...f``
  0020 d263b0e7 b1e2b6e6 66e6f014 16641462  .c......f....d.b