public inbox for binutils-cvs@sourceware.org
 help / color / mirror / Atom feed
* [binutils-gdb] Enhance objdump's --private option so that it can display the contents of PE format files.
@ 2023-05-26 14:42 Nick Clifton
  0 siblings, 0 replies; only message in thread
From: Nick Clifton @ 2023-05-26 14:42 UTC (permalink / raw)
  To: bfd-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=45b8517aae872adf7756dce6e138e487e5bc9b96

commit 45b8517aae872adf7756dce6e138e487e5bc9b96
Author: Nick Clifton <nickc@redhat.com>
Date:   Fri May 26 15:41:20 2023 +0100

    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.

Diff:
---
 binutils/ChangeLog                          |  17 +
 binutils/Makefile.am                        |   4 +-
 binutils/Makefile.in                        |   5 +-
 binutils/NEWS                               |   3 +
 binutils/configure                          |   7 +-
 binutils/configure.ac                       |   7 +-
 binutils/doc/binutils.texi                  |   6 +
 binutils/objdump.c                          |   2 +-
 binutils/objdump.h                          |   7 +
 binutils/od-pe.c                            | 565 ++++++++++++++++++++++++++++
 binutils/testsuite/binutils-all/objdump.exp |  54 +++
 11 files changed, 670 insertions(+), 7 deletions(-)

diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index cd075e7268a..b041050dc95 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -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.
diff --git a/binutils/Makefile.am b/binutils/Makefile.am
index 8e51310cf55..904785bd549 100644
--- a/binutils/Makefile.am
+++ b/binutils/Makefile.am
@@ -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
diff --git a/binutils/Makefile.in b/binutils/Makefile.in
index a58c8aea259..0b8b8d48b0d 100644
--- a/binutils/Makefile.in
+++ b/binutils/Makefile.in
@@ -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@
diff --git a/binutils/NEWS b/binutils/NEWS
index d1c88f592b9..b29f36bf1d4 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -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
diff --git a/binutils/configure b/binutils/configure
index f27ee48be73..6987769fc51 100755
--- a/binutils/configure
+++ b/binutils/configure
@@ -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 ;;
diff --git a/binutils/configure.ac b/binutils/configure.ac
index dc93ac1f390..b5798bf755f 100644
--- a/binutils/configure.ac
+++ b/binutils/configure.ac
@@ -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) ;;
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index b1982a95704..8c14d1121d9 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -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.
 
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 3f4399194ad..a35982ea969 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -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 */
diff --git a/binutils/objdump.h b/binutils/objdump.h
index 4785489d42a..8d0b3878c1c 100644
--- a/binutils/objdump.h
+++ b/binutils/objdump.h
@@ -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;
diff --git a/binutils/od-pe.c b/binutils/od-pe.c
new file mode 100644
index 00000000000..5f03bd8c3da
--- /dev/null
+++ b/binutils/od-pe.c
@@ -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
+};
diff --git a/binutils/testsuite/binutils-all/objdump.exp b/binutils/testsuite/binutils-all/objdump.exp
index bda63d0238c..28a7ad671cd 100644
--- a/binutils/testsuite/binutils-all/objdump.exp
+++ b/binutils/testsuite/binutils-all/objdump.exp
@@ -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

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-05-26 14:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-26 14:42 [binutils-gdb] Enhance objdump's --private option so that it can display the contents of PE format files Nick Clifton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).