mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
91b999864f
PR 31953
865 lines
28 KiB
C
865 lines
28 KiB
C
/* od-pe.c -- dump information about a PE object file.
|
|
Copyright (C) 2011-2024 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"
|
|
#include "libiberty.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 }
|
|
};
|
|
|
|
typedef struct target_specific_info
|
|
{
|
|
unsigned int machine_number;
|
|
const char * name;
|
|
unsigned int aout_hdr_size;
|
|
} target_specific_info;
|
|
|
|
const struct target_specific_info targ_info[] =
|
|
{
|
|
{ IMAGE_FILE_MACHINE_ALPHA, "ALPHA", 80 },
|
|
{ IMAGE_FILE_MACHINE_ALPHA64, "ALPHA64", 80 },
|
|
{ IMAGE_FILE_MACHINE_AM33, "AM33", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_AMD64, "AMD64", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_ARM, "ARM", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_ARM64, "ARM64", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_ARMNT, "ARM NT", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_CEE, "CEE", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_CEF, "CEF", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_EBC, "EBC", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_I386, "I386", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_IA64, "IA64", 108 },
|
|
{ IMAGE_FILE_MACHINE_LOONGARCH64, "LOONGARCH64", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_M32R, "M32R", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_M68K, "M68K", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_MIPS16, "MIPS16", 56 },
|
|
{ IMAGE_FILE_MACHINE_MIPSFPU, "MIPSFPU", 56 },
|
|
{ IMAGE_FILE_MACHINE_MIPSFPU16, "MIPSFPU16", 56 },
|
|
{ IMAGE_FILE_MACHINE_POWERPC, "POWERPC", 72 },
|
|
{ IMAGE_FILE_MACHINE_POWERPCFP, "POWERPCFP", 72 },
|
|
{ IMAGE_FILE_MACHINE_R10000, "R10000", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_R3000, "R3000", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_R4000, "R4000", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_SH3, "SH3", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_SH3DSP, "SH3DSP", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_SH3E, "SH3E", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_SH4, "SH4", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_SH5, "SH5", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_THUMB, "THUMB", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_TRICORE, "TRICORE", AOUTHDRSZ },
|
|
{ IMAGE_FILE_MACHINE_WCEMIPSV2, "WCEMIPSV2", AOUTHDRSZ },
|
|
|
|
{ 0x0093, "TI C4X", 28 },
|
|
{ 0x00C1, "TI C4X", 28 },
|
|
{ 0x00C2, "TI C4X", 28 },
|
|
{ 0x0500, "SH (big endian)", AOUTHDRSZ },
|
|
{ 0x0550, "SH (little endian)", AOUTHDRSZ },
|
|
{ 0x0a00, "ARM", AOUTHDRSZ },
|
|
{ 0x0b00, "MCore", AOUTHDRSZ }
|
|
};
|
|
|
|
static const struct target_specific_info unknown_info =
|
|
{ 0, "unknown", AOUTHDRSZ };
|
|
|
|
static const struct target_specific_info *
|
|
get_target_specific_info (unsigned int machine)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = ARRAY_SIZE (targ_info); i--;)
|
|
if (targ_info[i].machine_number == machine)
|
|
return targ_info + i;
|
|
|
|
return &unknown_info;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Return string representation of the platform id
|
|
stored in upper 2 bits of Win32Version field. */
|
|
|
|
static const char *
|
|
pe_platform_id_str (unsigned int platform_id)
|
|
{
|
|
static const char *const platform_id_str_table[4] =
|
|
{ "WinNT", "WinCE", "Win32s", "Win9x" };
|
|
return platform_id_str_table[platform_id & 0x3];
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
/* Dump the file header. */
|
|
|
|
static void
|
|
dump_pe_file_header (bfd * abfd,
|
|
struct external_PEI_filehdr * fhdr,
|
|
struct external_PEI_IMAGE_hdr * ihdr)
|
|
{
|
|
unsigned int data;
|
|
unsigned long ldata;
|
|
unsigned long ihdr_off = 0;
|
|
|
|
if (fhdr == NULL)
|
|
printf (_("\n File header not present\n"));
|
|
else
|
|
{
|
|
printf (_("\n File Header (at offset 0):\n"));
|
|
|
|
// The values of the following fields are normally fixed.
|
|
// But we display them anyway, in case there are discrepancies.
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_cblp);
|
|
printf (_("Bytes on Last Page:\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_cp);
|
|
printf (_("Pages In File:\t\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_crlc);
|
|
printf (_("Relocations:\t\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_cparhdr);
|
|
printf (_("Size of header in paragraphs:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_minalloc);
|
|
printf (_("Min extra paragraphs needed:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_maxalloc);
|
|
printf (_("Max extra paragraphs needed:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_ss);
|
|
printf (_("Initial (relative) SS value:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_sp);
|
|
printf (_("Initial SP value:\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_csum);
|
|
printf (_("Checksum:\t\t\t%#x\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_ip);
|
|
printf (_("Initial IP value:\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_cs);
|
|
printf (_("Initial (relative) CS value:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_lfarlc);
|
|
printf (_("File address of reloc table:\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_ovno);
|
|
printf (_("Overlay number:\t\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_oemid);
|
|
printf (_("OEM identifier:\t\t\t%d\n"), data);
|
|
|
|
data = bfd_h_get_16 (abfd, fhdr->e_oeminfo);
|
|
printf (_("OEM information:\t\t%#x\n"), data);
|
|
|
|
ldata = bfd_h_get_32 (abfd, fhdr->e_lfanew);
|
|
printf (_("File address of new exe header:\t%#lx\n"), ldata);
|
|
|
|
/* 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:\t\t\t"));
|
|
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 (_("\n Image Header (at offset %#lx):\n"), ihdr_off);
|
|
|
|
/* Note - we try to make this output use the same format as the output from -p.
|
|
But since there are multiple headers to display and the order of the fields
|
|
in the headers do not match the order of information displayed by -p, there
|
|
are some discrepancies. */
|
|
|
|
unsigned int machine = (int) bfd_h_get_16 (abfd, ihdr->f_magic);
|
|
printf (_("Machine Number:\t\t\t%#x\t\t- %s\n"), machine,
|
|
get_target_specific_info (machine)->name);
|
|
|
|
printf (_("Number of sections:\t\t\%d\n"), (int) bfd_h_get_16 (abfd, ihdr->f_nscns));
|
|
|
|
long timedat = bfd_h_get_32 (abfd, ihdr->f_timdat);
|
|
printf (_("Time/Date:\t\t\t%#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 (_("Symbol table offset:\t\t%#08lx\n"),
|
|
(long) bfd_h_get_32 (abfd, ihdr->f_symptr));
|
|
printf (_("Number of symbols:\t\t\%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 (_("Optional header size:\t\t%#x\n"), opt_header_size);
|
|
|
|
unsigned int flags = (int) bfd_h_get_16 (abfd, ihdr->f_flags);
|
|
printf (_("Flags:\t\t\t\t0x%04x\t\t- "), flags);
|
|
dump_flags (file_flag_xlat, flags);
|
|
putchar ('\n');
|
|
|
|
if (opt_header_size == PEPAOUTSZ)
|
|
{
|
|
PEPAOUTHDR xhdr;
|
|
|
|
printf (_("\n Optional 64-bit AOUT Header (at offset %#lx):\n"),
|
|
ihdr_off + sizeof (* ihdr));
|
|
|
|
// Fortunately, it appears that the size and layout of the
|
|
// PEPAOUTHDR header is consistent across all architectures.
|
|
if (bfd_seek (abfd, ihdr_off + sizeof (* ihdr), SEEK_SET) != 0
|
|
|| bfd_read (&xhdr, sizeof (xhdr), abfd) != sizeof (xhdr))
|
|
printf (_("error: unable to read AOUT and PE+ headers\n"));
|
|
else
|
|
{
|
|
data = (int) bfd_h_get_16 (abfd, xhdr.standard.magic);
|
|
printf (_("Magic:\t\t\t\t%x\t\t- %s\n"), data,
|
|
data == 0x020b ? "PE32+" : _("Unknown"));
|
|
|
|
printf (_("Linker Version:\t\t\t%x\t\t- %u.%02u\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.standard.vstamp),
|
|
(int) (bfd_h_get_16 (abfd, xhdr.standard.vstamp) & 0xff),
|
|
(int) (bfd_h_get_16 (abfd, xhdr.standard.vstamp) >> 8));
|
|
|
|
printf (_("Text Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.tsize));
|
|
printf (_("Data Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.dsize));
|
|
printf (_("BSS Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.bsize));
|
|
printf (_("Entry Point:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.entry));
|
|
printf (_("Text Start:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.text_start));
|
|
/* There is no data_start field in the PE+ standard header. */
|
|
|
|
printf (_("\n Optional PE+ Header (at offset %#lx):\n"),
|
|
ihdr_off + sizeof (* ihdr) + sizeof (xhdr.standard));
|
|
|
|
printf (_("Image Base:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.ImageBase));
|
|
printf (_("Section Alignment:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SectionAlignment));
|
|
printf (_("File Alignment:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.FileAlignment));
|
|
|
|
printf (_("Image Version:\t\t\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorImageVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorImageVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorImageVersion));
|
|
|
|
printf (_("Minimal Subsystem Version:\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorSubsystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorSubsystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorSubsystemVersion));
|
|
|
|
printf (_("Minimal OS Version:\t\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorOperatingSystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorOperatingSystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorOperatingSystemVersion));
|
|
|
|
printf (_("Overwrite OS Version:\t\t%lx\t\t- "),
|
|
(long) bfd_h_get_32 (abfd, xhdr.Win32Version));
|
|
if (bfd_h_get_32 (abfd, xhdr.Win32Version) == 0)
|
|
printf (_("(default)\n"));
|
|
else
|
|
printf (_("%u.%02u (build %u, platform %s)\n"),
|
|
((int) (bfd_h_get_32 (abfd, xhdr.Win32Version) & 0xff)),
|
|
((int) ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 8) & 0xff)),
|
|
((int) ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 16) & 0x3fff)),
|
|
pe_platform_id_str ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 30) & 0x3));
|
|
|
|
printf (_("Size Of Image:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfImage));
|
|
printf (_("Size Of Headers:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeaders));
|
|
printf (_("CheckSum:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.CheckSum));
|
|
printf (_("Subsystem:\t\t\t%d\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.Subsystem));
|
|
// FIXME: Decode the characteristics.
|
|
printf (_("DllCharacteristics:\t\t%#x\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.DllCharacteristics));
|
|
printf (_("Size Of Stack Reserve:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfStackReserve));
|
|
printf (_("Size Of Stack Commit:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfStackCommit));
|
|
printf (_("Size Of Heap Reserve:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeapReserve));
|
|
printf (_("Size Of Heap Commit:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeapCommit));
|
|
printf (_("Loader Flags:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.LoaderFlags));
|
|
printf (_("Number Of Rva and Sizes:\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.NumberOfRvaAndSizes));
|
|
|
|
// FIXME: Decode the Data Directory.
|
|
}
|
|
}
|
|
else if (opt_header_size == AOUTSZ)
|
|
{
|
|
PEAOUTHDR xhdr;
|
|
|
|
/* Different architectures have different sizes of AOUT header. */
|
|
unsigned int aout_hdr_size = get_target_specific_info (machine)->aout_hdr_size;
|
|
|
|
unsigned long off = ihdr_off + sizeof (* ihdr);
|
|
unsigned long size = sizeof (xhdr.standard);
|
|
|
|
printf (_("\n Optional 32-bit AOUT Header (at offset %#lx, size %d):\n"),
|
|
off, aout_hdr_size);
|
|
|
|
if (bfd_seek (abfd, off, SEEK_SET) != 0
|
|
|| bfd_read (&xhdr.standard, size, abfd) != size)
|
|
printf (_("error: unable to seek to/read AOUT header\n"));
|
|
else
|
|
{
|
|
data = (int) bfd_h_get_16 (abfd, xhdr.standard.magic);
|
|
printf (_("Magic:\t\t\t\t%x\t\t- %s\n"), data,
|
|
data == 0x010b ? "PE32" : _("Unknown"));
|
|
|
|
printf (_("Linker Version:\t\t\t%x\t\t- %u.%02u\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.standard.vstamp),
|
|
(int) (bfd_h_get_16 (abfd, xhdr.standard.vstamp) & 0xff),
|
|
(int) (bfd_h_get_16 (abfd, xhdr.standard.vstamp) >> 8));
|
|
|
|
printf (_("Text Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.tsize));
|
|
printf (_("Data Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.dsize));
|
|
printf (_("BSS Size:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.bsize));
|
|
printf (_("Entry Point:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.entry));
|
|
printf (_("Text Start:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.text_start));
|
|
printf (_("Data Start:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.standard.data_start));
|
|
}
|
|
|
|
off = ihdr_off + sizeof (* ihdr) + aout_hdr_size;
|
|
size = sizeof (xhdr) - sizeof (xhdr.standard);
|
|
|
|
printf (_("\n Optional PE Header (at offset %#lx):\n"), off);
|
|
|
|
/* FIXME: Sanitizers might complain about reading more bytes than
|
|
fit into the ImageBase field. Find a way to solve this. */
|
|
if (bfd_seek (abfd, off, SEEK_SET) != 0
|
|
|| bfd_read (&xhdr.ImageBase, size, abfd) != size)
|
|
printf (_("error: unable to seek to/read PE header\n"));
|
|
else
|
|
{
|
|
printf (_("Image Base:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.ImageBase));
|
|
printf (_("Section Alignment:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SectionAlignment));
|
|
printf (_("File Alignment:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.FileAlignment));
|
|
|
|
printf (_("Image Version:\t\t\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorImageVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorImageVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorImageVersion));
|
|
|
|
printf (_("Minimal Subsystem Version:\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorSubsystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorSubsystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorSubsystemVersion));
|
|
|
|
printf (_("Minimal OS Version:\t\t%lx\t\t- %u.%02u\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.MajorOperatingSystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MajorOperatingSystemVersion),
|
|
(int) bfd_h_get_16 (abfd, xhdr.MinorOperatingSystemVersion));
|
|
|
|
printf (_("Overwrite OS Version:\t\t%lx\t\t- "),
|
|
(long) bfd_h_get_32 (abfd, xhdr.Win32Version));
|
|
if (bfd_h_get_32 (abfd, xhdr.Win32Version) == 0)
|
|
printf (_("(default)\n"));
|
|
else
|
|
printf (_("%u.%02u (build %u, platform %s)\n"),
|
|
((int) (bfd_h_get_32 (abfd, xhdr.Win32Version) & 0xff)),
|
|
((int) ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 8) & 0xff)),
|
|
((int) ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 16) & 0x3fff)),
|
|
pe_platform_id_str ((bfd_h_get_32 (abfd, xhdr.Win32Version) >> 30) & 0x3));
|
|
|
|
printf (_("Size Of Image:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfImage));
|
|
printf (_("Size Of Headers:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeaders));
|
|
printf (_("CheckSum:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.CheckSum));
|
|
printf (_("Subsystem:\t\t\t%d\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.Subsystem));
|
|
// FIXME: Decode the characteristics.
|
|
printf (_("DllCharacteristics:\t\t%#x\n"),
|
|
(int) bfd_h_get_16 (abfd, xhdr.DllCharacteristics));
|
|
printf (_("Size Of Stack Reserve:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfStackReserve));
|
|
printf (_("Size Of Stack Commit:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfStackCommit));
|
|
printf (_("Size Of Heap Reserve:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeapReserve));
|
|
printf (_("Size Of Heap Commit:\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.SizeOfHeapCommit));
|
|
printf (_("Loader Flags:\t\t\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.LoaderFlags));
|
|
printf (_("Number Of Rva and Sizes:\t%#lx\n"),
|
|
(long) bfd_h_get_32 (abfd, xhdr.NumberOfRvaAndSizes));
|
|
|
|
// FIXME: Decode the Data Directory.
|
|
}
|
|
}
|
|
else if (opt_header_size != 0)
|
|
{
|
|
printf (_("\nUnsupported size of Optional Header\n"));
|
|
}
|
|
else
|
|
printf (_("\n Optional header not present\n"));
|
|
}
|
|
|
|
static void
|
|
dump_alignment (unsigned int flags)
|
|
{
|
|
flags &= IMAGE_SCN_ALIGN_POWER_BIT_MASK;
|
|
|
|
if (flags == IMAGE_SCN_ALIGN_8192BYTES)
|
|
printf (_("Align: 8192 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_4096BYTES)
|
|
printf (_("Align: 4096 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_2048BYTES)
|
|
printf (_("Align: 2048 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_1024BYTES)
|
|
printf (_("Align: 1024 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_512BYTES)
|
|
printf (_("Align: 512 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_256BYTES)
|
|
printf (_("Align: 256 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_128BYTES)
|
|
printf (_("Align: 128 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_64BYTES)
|
|
printf (_("Align: 64 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_32BYTES)
|
|
printf (_("Align: 32 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_16BYTES)
|
|
printf (_("Align: 16 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_8BYTES)
|
|
printf (_("Align: 8 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_4BYTES)
|
|
printf (_("Align: 4 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_2BYTES)
|
|
printf (_("Align: 2 "));
|
|
else if (flags == IMAGE_SCN_ALIGN_1BYTES)
|
|
printf (_("Align: 1 "));
|
|
else
|
|
printf (_("Align: *unknown* "));
|
|
}
|
|
|
|
/* Dump the section's 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_read (&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 & IMAGE_SCN_ALIGN_POWER_BIT_MASK)
|
|
{
|
|
dump_alignment (flags);
|
|
flags &= ~ IMAGE_SCN_ALIGN_POWER_BIT_MASK;
|
|
}
|
|
|
|
if (flags != 0)
|
|
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);
|
|
}
|
|
|
|
/* 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_read (&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_read (&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);
|
|
}
|
|
/* See if we recognise this particular PE object file. */
|
|
else if (get_target_specific_info (magic)->machine_number)
|
|
{
|
|
struct external_filehdr ehdr;
|
|
|
|
if (bfd_seek (abfd, 0, SEEK_SET) != 0
|
|
|| bfd_read (&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 ("unknown PE format binary - unsupported 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
|
|
};
|