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