/* 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
};
