forked from Imagelibrary/binutils-gdb
Enhance objdump's --private option so that it can display the contents of PE format files.
* od-pe.c: New file: Dumps fields in PE format headers. * configure.ac (od_vectors): Add objdump_private_desc_pe for PE format targets. (od_files): Add od-pe for PE format targets. * configure: Regenerate. * Makefile.am (CFILES): Add od-pe.c (EXTRA_objdump_SOURCE): Likewise. * Makefile.in: Generate. * NEWS: Mention the new feature. * doc/binutils.texi: Document the new support. * objdump.c (wide_output): Change from local to global. * objdump.h (wide_output): Prototype. (objdump_private_desc_pe): Prototype. * testsuite/binutils-all/objdump.exp: Add a test of the new feature.
This commit is contained in:
@@ -1,3 +1,20 @@
|
||||
2023-05-26 Nick Clifton <nickc@redhat.com>
|
||||
|
||||
* od-pe.c: New file: Dumps fields in PE format headers.
|
||||
* configure.ac (od_vectors): Add objdump_private_desc_pe for PE
|
||||
format targets.
|
||||
(od_files): Add od-pe for PE format targets.
|
||||
* configure: Regenerate.
|
||||
* Makefile.am (CFILES): Add od-pe.c
|
||||
(EXTRA_objdump_SOURCE): Likewise.
|
||||
* Makefile.in: Generate.
|
||||
* NEWS: Mention the new feature.
|
||||
* doc/binutils.texi: Document the new support.
|
||||
* objdump.c (wide_output): Change from local to global.
|
||||
* objdump.h (wide_output): Prototype.
|
||||
(objdump_private_desc_pe): Prototype.
|
||||
* testsuite/binutils-all/objdump.exp: Add a test of the new feature.
|
||||
|
||||
2023-05-09 Enze Li <enze.li@gmx.com>
|
||||
|
||||
* README: Correct a typo.
|
||||
|
||||
@@ -140,7 +140,7 @@ CFILES = \
|
||||
is-ranlib.c is-strip.c maybe-ranlib.c maybe-strip.c \
|
||||
nm.c not-ranlib.c not-strip.c \
|
||||
objcopy.c objdump.c prdbg.c \
|
||||
od-elf32_avr.c od-macho.c od-xcoff.c \
|
||||
od-elf32_avr.c od-macho.c od-pe.c od-xcoff.c \
|
||||
rclex.c rdcoff.c rddbg.c readelf.c rename.c \
|
||||
resbin.c rescoff.c resrc.c resres.c \
|
||||
size.c srconv.c stabs.c strings.c sysdump.c \
|
||||
@@ -270,7 +270,7 @@ strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
|
||||
nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
|
||||
|
||||
objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
|
||||
EXTRA_objdump_SOURCES = od-xcoff.c
|
||||
EXTRA_objdump_SOURCES = od-elf32_avr.c od-macho.c od-xcoff.c od-pe.c
|
||||
objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBSFRAME)
|
||||
|
||||
objdump.@OBJEXT@:objdump.c
|
||||
|
||||
@@ -715,7 +715,7 @@ CFILES = \
|
||||
is-ranlib.c is-strip.c maybe-ranlib.c maybe-strip.c \
|
||||
nm.c not-ranlib.c not-strip.c \
|
||||
objcopy.c objdump.c prdbg.c \
|
||||
od-elf32_avr.c od-macho.c od-xcoff.c \
|
||||
od-elf32_avr.c od-macho.c od-pe.c od-xcoff.c \
|
||||
rclex.c rdcoff.c rddbg.c readelf.c rename.c \
|
||||
resbin.c rescoff.c resrc.c resres.c \
|
||||
size.c srconv.c stabs.c strings.c sysdump.c \
|
||||
@@ -797,7 +797,7 @@ elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
|
||||
strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
|
||||
nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
|
||||
objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
|
||||
EXTRA_objdump_SOURCES = od-xcoff.c
|
||||
EXTRA_objdump_SOURCES = od-elf32_avr.c od-macho.c od-xcoff.c od-pe.c
|
||||
objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBSFRAME)
|
||||
cxxfilt_SOURCES = cxxfilt.c $(BULIBS)
|
||||
ar_SOURCES = arparse.y arlex.l ar.c not-ranlib.c arsup.c rename.c binemul.c \
|
||||
@@ -1181,6 +1181,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/objdump.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/od-elf32_avr.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/od-macho.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/od-pe.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/od-xcoff.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prdbg.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rclex.Po@am__quote@
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
-*- text -*-
|
||||
|
||||
* Objdump's --private option can now be used on PE format files to display the
|
||||
fields in the file header and section headers.
|
||||
|
||||
Changes in 2.40:
|
||||
|
||||
* Objdump has a new command line option --show-all-symbols which will make it
|
||||
|
||||
7
binutils/configure
vendored
7
binutils/configure
vendored
@@ -14571,7 +14571,7 @@ do
|
||||
fi
|
||||
DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_I386"
|
||||
BUILD_DLLWRAP='$(DLLWRAP_PROG)$(EXEEXT)'
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff"
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff objdump_private_desc_pe"
|
||||
else
|
||||
case $targ in
|
||||
*-*-hms*) BUILD_SRCONV='$(SRCONV_PROG)' ;;
|
||||
@@ -14685,6 +14685,9 @@ do
|
||||
powerpc*-*-aix* | rs6000-*-aix*)
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff"
|
||||
;;
|
||||
*-*-pe* | *-*-cygwin* | *-*-mingw*)
|
||||
od_vectors="$od_vectors objdump_private_desc_pe"
|
||||
;;
|
||||
*-*-darwin*)
|
||||
od_vectors="$od_vectors objdump_private_desc_mach_o"
|
||||
;;
|
||||
@@ -14706,6 +14709,8 @@ for i in $od_vectors ; do
|
||||
od_files="$od_files od-elf32_avr" ;;
|
||||
objdump_private_desc_xcoff)
|
||||
od_files="$od_files od-xcoff" ;;
|
||||
objdump_private_desc_pe)
|
||||
od_files="$od_files od-pe" ;;
|
||||
objdump_private_desc_mach_o)
|
||||
od_files="$od_files od-macho" ;;
|
||||
*) as_fn_error $? "*** unknown private vector $i" "$LINENO" 5 ;;
|
||||
|
||||
@@ -348,7 +348,7 @@ do
|
||||
fi
|
||||
DLLTOOL_DEFS="$DLLTOOL_DEFS -DDLLTOOL_I386"
|
||||
BUILD_DLLWRAP='$(DLLWRAP_PROG)$(EXEEXT)'
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff"
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff objdump_private_desc_pe"
|
||||
else
|
||||
case $targ in
|
||||
*-*-hms*) BUILD_SRCONV='$(SRCONV_PROG)' ;;
|
||||
@@ -470,6 +470,9 @@ changequote([,])dnl
|
||||
powerpc*-*-aix* | rs6000-*-aix*)
|
||||
od_vectors="$od_vectors objdump_private_desc_xcoff"
|
||||
;;
|
||||
*-*-pe* | *-*-cygwin* | *-*-mingw*)
|
||||
od_vectors="$od_vectors objdump_private_desc_pe"
|
||||
;;
|
||||
*-*-darwin*)
|
||||
od_vectors="$od_vectors objdump_private_desc_mach_o"
|
||||
;;
|
||||
@@ -491,6 +494,8 @@ for i in $od_vectors ; do
|
||||
od_files="$od_files od-elf32_avr" ;;
|
||||
objdump_private_desc_xcoff)
|
||||
od_files="$od_files od-xcoff" ;;
|
||||
objdump_private_desc_pe)
|
||||
od_files="$od_files od-pe" ;;
|
||||
objdump_private_desc_mach_o)
|
||||
od_files="$od_files od-macho" ;;
|
||||
*) AC_MSG_ERROR(*** unknown private vector $i) ;;
|
||||
|
||||
@@ -2754,6 +2754,12 @@ For XCOFF, the available options are:
|
||||
@item ldinfo
|
||||
@end table
|
||||
|
||||
For PE, the available options are:
|
||||
@table @code
|
||||
@item header
|
||||
@item sections
|
||||
@end table
|
||||
|
||||
Not all object formats support this option. In particular the ELF
|
||||
format does not use it.
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ static bool disassemble; /* -d */
|
||||
static bool disassemble_all; /* -D */
|
||||
static int disassemble_zeroes; /* --disassemble-zeroes */
|
||||
static bool formats_info; /* -i */
|
||||
static int wide_output; /* -w */
|
||||
int wide_output; /* -w */
|
||||
static int insn_width; /* --insn-width */
|
||||
static bfd_vma start_address = (bfd_vma) -1; /* --start-address */
|
||||
static bfd_vma stop_address = (bfd_vma) -1; /* --stop-address */
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
||||
MA 02110-1301, USA. */
|
||||
|
||||
/* Non-zero if wide output has been enabled. */
|
||||
extern int wide_output;
|
||||
|
||||
struct objdump_private_option
|
||||
{
|
||||
/* Option name. */
|
||||
@@ -43,11 +46,15 @@ struct objdump_private_desc
|
||||
/* List of options. Terminated by a NULL name. */
|
||||
struct objdump_private_option *options;
|
||||
};
|
||||
|
||||
/* ELF32_AVR specific target. */
|
||||
extern const struct objdump_private_desc objdump_private_desc_elf32_avr;
|
||||
|
||||
/* XCOFF specific target. */
|
||||
extern const struct objdump_private_desc objdump_private_desc_xcoff;
|
||||
|
||||
/* PE specific target. */
|
||||
extern const struct objdump_private_desc objdump_private_desc_pe;
|
||||
|
||||
/* Mach-O specific target. */
|
||||
extern const struct objdump_private_desc objdump_private_desc_mach_o;
|
||||
|
||||
565
binutils/od-pe.c
Normal file
565
binutils/od-pe.c
Normal file
@@ -0,0 +1,565 @@
|
||||
/* od-pe.c -- dump information about a PE object file.
|
||||
Copyright (C) 2011-2023 Free Software Foundation, Inc.
|
||||
Written by Tristan Gingold, Adacore and Nick Clifton, Red Hat.
|
||||
|
||||
This file is part of GNU Binutils.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
||||
MA 02110-1301, USA. */
|
||||
|
||||
#include "sysdep.h"
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include "safe-ctype.h"
|
||||
#include "bfd.h"
|
||||
#include "objdump.h"
|
||||
#include "bucomm.h"
|
||||
#include "bfdlink.h"
|
||||
#include "coff/internal.h"
|
||||
#define L_LNNO_SIZE 4 /* FIXME: which value should we use ? */
|
||||
#include "coff/external.h"
|
||||
#include "coff/pe.h"
|
||||
#include "libcoff.h"
|
||||
#include "libpei.h"
|
||||
|
||||
/* Index of the options in the options[] array. */
|
||||
#define OPT_FILE_HEADER 0
|
||||
#define OPT_AOUT 1
|
||||
#define OPT_SECTIONS 2
|
||||
#define OPT_SYMS 3
|
||||
#define OPT_RELOCS 4
|
||||
#define OPT_LINENO 5
|
||||
#define OPT_LOADER 6
|
||||
#define OPT_EXCEPT 7
|
||||
#define OPT_TYPCHK 8
|
||||
#define OPT_TRACEBACK 9
|
||||
#define OPT_TOC 10
|
||||
#define OPT_LDINFO 11
|
||||
|
||||
/* List of actions. */
|
||||
static struct objdump_private_option options[] =
|
||||
{
|
||||
{ "header", 0 },
|
||||
{ "aout", 0 },
|
||||
{ "sections", 0 },
|
||||
{ "syms", 0 },
|
||||
{ "relocs", 0 },
|
||||
{ "lineno", 0 },
|
||||
{ "loader", 0 },
|
||||
{ "except", 0 },
|
||||
{ "typchk", 0 },
|
||||
{ "traceback", 0 },
|
||||
{ "toc", 0 },
|
||||
{ "ldinfo", 0 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/* Simplified section header. */
|
||||
struct pe_section
|
||||
{
|
||||
/* NUL terminated name. */
|
||||
char name[9];
|
||||
|
||||
/* Section flags. */
|
||||
unsigned int flags;
|
||||
|
||||
/* Offsets in file. */
|
||||
ufile_ptr scnptr;
|
||||
ufile_ptr relptr;
|
||||
ufile_ptr lnnoptr;
|
||||
|
||||
/* Number of relocs and line numbers. */
|
||||
unsigned int nreloc;
|
||||
unsigned int nlnno;
|
||||
};
|
||||
|
||||
/* Translation entry type. The last entry must be {0, NULL}. */
|
||||
|
||||
struct xlat_table
|
||||
{
|
||||
unsigned int val;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
/* PE file flags. */
|
||||
static const struct xlat_table file_flag_xlat[] =
|
||||
{
|
||||
{ IMAGE_FILE_RELOCS_STRIPPED, "RELOCS STRIPPED"},
|
||||
{ IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE"},
|
||||
{ IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE NUMS STRIPPED"},
|
||||
{ IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL SYMS STRIPPED"},
|
||||
{ IMAGE_FILE_AGGRESSIVE_WS_TRIM, "AGGRESSIVE WS TRIM"},
|
||||
{ IMAGE_FILE_LARGE_ADDRESS_AWARE, "LARGE ADDRESS AWARE"},
|
||||
{ IMAGE_FILE_16BIT_MACHINE, "16BIT MACHINE"},
|
||||
{ IMAGE_FILE_BYTES_REVERSED_LO, "BYTES REVERSED LO"},
|
||||
{ IMAGE_FILE_32BIT_MACHINE, "32BIT MACHINE"},
|
||||
{ IMAGE_FILE_DEBUG_STRIPPED, "DEBUG STRIPPED"},
|
||||
{ IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "REMOVABLE RUN FROM SWAP"},
|
||||
{ IMAGE_FILE_NET_RUN_FROM_SWAP, "NET RUN FROM SWAP"},
|
||||
{ IMAGE_FILE_SYSTEM, "SYSTEM"},
|
||||
{ IMAGE_FILE_DLL, "DLL"},
|
||||
{ IMAGE_FILE_UP_SYSTEM_ONLY, "UP SYSTEM ONLY"},
|
||||
{ IMAGE_FILE_BYTES_REVERSED_HI, "BYTES REVERSED HI"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* PE section flags. */
|
||||
static const struct xlat_table section_flag_xlat[] =
|
||||
{
|
||||
{ IMAGE_SCN_MEM_DISCARDABLE, "DISCARDABLE" },
|
||||
{ IMAGE_SCN_MEM_EXECUTE, "EXECUTE" },
|
||||
{ IMAGE_SCN_MEM_READ, "READ" },
|
||||
{ IMAGE_SCN_MEM_WRITE, "WRITE" },
|
||||
{ IMAGE_SCN_TYPE_NO_PAD, "NO PAD" },
|
||||
{ IMAGE_SCN_CNT_CODE, "CODE" },
|
||||
{ IMAGE_SCN_CNT_INITIALIZED_DATA, "INITIALIZED DATA" },
|
||||
{ IMAGE_SCN_CNT_UNINITIALIZED_DATA, "UNINITIALIZED DATA" },
|
||||
{ IMAGE_SCN_LNK_OTHER, "OTHER" },
|
||||
{ IMAGE_SCN_LNK_INFO, "INFO" },
|
||||
{ IMAGE_SCN_LNK_REMOVE, "REMOVE" },
|
||||
{ IMAGE_SCN_LNK_COMDAT, "COMDAT" },
|
||||
{ IMAGE_SCN_MEM_FARDATA, "FARDATA" },
|
||||
{ IMAGE_SCN_MEM_PURGEABLE, "PURGEABLE" },
|
||||
{ IMAGE_SCN_MEM_LOCKED, "LOCKED" },
|
||||
{ IMAGE_SCN_MEM_PRELOAD, "PRELOAD" },
|
||||
{ IMAGE_SCN_LNK_NRELOC_OVFL, "NRELOC OVFL" },
|
||||
{ IMAGE_SCN_MEM_NOT_CACHED, "NOT CACHED" },
|
||||
{ IMAGE_SCN_MEM_NOT_PAGED, "NOT PAGED" },
|
||||
{ IMAGE_SCN_MEM_SHARED, "SHARED" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
/* Display help. */
|
||||
|
||||
static void
|
||||
pe_help (FILE *stream)
|
||||
{
|
||||
fprintf (stream, _("\
|
||||
For PE files:\n\
|
||||
header Display the file header\n\
|
||||
sections Display the section headers\n\
|
||||
"));
|
||||
}
|
||||
|
||||
/* Return true if ABFD is handled. */
|
||||
|
||||
static int
|
||||
pe_filter (bfd *abfd)
|
||||
{
|
||||
return bfd_get_flavour (abfd) == bfd_target_coff_flavour;
|
||||
}
|
||||
|
||||
/* Display the list of name (from TABLE) for FLAGS, using comma to
|
||||
separate them. A name is displayed if FLAGS & VAL is not 0. */
|
||||
|
||||
static void
|
||||
dump_flags (const struct xlat_table * table, unsigned int flags)
|
||||
{
|
||||
unsigned int r = flags;
|
||||
bool first = true;
|
||||
const struct xlat_table *t;
|
||||
|
||||
for (t = table; t->name; t++)
|
||||
if ((flags & t->val) != 0)
|
||||
{
|
||||
r &= ~t->val;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
putchar (',');
|
||||
fputs (t->name, stdout);
|
||||
}
|
||||
|
||||
/* Undecoded flags. */
|
||||
if (r != 0)
|
||||
{
|
||||
if (!first)
|
||||
putchar (',');
|
||||
printf (_("unknown: 0x%x"), r);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
decode_machine_number (unsigned int machine)
|
||||
{
|
||||
switch (machine)
|
||||
{
|
||||
case IMAGE_FILE_MACHINE_ALPHA: return "ALPHA";
|
||||
case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
|
||||
case IMAGE_FILE_MACHINE_ARM: return "ARM";
|
||||
case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
|
||||
case IMAGE_FILE_MACHINE_I386: return "I386";
|
||||
case IMAGE_FILE_MACHINE_IA64: return "IA64";
|
||||
case IMAGE_FILE_MACHINE_LOONGARCH64: return "LOONGARCH64";
|
||||
case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
|
||||
case 0x0500: return "SH (big endian)";
|
||||
case 0x0550: return "SH (little endian)";
|
||||
case 0x0b00: return "MCore";
|
||||
case 0x0093: return "TI C4X";
|
||||
// FIXME: Add more machine numbers.
|
||||
default: return N_("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the file header. */
|
||||
|
||||
static void
|
||||
dump_pe_file_header (bfd * abfd,
|
||||
struct external_PEI_filehdr * fhdr,
|
||||
struct external_PEI_IMAGE_hdr * ihdr)
|
||||
{
|
||||
unsigned long ihdr_off = 0;
|
||||
|
||||
if (fhdr != NULL)
|
||||
{
|
||||
printf (_("\nFile Header:\n"));
|
||||
|
||||
/* FIXME: The fields in the file header are boring an generally have
|
||||
fixed values. Is there any benefit in displaying them ? */
|
||||
|
||||
/* Display the first string found in the stub.
|
||||
FIXME: Look for more than one string ?
|
||||
FIXME: Strictly speaking we may not have read the full stub, since
|
||||
it can be longer than the dos_message array in the PEI_fileheader
|
||||
structure. */
|
||||
const unsigned char * message = (const unsigned char *) fhdr->dos_message;
|
||||
unsigned int len = sizeof (fhdr->dos_message);
|
||||
unsigned int i;
|
||||
unsigned int seen_count = 0;
|
||||
unsigned int string_start = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (ISPRINT (message[i]))
|
||||
{
|
||||
if (string_start == 0)
|
||||
string_start = i;
|
||||
++ seen_count;
|
||||
if (seen_count > 4)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
seen_count = string_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (seen_count > 4)
|
||||
{
|
||||
printf (_(" Stub message: "));
|
||||
while (string_start < len)
|
||||
{
|
||||
char c = message[string_start ++];
|
||||
if (! ISPRINT (c))
|
||||
break;
|
||||
putchar (c);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
ihdr_off = (long) bfd_h_get_32 (abfd, fhdr->e_lfanew);
|
||||
}
|
||||
|
||||
printf (_("\nImage Header (at offset %#lx):\n"), ihdr_off);
|
||||
|
||||
unsigned int machine = (int) bfd_h_get_16 (abfd, ihdr->f_magic);
|
||||
printf (_(" Machine Num: %#x\t\t- %s\n"), machine,
|
||||
decode_machine_number (machine));
|
||||
|
||||
printf (_(" Num sections: %d\n"), (int) bfd_h_get_16 (abfd, ihdr->f_nscns));
|
||||
|
||||
long timedat = bfd_h_get_32 (abfd, ihdr->f_timdat);
|
||||
printf (_(" Time and date: %#08lx\t- "), timedat);
|
||||
if (timedat == 0)
|
||||
printf (_("not set\n"));
|
||||
else
|
||||
{
|
||||
/* Not correct on all platforms, but works on unix. */
|
||||
time_t t = timedat;
|
||||
fputs (ctime (& t), stdout);
|
||||
}
|
||||
|
||||
printf (_(" Symbols off: %#08lx\n"),
|
||||
(long) bfd_h_get_32 (abfd, ihdr->f_symptr));
|
||||
printf (_(" Num symbols: %ld\n"),
|
||||
(long) bfd_h_get_32 (abfd, ihdr->f_nsyms));
|
||||
|
||||
unsigned int opt_header_size = (int) bfd_h_get_16 (abfd, ihdr->f_opthdr);
|
||||
printf (_(" Opt hdr sz: %#x\n"), opt_header_size);
|
||||
|
||||
unsigned int flags = (int) bfd_h_get_16 (abfd, ihdr->f_flags);
|
||||
printf (_(" Flags: 0x%04x\t\t- "), flags);
|
||||
dump_flags (file_flag_xlat, flags);
|
||||
putchar ('\n');
|
||||
|
||||
if (opt_header_size == PEPAOUTSZ)
|
||||
{
|
||||
PEPAOUTHDR xhdr;
|
||||
|
||||
printf (_("\nOptional PE+ Header (at offset %#lx):\n"),
|
||||
ihdr_off + sizeof (* ihdr));
|
||||
|
||||
if (bfd_seek (abfd, ihdr_off + sizeof (* ihdr), SEEK_SET) != 0
|
||||
|| bfd_bread (& xhdr, sizeof (xhdr), abfd) != sizeof (xhdr))
|
||||
printf ("error: unable to read PE+ header\n");
|
||||
else
|
||||
{
|
||||
/* FIXME: Check that the magic number is 0x020b ? */
|
||||
printf (_(" Magic: %x\n"),
|
||||
(int) bfd_h_get_16 (abfd, xhdr.standard.magic));
|
||||
printf (_(" Image Base: %lx\n"),
|
||||
(long) bfd_h_get_64 (abfd, xhdr.ImageBase));
|
||||
/* FIXME: Print more fields. */
|
||||
}
|
||||
}
|
||||
else if (opt_header_size == AOUTSZ)
|
||||
{
|
||||
PEAOUTHDR xhdr;
|
||||
|
||||
printf (_("\nOptional PE Header (at offset %#lx):\n"),
|
||||
ihdr_off + sizeof (* ihdr));
|
||||
|
||||
if (bfd_seek (abfd, ihdr_off + sizeof (* ihdr), SEEK_SET) != 0
|
||||
|| bfd_bread (& xhdr, sizeof (xhdr), abfd) != sizeof (xhdr))
|
||||
printf ("error: unable to read PE+ header\n");
|
||||
else
|
||||
{
|
||||
/* FIXME: Check that the magic number is 0x010b ? */
|
||||
printf (_(" Magic: %x\n"),
|
||||
(int) bfd_h_get_16 (abfd, xhdr.standard.magic));
|
||||
printf (_(" Image Base: %lx\n"),
|
||||
(long) bfd_h_get_32 (abfd, xhdr.ImageBase));
|
||||
/* FIXME: Print more fields. */
|
||||
}
|
||||
}
|
||||
else if (opt_header_size != 0)
|
||||
{
|
||||
printf (_("\nUnsupported size of Optional Header\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the sections header. */
|
||||
|
||||
static void
|
||||
dump_pe_sections_header (bfd * abfd,
|
||||
struct external_PEI_filehdr * fhdr,
|
||||
struct external_PEI_IMAGE_hdr * ihdr)
|
||||
{
|
||||
unsigned int opthdr = (int) bfd_h_get_16 (abfd, ihdr->f_opthdr);
|
||||
unsigned int n_scns = (int) bfd_h_get_16 (abfd, ihdr->f_nscns);
|
||||
unsigned int off;
|
||||
|
||||
/* The section header starts after the file, image and optional headers. */
|
||||
if (fhdr == NULL)
|
||||
off = sizeof (struct external_filehdr) + opthdr;
|
||||
else
|
||||
off = (int) bfd_h_get_16 (abfd, fhdr->e_lfanew) + sizeof (* ihdr) + opthdr;
|
||||
|
||||
printf (_("\nSection headers (at offset 0x%08x):\n"), off);
|
||||
|
||||
if (n_scns == 0)
|
||||
{
|
||||
printf (_(" No section headers\n"));
|
||||
return;
|
||||
}
|
||||
if (bfd_seek (abfd, off, SEEK_SET) != 0)
|
||||
{
|
||||
non_fatal (_("cannot seek to section headers start\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* We don't translate this string as it consists of field names. */
|
||||
if (wide_output)
|
||||
printf (" # Name paddr vaddr size scnptr relptr lnnoptr nrel nlnno Flags\n");
|
||||
else
|
||||
printf (" # Name paddr vaddr size scnptr relptr lnnoptr nrel nlnno\n");
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < n_scns; i++)
|
||||
{
|
||||
struct external_scnhdr scn;
|
||||
unsigned int flags;
|
||||
|
||||
if (bfd_bread (& scn, sizeof (scn), abfd) != sizeof (scn))
|
||||
{
|
||||
non_fatal (_("cannot read section header"));
|
||||
return;
|
||||
}
|
||||
|
||||
printf ("%2d %-8.8s %08x %08x %08x %08x %08x %08x %5d %5d",
|
||||
i + 1, scn.s_name,
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_paddr),
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_vaddr),
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_size),
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_scnptr),
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_relptr),
|
||||
(unsigned int) bfd_h_get_32 (abfd, scn.s_lnnoptr),
|
||||
(unsigned int) bfd_h_get_16 (abfd, scn.s_nreloc),
|
||||
(unsigned int) bfd_h_get_16 (abfd, scn.s_nlnno));
|
||||
|
||||
flags = bfd_h_get_32 (abfd, scn.s_flags);
|
||||
if (wide_output)
|
||||
printf (_(" %08x "), flags);
|
||||
else
|
||||
printf (_("\n Flags: %08x: "), flags);
|
||||
|
||||
if (flags != 0)
|
||||
{
|
||||
/* Skip the alignment bits. */
|
||||
flags &= ~ IMAGE_SCN_ALIGN_POWER_BIT_MASK;
|
||||
dump_flags (section_flag_xlat, flags);
|
||||
}
|
||||
|
||||
putchar ('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle a PE format file. */
|
||||
|
||||
static void
|
||||
dump_pe (bfd * abfd,
|
||||
struct external_PEI_filehdr * fhdr,
|
||||
struct external_PEI_IMAGE_hdr * ihdr)
|
||||
{
|
||||
if (options[OPT_FILE_HEADER].selected)
|
||||
dump_pe_file_header (abfd, fhdr, ihdr);
|
||||
|
||||
if (options[OPT_SECTIONS].selected)
|
||||
dump_pe_sections_header (abfd, fhdr, ihdr);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_pe_object_magic (unsigned short magic)
|
||||
{
|
||||
switch (magic)
|
||||
{
|
||||
case IMAGE_FILE_MACHINE_ALPHA:
|
||||
case IMAGE_FILE_MACHINE_ARM:
|
||||
case IMAGE_FILE_MACHINE_ARM64:
|
||||
case IMAGE_FILE_MACHINE_I386:
|
||||
case IMAGE_FILE_MACHINE_IA64:
|
||||
case IMAGE_FILE_MACHINE_POWERPC:
|
||||
case IMAGE_FILE_MACHINE_LOONGARCH64:
|
||||
case IMAGE_FILE_MACHINE_AMD64:
|
||||
// FIXME: Add more machine numbers.
|
||||
return true;
|
||||
case 0x0500: /* SH_ARCH_MAGIC_BIG */
|
||||
case 0x0550: /* SH_ARCH_MAGIC_LITTLE */
|
||||
case 0x0b00: /* MCore */
|
||||
case 0x0093: /* TI C4x */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump ABFD (according to the options[] array). */
|
||||
|
||||
static void
|
||||
pe_dump_obj (bfd *abfd)
|
||||
{
|
||||
struct external_PEI_filehdr fhdr;
|
||||
|
||||
/* Read file header. */
|
||||
if (bfd_seek (abfd, 0, SEEK_SET) != 0
|
||||
|| bfd_bread (& fhdr, sizeof (fhdr), abfd) != sizeof (fhdr))
|
||||
{
|
||||
non_fatal (_("cannot seek to/read file header"));
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned short magic = bfd_h_get_16 (abfd, fhdr.e_magic);
|
||||
|
||||
/* PE format executable files have a full external_PEI_filehdr structure
|
||||
at the start. PE format object files just have an external_filehdr
|
||||
structure at the start. */
|
||||
if (magic == IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
unsigned int ihdr_offset = (int) bfd_h_get_16 (abfd, fhdr.e_lfanew);
|
||||
|
||||
/* FIXME: We could reuse the fields in fhdr, but that might
|
||||
confuse various sanitization and memory checker tools. */
|
||||
struct external_PEI_IMAGE_hdr ihdr;
|
||||
|
||||
if (bfd_seek (abfd, ihdr_offset, SEEK_SET) != 0
|
||||
|| bfd_bread (& ihdr, sizeof (ihdr), abfd) != sizeof (ihdr))
|
||||
{
|
||||
non_fatal (_("cannot seek to/read image header at offset %#x"),
|
||||
ihdr_offset);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int signature = (int) bfd_h_get_16 (abfd, ihdr.nt_signature);
|
||||
if (signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
non_fatal ("file does not have an NT format signature: %#x",
|
||||
signature);
|
||||
return;
|
||||
}
|
||||
|
||||
dump_pe (abfd, & fhdr, & ihdr);
|
||||
}
|
||||
else if (is_pe_object_magic (magic))
|
||||
{
|
||||
struct external_filehdr ehdr;
|
||||
|
||||
if (bfd_seek (abfd, 0, SEEK_SET) != 0
|
||||
|| bfd_bread (& ehdr, sizeof (ehdr), abfd) != sizeof (ehdr))
|
||||
{
|
||||
non_fatal (_("cannot seek to/read image header"));
|
||||
return;
|
||||
}
|
||||
|
||||
struct external_PEI_IMAGE_hdr ihdr;
|
||||
memcpy (& ihdr.f_magic, & ehdr, sizeof (ehdr));
|
||||
dump_pe (abfd, NULL, & ihdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
non_fatal ("not a PE format binary - unexpected magic number: %#x",
|
||||
magic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump a PE file. */
|
||||
|
||||
static void
|
||||
pe_dump (bfd *abfd)
|
||||
{
|
||||
/* We rely on BFD to decide if the file is a core file. Note that core
|
||||
files are only supported on native environment by BFD. */
|
||||
switch (bfd_get_format (abfd))
|
||||
{
|
||||
case bfd_core:
|
||||
// FIXME: Handle PE format core files ?
|
||||
break;
|
||||
default:
|
||||
pe_dump_obj (abfd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Vector for pe. */
|
||||
|
||||
const struct objdump_private_desc objdump_private_desc_pe =
|
||||
{
|
||||
pe_help,
|
||||
pe_filter,
|
||||
pe_dump,
|
||||
options
|
||||
};
|
||||
@@ -923,6 +923,60 @@ proc test_objdump_S { } {
|
||||
|
||||
test_objdump_S
|
||||
|
||||
# Test objdump --private
|
||||
proc test_objdump_P {} {
|
||||
global srcdir
|
||||
global subdir
|
||||
global OBJDUMP
|
||||
global OBJDUMPFLAGS
|
||||
global obj
|
||||
|
||||
set test "objdump -P"
|
||||
|
||||
if {![binutils_assemble $srcdir/$subdir/bintest.s tmpdir/bintest.${obj}]} then {
|
||||
unsupported "$test (build)"
|
||||
return
|
||||
}
|
||||
|
||||
if [is_remote host] {
|
||||
set testfile [remote_download host tmpdir/bintest.${obj}]
|
||||
} else {
|
||||
set testfile tmpdir/bintest.${obj}
|
||||
}
|
||||
|
||||
set got [binutils_run $OBJDUMP "$OBJDUMPFLAGS -P header $testfile"]
|
||||
|
||||
# FIXME: We should probably test the output further than this...
|
||||
set want "Machine Num"
|
||||
|
||||
if [regexp $want $got] then {
|
||||
pass "$test (dump headers)"
|
||||
} else {
|
||||
fail "$test (dump headers)"
|
||||
}
|
||||
|
||||
set got [binutils_run $OBJDUMP "$OBJDUMPFLAGS --private=sections $testfile"]
|
||||
|
||||
# FIXME: We should probably test the output further than this...
|
||||
set want "EXECUTE,READ"
|
||||
|
||||
if [regexp $want $got] then {
|
||||
pass "$test (dump sections)"
|
||||
} else {
|
||||
fail "$test (dump sections)"
|
||||
}
|
||||
|
||||
# remote_file host delete $testfile
|
||||
}
|
||||
|
||||
# Test objdump --private of a PE format file.
|
||||
if { [istarget "*-*-pe*"]
|
||||
|| [istarget "*-*-cygwin*"]
|
||||
|| [istarget "*-*-mingw*"]
|
||||
} then {
|
||||
test_objdump_P
|
||||
}
|
||||
|
||||
# Options which are not tested: -a -D -R -T -x -l --stabs
|
||||
# I don't see any generic way to test any of these other than -a.
|
||||
# Tests could be written for specific targets, and that should be done
|
||||
|
||||
Reference in New Issue
Block a user