3 Copyright (c) 1999-2004 Intel Corporation. All rights reserved
4 This software and associated documentation (if any) is furnished
5 under a license and may only be used or copied in accordance
6 with the terms of the license. Except as permitted by such
7 license, no part of this software or documentation may be
8 reproduced, stored in a retrieval system, or transmitted in any
9 form or by any means without the express written consent of
18 Utility program to shrink a PE32 image down by replacing
19 the DOS, PE, and optional headers with a minimal header.
27 #include <UefiBaseTypes.h>
29 #include <CommonLib.h>
31 #include "EfiImage.h" // for PE32 structure definitions
32 #include "EfiUtilityMsgs.h"
35 // Version of this utility
37 #define UTILITY_NAME "GenTEImage"
38 #define UTILITY_VERSION "v0.11"
41 // Define the max length of a filename
44 #define DEFAULT_OUTPUT_EXTENSION ".te"
47 // Use this to track our command-line options and globals
50 INT8 OutFileName
[MAX_PATH
];
51 INT8 InFileName
[MAX_PATH
];
57 // Use these to convert from machine type value to a named type
64 static STRING_LOOKUP mMachineTypes
[] = {
65 EFI_IMAGE_MACHINE_IA32
,
67 EFI_IMAGE_MACHINE_IA64
,
69 EFI_IMAGE_MACHINE_EBC
,
75 static STRING_LOOKUP mSubsystemTypes
[] = {
76 EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION
,
78 EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER
,
79 "EFI boot service driver",
80 EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER
,
86 // Function prototypes
131 GetSubsystemTypeStr (
146 Argc - standard C main() argument count
148 Argv - standard C main() argument list
156 // GC_TODO: ] - add argument and description to function comment
161 SetUtilityName (UTILITY_NAME
);
163 // Parse the command line arguments
165 if (ParseCommandLine (Argc
, Argv
)) {
169 // If dumping an image, then do that and quit
172 DumpImage (mOptions
.InFileName
);
176 // Determine the output filename. Either what they specified on
177 // the command line, or the first input filename with a different extension.
179 if (!mOptions
.OutFileName
[0]) {
180 strcpy (mOptions
.OutFileName
, mOptions
.InFileName
);
182 // Find the last . on the line and replace the filename extension with
185 for (Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
) - 1;
186 (Ext
>= mOptions
.OutFileName
) && (*Ext
!= '.') && (*Ext
!= '\\');
191 // If dot here, then insert extension here, otherwise append
194 Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
);
197 strcpy (Ext
, DEFAULT_OUTPUT_EXTENSION
);
200 // Make sure we don't have the same filename for input and output files
202 if (stricmp (mOptions
.OutFileName
, mOptions
.InFileName
) == 0) {
203 Error (NULL
, 0, 0, mOptions
.OutFileName
, "input and output file names must be different");
209 ProcessFile (mOptions
.InFileName
, mOptions
.OutFileName
);
211 Status
= GetUtilityStatus ();
225 Process a PE32 EFI file.
229 InFileName - the file name pointer to the input file
230 OutFileName - the file name pointer to the output file
234 STATUS_SUCCESS - the process has been finished successfully
235 STATUS_ERROR - error occured during the processing
244 EFI_TE_IMAGE_HEADER TEImageHeader
;
246 EFI_IMAGE_FILE_HEADER FileHeader
;
247 EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader32
;
248 EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader64
;
249 UINT32 BytesStripped
;
252 long SaveFilePosition
;
257 Status
= STATUS_ERROR
;
260 // Try to open the input file
262 if ((InFptr
= fopen (InFileName
, "rb")) == NULL
) {
263 Error (NULL
, 0, 0, InFileName
, "failed to open input file for reading");
267 // Double-check the file to make sure it's what we expect it to be
269 if (CheckPE32File (InFileName
, InFptr
, &MachineType
, &SubSystem
) != STATUS_SUCCESS
) {
273 // Initialize our new header
275 memset (&TEImageHeader
, 0, sizeof (EFI_TE_IMAGE_HEADER
));
278 // Seek to the end to get the file size
280 fseek (InFptr
, 0, SEEK_END
);
281 FileSize
= ftell (InFptr
);
282 fseek (InFptr
, 0, SEEK_SET
);
285 // Per the PE/COFF specification, at offset 0x3C in the file is a 32-bit
286 // offset (from the start of the file) to the PE signature, which always
287 // follows the MSDOS stub. The PE signature is immediately followed by the
291 if (fseek (InFptr
, 0x3C, SEEK_SET
) != 0) {
292 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature in file", NULL
);
296 if (fread (&PESigOffset
, sizeof (PESigOffset
), 1, InFptr
) != 1) {
297 Error (NULL
, 0, 0, InFileName
, "failed to read PE signature offset from file");
301 if (fseek (InFptr
, PESigOffset
+ 4, SEEK_SET
) != 0) {
302 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature");
306 // We should now be at the COFF file header. Read it in and verify it's
307 // of an image type we support.
309 if (fread (&FileHeader
, sizeof (EFI_IMAGE_FILE_HEADER
), 1, InFptr
) != 1) {
310 Error (NULL
, 0, 0, InFileName
, "failed to read file header from image");
314 if ((FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA32
) && (FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA64
)) {
315 Error (NULL
, 0, 0, InFileName
, "image is of an unsupported machine type 0x%X", (UINT32
) FileHeader
.Machine
);
319 // Calculate the total number of bytes we're going to strip off. The '4' is for the
320 // PE signature PE\0\0. Then sanity check the size.
322 BytesStripped
= PESigOffset
+ 4 + sizeof (EFI_IMAGE_FILE_HEADER
) + FileHeader
.SizeOfOptionalHeader
;
323 if (BytesStripped
>= FileSize
) {
324 Error (NULL
, 0, 0, InFileName
, "attempt to strip more bytes than the total file size");
328 if (BytesStripped
&~0xFFFF) {
329 Error (NULL
, 0, 0, InFileName
, "attempt to strip more than 64K bytes", NULL
);
333 TEImageHeader
.StrippedSize
= (UINT16
) BytesStripped
;
336 // Read in the optional header. Assume PE32, and if not, then re-read as PE32+
338 SaveFilePosition
= ftell (InFptr
);
339 if (fread (&OptionalHeader32
, sizeof (EFI_IMAGE_OPTIONAL_HEADER32
), 1, InFptr
) != 1) {
340 Error (NULL
, 0, 0, InFileName
, "failed to read optional header from input file");
344 if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
346 // Fill in our new header with required data directory entries
348 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader32
.AddressOfEntryPoint
;
350 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
352 // We're going to pack the subsystem into 1 byte. Make sure it fits
354 if (OptionalHeader32
.Subsystem
&~0xFF) {
361 "image subsystem 0x%X cannot be packed into 1 byte",
362 (UINT32
) OptionalHeader32
.Subsystem
367 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader32
.Subsystem
;
368 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
369 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader32
.ImageBase
+ TEImageHeader
.StrippedSize
- sizeof (EFI_TE_IMAGE_HEADER
));
370 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
371 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
372 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
375 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
376 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
377 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
379 } else if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
381 // Rewind and re-read the optional header
383 fseek (InFptr
, SaveFilePosition
, SEEK_SET
);
384 if (fread (&OptionalHeader64
, sizeof (EFI_IMAGE_OPTIONAL_HEADER64
), 1, InFptr
) != 1) {
385 Error (NULL
, 0, 0, InFileName
, "failed to re-read optional header from input file");
389 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader64
.AddressOfEntryPoint
;
391 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
393 // We're going to pack the subsystem into 1 byte. Make sure it fits
395 if (OptionalHeader64
.Subsystem
&~0xFF) {
402 "image subsystem 0x%X cannot be packed into 1 byte",
403 (UINT32
) OptionalHeader64
.Subsystem
408 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader64
.Subsystem
;
409 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
410 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader64
.ImageBase
+ TEImageHeader
.StrippedSize
- sizeof (EFI_TE_IMAGE_HEADER
));
411 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
412 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
413 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
416 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
417 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
418 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
426 "unsupported magic number 0x%X found in optional header",
427 (UINT32
) OptionalHeader32
.Magic
432 // Fill in the remainder of our new image header
434 TEImageHeader
.Signature
= EFI_TE_IMAGE_HEADER_SIGNATURE
;
435 TEImageHeader
.Machine
= FileHeader
.Machine
;
437 // We're going to pack the number of sections into a single byte. Make sure it fits.
439 if (FileHeader
.NumberOfSections
&~0xFF) {
446 "image's number of sections 0x%X cannot be packed into 1 byte",
447 (UINT32
) FileHeader
.NumberOfSections
452 TEImageHeader
.NumberOfSections
= (UINT8
) FileHeader
.NumberOfSections
;
455 // Now open our output file
457 if ((OutFptr
= fopen (OutFileName
, "wb")) == NULL
) {
458 Error (NULL
, 0, 0, OutFileName
, "failed to open output file for writing");
462 // Write the TE header
464 if (fwrite (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, OutFptr
) != 1) {
465 Error (NULL
, 0, 0, "failed to write image header to output file", NULL
);
469 // Position into the input file, read the part we're not stripping, and
472 fseek (InFptr
, BytesStripped
, SEEK_SET
);
473 Buffer
= (UINT8
*) malloc (FileSize
- BytesStripped
);
474 if (Buffer
== NULL
) {
475 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
479 if (fread (Buffer
, FileSize
- BytesStripped
, 1, InFptr
) != 1) {
480 Error (NULL
, 0, 0, InFileName
, "failed to read remaining contents of input file");
484 if (fwrite (Buffer
, FileSize
- BytesStripped
, 1, OutFptr
) != 1) {
485 Error (NULL
, 0, 0, OutFileName
, "failed to write all bytes to output file");
489 Status
= STATUS_SUCCESS
;
492 if (InFptr
!= NULL
) {
496 // Close the output file. If there was an error, delete the output file so
497 // that a subsequent build will rebuild it.
499 if (OutFptr
!= NULL
) {
501 if (GetUtilityStatus () == STATUS_ERROR
) {
502 remove (OutFileName
);
507 // Free up our buffer
509 if (Buffer
!= NULL
) {
528 GC_TODO: Add function description
532 FileName - GC_TODO: add argument description
533 Fptr - GC_TODO: add argument description
534 MachineType - GC_TODO: add argument description
535 SubSystem - GC_TODO: add argument description
539 GC_TODO: add return values
547 Given a file pointer to a supposed PE32 image file, verify that it is indeed a
548 PE32 image file, and then return the machine type in the supplied pointer.
552 Fptr File pointer to the already-opened PE32 file
553 MachineType Location to stuff the machine type of the PE32 file. This is needed
554 because the image may be Itanium-based, IA32, or EBC.
562 EFI_IMAGE_DOS_HEADER DosHeader
;
563 EFI_IMAGE_FILE_HEADER FileHdr
;
564 EFI_IMAGE_OPTIONAL_HEADER OptionalHdr
;
568 Status
= STATUS_ERROR
;
570 // Position to the start of the file
572 fseek (Fptr
, 0, SEEK_SET
);
574 // Read the DOS header
576 if (fread (&DosHeader
, sizeof (DosHeader
), 1, Fptr
) != 1) {
577 Error (NULL
, 0, 0, FileName
, "failed to read the DOS stub from the input file");
581 // Check the magic number (0x5A4D)
583 if (DosHeader
.e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
584 Error (NULL
, 0, 0, FileName
, "input file does not appear to be a PE32 image (magic number)");
588 // Position into the file and check the PE signature
590 fseek (Fptr
, (long) DosHeader
.e_lfanew
, SEEK_SET
);
591 if (fread (&PESig
, sizeof (PESig
), 1, Fptr
) != 1) {
592 Error (NULL
, 0, 0, FileName
, "failed to read PE signature bytes");
596 // Check the PE signature in the header "PE\0\0"
598 if (PESig
!= EFI_IMAGE_NT_SIGNATURE
) {
599 Error (NULL
, 0, 0, FileName
, "file does not appear to be a PE32 image (signature)");
603 // Read the file header
605 if (fread (&FileHdr
, sizeof (FileHdr
), 1, Fptr
) != 1) {
606 Error (NULL
, 0, 0, FileName
, "failed to read PE file header from input file");
610 // Read the optional header so we can get the subsystem
612 if (fread (&OptionalHdr
, sizeof (OptionalHdr
), 1, Fptr
) != 1) {
613 Error (NULL
, 0, 0, FileName
, "failed to read COFF optional header from input file");
617 *SubSystem
= OptionalHdr
.Subsystem
;
618 if (mOptions
.Verbose
) {
619 fprintf (stdout
, " Got subsystem = 0x%X from image\n", (int) *SubSystem
);
624 Status
= STATUS_SUCCESS
;
626 fseek (Fptr
, 0, SEEK_SET
);
640 Given the Argc/Argv program arguments, and a pointer to an options structure,
641 parse the command-line options and check their validity.
646 Argc - standard C main() argument count
647 Argv - standard C main() argument list
651 STATUS_SUCCESS success
655 // GC_TODO: ] - add argument and description to function comment
658 // Clear out the options
660 memset ((char *) &mOptions
, 0, sizeof (mOptions
));
662 // Skip over the program name
667 // If no arguments, assume they want usage info
674 // Process until no more arguments
676 while ((Argc
> 0) && ((Argv
[0][0] == '-') || (Argv
[0][0] == '/'))) {
678 // To simplify string comparisons, replace slashes with dashes
681 if (stricmp (Argv
[0], "-o") == 0) {
683 // Output filename specified with -o
684 // Make sure there's another parameter
687 strcpy (mOptions
.OutFileName
, Argv
[1]);
689 Error (NULL
, 0, 0, Argv
[0], "missing output file name with option");
696 } else if ((stricmp (Argv
[0], "-h") == 0) || (strcmp (Argv
[0], "-?") == 0)) {
702 } else if (stricmp (Argv
[0], "-v") == 0) {
706 mOptions
.Verbose
= 1;
707 } else if (stricmp (Argv
[0], "-dump") == 0) {
709 // -dump for dumping an image
713 Error (NULL
, 0, 0, Argv
[0], "unrecognized option");
724 // Better be one more arg for input file name
727 Error (NULL
, 0, 0, "input file name required", NULL
);
733 Error (NULL
, 0, 0, Argv
[1], "extra arguments on command line");
737 strcpy (mOptions
.InFileName
, Argv
[0]);
738 return STATUS_SUCCESS
;
750 Print usage information for this utility.
763 static const char *Msg
[] = {
764 UTILITY_NAME
" version "UTILITY_VERSION
" - TE image utility",
765 " Generate a TE image from an EFI PE32 image",
766 " Usage: "UTILITY_NAME
" {-v} {-dump} {-h|-?} {-o OutFileName} InFileName",
767 " [-e|-b] [FileName(s)]",
769 " -v - for verbose output",
770 " -dump - to dump the input file to a text file",
771 " -h -? - for this help information",
772 " -o OutFileName - to write output to OutFileName rather than InFileName"DEFAULT_OUTPUT_EXTENSION
,
773 " InFileName - name of the input PE32 file",
777 for (Index
= 0; Msg
[Index
] != NULL
; Index
++) {
778 fprintf (stdout
, "%s\n", Msg
[Index
]);
791 Dump a specified image information
795 FileName - File name pointer to the image to dump
804 EFI_TE_IMAGE_HEADER TEImageHeader
;
808 // Open the input file
812 if ((InFptr
= fopen (FileName
, "rb")) == NULL
) {
813 Error (NULL
, 0, 0, FileName
, "failed to open input file for reading");
817 if (fread (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, InFptr
) != 1) {
818 Error (NULL
, 0, 0, FileName
, "failed to read image header from input file");
822 if (TEImageHeader
.Signature
!= EFI_TE_IMAGE_HEADER_SIGNATURE
) {
823 Error (NULL
, 0, 0, FileName
, "Image does not appear to be a TE image (bad signature)");
829 fprintf (stdout
, "Header (%d bytes):\n", sizeof (EFI_TE_IMAGE_HEADER
));
830 fprintf (stdout
, " Signature: 0x%04X (TE)\n", (UINT32
) TEImageHeader
.Signature
);
831 NamePtr
= GetMachineTypeStr (TEImageHeader
.Machine
);
832 fprintf (stdout
, " Machine: 0x%04X (%s)\n", (UINT32
) TEImageHeader
.Machine
, NamePtr
);
833 NamePtr
= GetSubsystemTypeStr (TEImageHeader
.Subsystem
);
834 fprintf (stdout
, " Subsystem: 0x%02X (%s)\n", (UINT32
) TEImageHeader
.Subsystem
, NamePtr
);
835 fprintf (stdout
, " Number of sections 0x%02X\n", (UINT32
) TEImageHeader
.NumberOfSections
);
836 fprintf (stdout
, " Stripped size: 0x%04X\n", (UINT32
) TEImageHeader
.StrippedSize
);
837 fprintf (stdout
, " Entry point: 0x%08X\n", TEImageHeader
.AddressOfEntryPoint
);
838 fprintf (stdout
, " Base of code: 0x%08X\n", TEImageHeader
.BaseOfCode
);
839 fprintf (stdout
, " Data directories:\n");
842 " %8X [%8X] RVA [size] of Base Relocation Directory\n",
843 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
,
844 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
848 " %8X [%8X] RVA [size] of Debug Directory\n",
849 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
,
850 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
854 if (InFptr
!= NULL
) {
868 GC_TODO: Add function description
872 MachineType - GC_TODO: add argument description
876 GC_TODO: add return values
882 for (Index
= 0; mMachineTypes
[Index
].Name
!= NULL
; Index
++) {
883 if (mMachineTypes
[Index
].Value
== MachineType
) {
884 return mMachineTypes
[Index
].Name
;
893 GetSubsystemTypeStr (
900 GC_TODO: Add function description
904 SubsystemType - GC_TODO: add argument description
908 GC_TODO: add return values
914 for (Index
= 0; mSubsystemTypes
[Index
].Name
!= NULL
; Index
++) {
915 if (mSubsystemTypes
[Index
].Value
== SubsystemType
) {
916 return mSubsystemTypes
[Index
].Name
;