Improve objdump's handling of compressed sections.

  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.
diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index 57a8fa7..d695d3b 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -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,
diff --git a/binutils/NEWS b/binutils/NEWS
index 1aae340..3bf3b56 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -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
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index f946936..ec23a78 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2269,6 +2269,7 @@
         [@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 @@
 @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 @@
 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
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 6081342..640ccb5 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -137,6 +137,7 @@
 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 @@
   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 @@
 #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 @@
       comma = ", ";
     }
 
+  if (bfd_is_section_compressed (abfd, section))
+    printf ("%sCOMPRESSED", comma);
+
   printf ("\n");
 #undef PF
 }
@@ -5024,6 +5031,9 @@
 	    (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 @@
 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 @@
   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 @@
 	case 'm':
 	  machine = optarg;
 	  break;
+	case 'Z':
+	  decompressed_dumps = true;
+	  break;
 	case 'M':
 	  {
 	    char *options;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 661ef0a..775106f 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -15961,35 +15961,18 @@
   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 @@
 	{
 	  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 @@
 	    {
 	      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 @@
 	{
 	  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;
     }
-
-  /* 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)
+  else if (((section->sh_flags & SHF_COMPRESSED) != 0)
+	   || (section_size > 12 && streq ((char *) start, "ZLIB")))
     {
-      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;
+      printf (_(" NOTE: This section is compressed, but its contents have NOT been expanded for this dump.\n"));
     }
 
+  if (relocate)
+    {
+      if (! apply_relocations (filedata, section, start, section_size, NULL, NULL))
+	return (uint64_t) -1;
+    }
+  else
+    {
+      Elf_Internal_Shdr *relsec;
+
+      /* 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 @@
 		       Filedata *filedata,
 		       bool relocate)
 {
-  Elf_Internal_Shdr *relsec;
   size_t bytes;
   uint64_t section_size;
   uint64_t addr;
@@ -16210,102 +16241,9 @@
     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
index 0000000..32905b5
--- /dev/null
+++ b/binutils/testsuite/binutils-all/objdump.Zs
@@ -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
diff --git a/binutils/testsuite/binutils-all/objdump.exp b/binutils/testsuite/binutils-all/objdump.exp
index 4fe9e499..7071e96 100644
--- a/binutils/testsuite/binutils-all/objdump.exp
+++ b/binutils/testsuite/binutils-all/objdump.exp
@@ -466,6 +466,22 @@
     } 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.
diff --git a/binutils/testsuite/binutils-all/objdump.s b/binutils/testsuite/binutils-all/objdump.s
index aea35df..f80f8c5 100644
--- a/binutils/testsuite/binutils-all/objdump.s
+++ b/binutils/testsuite/binutils-all/objdump.s
@@ -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