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