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