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.
This commit is contained in:
parent
319b460545
commit
fab62191f8
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* 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 @@ 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;
|
||||
|
9
binutils/testsuite/binutils-all/objdump.Zs
Normal file
9
binutils/testsuite/binutils-all/objdump.Zs
Normal file
@ -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
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user