Enhance objdump's --private option so that it can display the contents of PE format...
[binutils-gdb.git] / binutils / od-pe.c
blob5f03bd8c3da34dc4d6ac990de1c7452dc01b326f
1 /* od-pe.c -- dump information about a PE object file.
2 Copyright (C) 2011-2023 Free Software Foundation, Inc.
3 Written by Tristan Gingold, Adacore and Nick Clifton, Red Hat.
5 This file is part of GNU Binutils.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
22 #include "sysdep.h"
23 #include <stddef.h>
24 #include <time.h>
25 #include "safe-ctype.h"
26 #include "bfd.h"
27 #include "objdump.h"
28 #include "bucomm.h"
29 #include "bfdlink.h"
30 #include "coff/internal.h"
31 #define L_LNNO_SIZE 4 /* FIXME: which value should we use ? */
32 #include "coff/external.h"
33 #include "coff/pe.h"
34 #include "libcoff.h"
35 #include "libpei.h"
37 /* Index of the options in the options[] array. */
38 #define OPT_FILE_HEADER 0
39 #define OPT_AOUT 1
40 #define OPT_SECTIONS 2
41 #define OPT_SYMS 3
42 #define OPT_RELOCS 4
43 #define OPT_LINENO 5
44 #define OPT_LOADER 6
45 #define OPT_EXCEPT 7
46 #define OPT_TYPCHK 8
47 #define OPT_TRACEBACK 9
48 #define OPT_TOC 10
49 #define OPT_LDINFO 11
51 /* List of actions. */
52 static struct objdump_private_option options[] =
54 { "header", 0 },
55 { "aout", 0 },
56 { "sections", 0 },
57 { "syms", 0 },
58 { "relocs", 0 },
59 { "lineno", 0 },
60 { "loader", 0 },
61 { "except", 0 },
62 { "typchk", 0 },
63 { "traceback", 0 },
64 { "toc", 0 },
65 { "ldinfo", 0 },
66 { NULL, 0 }
69 /* Simplified section header. */
70 struct pe_section
72 /* NUL terminated name. */
73 char name[9];
75 /* Section flags. */
76 unsigned int flags;
78 /* Offsets in file. */
79 ufile_ptr scnptr;
80 ufile_ptr relptr;
81 ufile_ptr lnnoptr;
83 /* Number of relocs and line numbers. */
84 unsigned int nreloc;
85 unsigned int nlnno;
88 /* Translation entry type. The last entry must be {0, NULL}. */
90 struct xlat_table
92 unsigned int val;
93 const char * name;
96 /* PE file flags. */
97 static const struct xlat_table file_flag_xlat[] =
99 { IMAGE_FILE_RELOCS_STRIPPED, "RELOCS STRIPPED"},
100 { IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE"},
101 { IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE NUMS STRIPPED"},
102 { IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL SYMS STRIPPED"},
103 { IMAGE_FILE_AGGRESSIVE_WS_TRIM, "AGGRESSIVE WS TRIM"},
104 { IMAGE_FILE_LARGE_ADDRESS_AWARE, "LARGE ADDRESS AWARE"},
105 { IMAGE_FILE_16BIT_MACHINE, "16BIT MACHINE"},
106 { IMAGE_FILE_BYTES_REVERSED_LO, "BYTES REVERSED LO"},
107 { IMAGE_FILE_32BIT_MACHINE, "32BIT MACHINE"},
108 { IMAGE_FILE_DEBUG_STRIPPED, "DEBUG STRIPPED"},
109 { IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "REMOVABLE RUN FROM SWAP"},
110 { IMAGE_FILE_NET_RUN_FROM_SWAP, "NET RUN FROM SWAP"},
111 { IMAGE_FILE_SYSTEM, "SYSTEM"},
112 { IMAGE_FILE_DLL, "DLL"},
113 { IMAGE_FILE_UP_SYSTEM_ONLY, "UP SYSTEM ONLY"},
114 { IMAGE_FILE_BYTES_REVERSED_HI, "BYTES REVERSED HI"},
115 { 0, NULL }
118 /* PE section flags. */
119 static const struct xlat_table section_flag_xlat[] =
121 { IMAGE_SCN_MEM_DISCARDABLE, "DISCARDABLE" },
122 { IMAGE_SCN_MEM_EXECUTE, "EXECUTE" },
123 { IMAGE_SCN_MEM_READ, "READ" },
124 { IMAGE_SCN_MEM_WRITE, "WRITE" },
125 { IMAGE_SCN_TYPE_NO_PAD, "NO PAD" },
126 { IMAGE_SCN_CNT_CODE, "CODE" },
127 { IMAGE_SCN_CNT_INITIALIZED_DATA, "INITIALIZED DATA" },
128 { IMAGE_SCN_CNT_UNINITIALIZED_DATA, "UNINITIALIZED DATA" },
129 { IMAGE_SCN_LNK_OTHER, "OTHER" },
130 { IMAGE_SCN_LNK_INFO, "INFO" },
131 { IMAGE_SCN_LNK_REMOVE, "REMOVE" },
132 { IMAGE_SCN_LNK_COMDAT, "COMDAT" },
133 { IMAGE_SCN_MEM_FARDATA, "FARDATA" },
134 { IMAGE_SCN_MEM_PURGEABLE, "PURGEABLE" },
135 { IMAGE_SCN_MEM_LOCKED, "LOCKED" },
136 { IMAGE_SCN_MEM_PRELOAD, "PRELOAD" },
137 { IMAGE_SCN_LNK_NRELOC_OVFL, "NRELOC OVFL" },
138 { IMAGE_SCN_MEM_NOT_CACHED, "NOT CACHED" },
139 { IMAGE_SCN_MEM_NOT_PAGED, "NOT PAGED" },
140 { IMAGE_SCN_MEM_SHARED, "SHARED" },
141 { 0, NULL }
145 /* Display help. */
147 static void
148 pe_help (FILE *stream)
150 fprintf (stream, _("\
151 For PE files:\n\
152 header Display the file header\n\
153 sections Display the section headers\n\
154 "));
157 /* Return true if ABFD is handled. */
159 static int
160 pe_filter (bfd *abfd)
162 return bfd_get_flavour (abfd) == bfd_target_coff_flavour;
165 /* Display the list of name (from TABLE) for FLAGS, using comma to
166 separate them. A name is displayed if FLAGS & VAL is not 0. */
168 static void
169 dump_flags (const struct xlat_table * table, unsigned int flags)
171 unsigned int r = flags;
172 bool first = true;
173 const struct xlat_table *t;
175 for (t = table; t->name; t++)
176 if ((flags & t->val) != 0)
178 r &= ~t->val;
180 if (first)
181 first = false;
182 else
183 putchar (',');
184 fputs (t->name, stdout);
187 /* Undecoded flags. */
188 if (r != 0)
190 if (!first)
191 putchar (',');
192 printf (_("unknown: 0x%x"), r);
196 static const char *
197 decode_machine_number (unsigned int machine)
199 switch (machine)
201 case IMAGE_FILE_MACHINE_ALPHA: return "ALPHA";
202 case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
203 case IMAGE_FILE_MACHINE_ARM: return "ARM";
204 case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
205 case IMAGE_FILE_MACHINE_I386: return "I386";
206 case IMAGE_FILE_MACHINE_IA64: return "IA64";
207 case IMAGE_FILE_MACHINE_LOONGARCH64: return "LOONGARCH64";
208 case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
209 case 0x0500: return "SH (big endian)";
210 case 0x0550: return "SH (little endian)";
211 case 0x0b00: return "MCore";
212 case 0x0093: return "TI C4X";
213 // FIXME: Add more machine numbers.
214 default: return N_("unknown");
218 /* Dump the file header. */
220 static void
221 dump_pe_file_header (bfd * abfd,
222 struct external_PEI_filehdr * fhdr,
223 struct external_PEI_IMAGE_hdr * ihdr)
225 unsigned long ihdr_off = 0;
227 if (fhdr != NULL)
229 printf (_("\nFile Header:\n"));
231 /* FIXME: The fields in the file header are boring an generally have
232 fixed values. Is there any benefit in displaying them ? */
234 /* Display the first string found in the stub.
235 FIXME: Look for more than one string ?
236 FIXME: Strictly speaking we may not have read the full stub, since
237 it can be longer than the dos_message array in the PEI_fileheader
238 structure. */
239 const unsigned char * message = (const unsigned char *) fhdr->dos_message;
240 unsigned int len = sizeof (fhdr->dos_message);
241 unsigned int i;
242 unsigned int seen_count = 0;
243 unsigned int string_start = 0;
245 for (i = 0; i < len; i++)
247 if (ISPRINT (message[i]))
249 if (string_start == 0)
250 string_start = i;
251 ++ seen_count;
252 if (seen_count > 4)
253 break;
255 else
257 seen_count = string_start = 0;
261 if (seen_count > 4)
263 printf (_(" Stub message: "));
264 while (string_start < len)
266 char c = message[string_start ++];
267 if (! ISPRINT (c))
268 break;
269 putchar (c);
271 putchar ('\n');
274 ihdr_off = (long) bfd_h_get_32 (abfd, fhdr->e_lfanew);
277 printf (_("\nImage Header (at offset %#lx):\n"), ihdr_off);
279 unsigned int machine = (int) bfd_h_get_16 (abfd, ihdr->f_magic);
280 printf (_(" Machine Num: %#x\t\t- %s\n"), machine,
281 decode_machine_number (machine));
283 printf (_(" Num sections: %d\n"), (int) bfd_h_get_16 (abfd, ihdr->f_nscns));
285 long timedat = bfd_h_get_32 (abfd, ihdr->f_timdat);
286 printf (_(" Time and date: %#08lx\t- "), timedat);
287 if (timedat == 0)
288 printf (_("not set\n"));
289 else
291 /* Not correct on all platforms, but works on unix. */
292 time_t t = timedat;
293 fputs (ctime (& t), stdout);
296 printf (_(" Symbols off: %#08lx\n"),
297 (long) bfd_h_get_32 (abfd, ihdr->f_symptr));
298 printf (_(" Num symbols: %ld\n"),
299 (long) bfd_h_get_32 (abfd, ihdr->f_nsyms));
301 unsigned int opt_header_size = (int) bfd_h_get_16 (abfd, ihdr->f_opthdr);
302 printf (_(" Opt hdr sz: %#x\n"), opt_header_size);
304 unsigned int flags = (int) bfd_h_get_16 (abfd, ihdr->f_flags);
305 printf (_(" Flags: 0x%04x\t\t- "), flags);
306 dump_flags (file_flag_xlat, flags);
307 putchar ('\n');
309 if (opt_header_size == PEPAOUTSZ)
311 PEPAOUTHDR xhdr;
313 printf (_("\nOptional PE+ Header (at offset %#lx):\n"),
314 ihdr_off + sizeof (* ihdr));
316 if (bfd_seek (abfd, ihdr_off + sizeof (* ihdr), SEEK_SET) != 0
317 || bfd_bread (& xhdr, sizeof (xhdr), abfd) != sizeof (xhdr))
318 printf ("error: unable to read PE+ header\n");
319 else
321 /* FIXME: Check that the magic number is 0x020b ? */
322 printf (_(" Magic: %x\n"),
323 (int) bfd_h_get_16 (abfd, xhdr.standard.magic));
324 printf (_(" Image Base: %lx\n"),
325 (long) bfd_h_get_64 (abfd, xhdr.ImageBase));
326 /* FIXME: Print more fields. */
329 else if (opt_header_size == AOUTSZ)
331 PEAOUTHDR xhdr;
333 printf (_("\nOptional PE Header (at offset %#lx):\n"),
334 ihdr_off + sizeof (* ihdr));
336 if (bfd_seek (abfd, ihdr_off + sizeof (* ihdr), SEEK_SET) != 0
337 || bfd_bread (& xhdr, sizeof (xhdr), abfd) != sizeof (xhdr))
338 printf ("error: unable to read PE+ header\n");
339 else
341 /* FIXME: Check that the magic number is 0x010b ? */
342 printf (_(" Magic: %x\n"),
343 (int) bfd_h_get_16 (abfd, xhdr.standard.magic));
344 printf (_(" Image Base: %lx\n"),
345 (long) bfd_h_get_32 (abfd, xhdr.ImageBase));
346 /* FIXME: Print more fields. */
349 else if (opt_header_size != 0)
351 printf (_("\nUnsupported size of Optional Header\n"));
355 /* Dump the sections header. */
357 static void
358 dump_pe_sections_header (bfd * abfd,
359 struct external_PEI_filehdr * fhdr,
360 struct external_PEI_IMAGE_hdr * ihdr)
362 unsigned int opthdr = (int) bfd_h_get_16 (abfd, ihdr->f_opthdr);
363 unsigned int n_scns = (int) bfd_h_get_16 (abfd, ihdr->f_nscns);
364 unsigned int off;
366 /* The section header starts after the file, image and optional headers. */
367 if (fhdr == NULL)
368 off = sizeof (struct external_filehdr) + opthdr;
369 else
370 off = (int) bfd_h_get_16 (abfd, fhdr->e_lfanew) + sizeof (* ihdr) + opthdr;
372 printf (_("\nSection headers (at offset 0x%08x):\n"), off);
374 if (n_scns == 0)
376 printf (_(" No section headers\n"));
377 return;
379 if (bfd_seek (abfd, off, SEEK_SET) != 0)
381 non_fatal (_("cannot seek to section headers start\n"));
382 return;
385 /* We don't translate this string as it consists of field names. */
386 if (wide_output)
387 printf (" # Name paddr vaddr size scnptr relptr lnnoptr nrel nlnno Flags\n");
388 else
389 printf (" # Name paddr vaddr size scnptr relptr lnnoptr nrel nlnno\n");
391 unsigned int i;
392 for (i = 0; i < n_scns; i++)
394 struct external_scnhdr scn;
395 unsigned int flags;
397 if (bfd_bread (& scn, sizeof (scn), abfd) != sizeof (scn))
399 non_fatal (_("cannot read section header"));
400 return;
403 printf ("%2d %-8.8s %08x %08x %08x %08x %08x %08x %5d %5d",
404 i + 1, scn.s_name,
405 (unsigned int) bfd_h_get_32 (abfd, scn.s_paddr),
406 (unsigned int) bfd_h_get_32 (abfd, scn.s_vaddr),
407 (unsigned int) bfd_h_get_32 (abfd, scn.s_size),
408 (unsigned int) bfd_h_get_32 (abfd, scn.s_scnptr),
409 (unsigned int) bfd_h_get_32 (abfd, scn.s_relptr),
410 (unsigned int) bfd_h_get_32 (abfd, scn.s_lnnoptr),
411 (unsigned int) bfd_h_get_16 (abfd, scn.s_nreloc),
412 (unsigned int) bfd_h_get_16 (abfd, scn.s_nlnno));
414 flags = bfd_h_get_32 (abfd, scn.s_flags);
415 if (wide_output)
416 printf (_(" %08x "), flags);
417 else
418 printf (_("\n Flags: %08x: "), flags);
420 if (flags != 0)
422 /* Skip the alignment bits. */
423 flags &= ~ IMAGE_SCN_ALIGN_POWER_BIT_MASK;
424 dump_flags (section_flag_xlat, flags);
427 putchar ('\n');
431 /* Handle a PE format file. */
433 static void
434 dump_pe (bfd * abfd,
435 struct external_PEI_filehdr * fhdr,
436 struct external_PEI_IMAGE_hdr * ihdr)
438 if (options[OPT_FILE_HEADER].selected)
439 dump_pe_file_header (abfd, fhdr, ihdr);
441 if (options[OPT_SECTIONS].selected)
442 dump_pe_sections_header (abfd, fhdr, ihdr);
445 static bool
446 is_pe_object_magic (unsigned short magic)
448 switch (magic)
450 case IMAGE_FILE_MACHINE_ALPHA:
451 case IMAGE_FILE_MACHINE_ARM:
452 case IMAGE_FILE_MACHINE_ARM64:
453 case IMAGE_FILE_MACHINE_I386:
454 case IMAGE_FILE_MACHINE_IA64:
455 case IMAGE_FILE_MACHINE_POWERPC:
456 case IMAGE_FILE_MACHINE_LOONGARCH64:
457 case IMAGE_FILE_MACHINE_AMD64:
458 // FIXME: Add more machine numbers.
459 return true;
460 case 0x0500: /* SH_ARCH_MAGIC_BIG */
461 case 0x0550: /* SH_ARCH_MAGIC_LITTLE */
462 case 0x0b00: /* MCore */
463 case 0x0093: /* TI C4x */
464 return true;
465 default:
466 return false;
470 /* Dump ABFD (according to the options[] array). */
472 static void
473 pe_dump_obj (bfd *abfd)
475 struct external_PEI_filehdr fhdr;
477 /* Read file header. */
478 if (bfd_seek (abfd, 0, SEEK_SET) != 0
479 || bfd_bread (& fhdr, sizeof (fhdr), abfd) != sizeof (fhdr))
481 non_fatal (_("cannot seek to/read file header"));
482 return;
485 unsigned short magic = bfd_h_get_16 (abfd, fhdr.e_magic);
487 /* PE format executable files have a full external_PEI_filehdr structure
488 at the start. PE format object files just have an external_filehdr
489 structure at the start. */
490 if (magic == IMAGE_DOS_SIGNATURE)
492 unsigned int ihdr_offset = (int) bfd_h_get_16 (abfd, fhdr.e_lfanew);
494 /* FIXME: We could reuse the fields in fhdr, but that might
495 confuse various sanitization and memory checker tools. */
496 struct external_PEI_IMAGE_hdr ihdr;
498 if (bfd_seek (abfd, ihdr_offset, SEEK_SET) != 0
499 || bfd_bread (& ihdr, sizeof (ihdr), abfd) != sizeof (ihdr))
501 non_fatal (_("cannot seek to/read image header at offset %#x"),
502 ihdr_offset);
503 return;
506 unsigned int signature = (int) bfd_h_get_16 (abfd, ihdr.nt_signature);
507 if (signature != IMAGE_NT_SIGNATURE)
509 non_fatal ("file does not have an NT format signature: %#x",
510 signature);
511 return;
514 dump_pe (abfd, & fhdr, & ihdr);
516 else if (is_pe_object_magic (magic))
518 struct external_filehdr ehdr;
520 if (bfd_seek (abfd, 0, SEEK_SET) != 0
521 || bfd_bread (& ehdr, sizeof (ehdr), abfd) != sizeof (ehdr))
523 non_fatal (_("cannot seek to/read image header"));
524 return;
527 struct external_PEI_IMAGE_hdr ihdr;
528 memcpy (& ihdr.f_magic, & ehdr, sizeof (ehdr));
529 dump_pe (abfd, NULL, & ihdr);
531 else
533 non_fatal ("not a PE format binary - unexpected magic number: %#x",
534 magic);
535 return;
539 /* Dump a PE file. */
541 static void
542 pe_dump (bfd *abfd)
544 /* We rely on BFD to decide if the file is a core file. Note that core
545 files are only supported on native environment by BFD. */
546 switch (bfd_get_format (abfd))
548 case bfd_core:
549 // FIXME: Handle PE format core files ?
550 break;
551 default:
552 pe_dump_obj (abfd);
553 break;
557 /* Vector for pe. */
559 const struct objdump_private_desc objdump_private_desc_pe =
561 pe_help,
562 pe_filter,
563 pe_dump,
564 options