3 Copyright (c) 1999-2006 Intel Corporation. All rights reserved
4 This program and the accompanying materials are licensed and made available
5 under the terms and conditions of the BSD License which accompanies this
6 distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Utility program to shrink a PE32 image down by replacing
19 the DOS, PE, and optional headers with a minimal header.
27 #include <Common/UefiBaseTypes.h>
28 #include <Common/EfiImage.h> // for PE32 structure definitions
30 #include "CommonLib.h"
31 #include "EfiUtilityMsgs.h"
34 // Version of this utility
36 #define UTILITY_NAME "GenTEImage"
37 #define UTILITY_VERSION "v0.11"
40 // Define the max length of a filename
43 #define DEFAULT_OUTPUT_EXTENSION ".te"
46 // Use this to track our command-line options and globals
49 INT8 OutFileName
[MAX_PATH
];
50 INT8 InFileName
[MAX_PATH
];
56 // Use these to convert from machine type value to a named type
63 static STRING_LOOKUP mMachineTypes
[] = {
64 EFI_IMAGE_MACHINE_IA32
,
66 EFI_IMAGE_MACHINE_IA64
,
68 EFI_IMAGE_MACHINE_EBC
,
74 static STRING_LOOKUP mSubsystemTypes
[] = {
75 EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION
,
77 EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER
,
78 "EFI boot service driver",
79 EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER
,
85 // Function prototypes
130 GetSubsystemTypeStr (
145 Argc - standard C main() argument count
147 Argv - standard C main() argument list
155 // GC_TODO: ] - add argument and description to function comment
160 SetUtilityName (UTILITY_NAME
);
162 // Parse the command line arguments
164 if (ParseCommandLine (Argc
, Argv
)) {
168 // If dumping an image, then do that and quit
171 DumpImage (mOptions
.InFileName
);
175 // Determine the output filename. Either what they specified on
176 // the command line, or the first input filename with a different extension.
178 if (!mOptions
.OutFileName
[0]) {
179 strcpy (mOptions
.OutFileName
, mOptions
.InFileName
);
181 // Find the last . on the line and replace the filename extension with
184 for (Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
) - 1;
185 (Ext
>= mOptions
.OutFileName
) && (*Ext
!= '.') && (*Ext
!= '\\');
190 // If dot here, then insert extension here, otherwise append
193 Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
);
196 strcpy (Ext
, DEFAULT_OUTPUT_EXTENSION
);
199 // Make sure we don't have the same filename for input and output files
201 if (stricmp (mOptions
.OutFileName
, mOptions
.InFileName
) == 0) {
202 Error (NULL
, 0, 0, mOptions
.OutFileName
, "input and output file names must be different");
208 ProcessFile (mOptions
.InFileName
, mOptions
.OutFileName
);
210 Status
= GetUtilityStatus ();
224 Process a PE32 EFI file.
228 InFileName - the file name pointer to the input file
229 OutFileName - the file name pointer to the output file
233 STATUS_SUCCESS - the process has been finished successfully
234 STATUS_ERROR - error occured during the processing
243 EFI_TE_IMAGE_HEADER TEImageHeader
;
245 EFI_IMAGE_FILE_HEADER FileHeader
;
246 EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader32
;
247 EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader64
;
248 UINT32 BytesStripped
;
251 long SaveFilePosition
;
256 Status
= STATUS_ERROR
;
259 // Try to open the input file
261 if ((InFptr
= fopen (InFileName
, "rb")) == NULL
) {
262 Error (NULL
, 0, 0, InFileName
, "failed to open input file for reading");
266 // Double-check the file to make sure it's what we expect it to be
268 if (CheckPE32File (InFileName
, InFptr
, &MachineType
, &SubSystem
) != STATUS_SUCCESS
) {
272 // Initialize our new header
274 memset (&TEImageHeader
, 0, sizeof (EFI_TE_IMAGE_HEADER
));
277 // Seek to the end to get the file size
279 fseek (InFptr
, 0, SEEK_END
);
280 FileSize
= ftell (InFptr
);
281 fseek (InFptr
, 0, SEEK_SET
);
284 // Per the PE/COFF specification, at offset 0x3C in the file is a 32-bit
285 // offset (from the start of the file) to the PE signature, which always
286 // follows the MSDOS stub. The PE signature is immediately followed by the
290 if (fseek (InFptr
, 0x3C, SEEK_SET
) != 0) {
291 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature in file", NULL
);
295 if (fread (&PESigOffset
, sizeof (PESigOffset
), 1, InFptr
) != 1) {
296 Error (NULL
, 0, 0, InFileName
, "failed to read PE signature offset from file");
300 if (fseek (InFptr
, PESigOffset
+ 4, SEEK_SET
) != 0) {
301 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature");
305 // We should now be at the COFF file header. Read it in and verify it's
306 // of an image type we support.
308 if (fread (&FileHeader
, sizeof (EFI_IMAGE_FILE_HEADER
), 1, InFptr
) != 1) {
309 Error (NULL
, 0, 0, InFileName
, "failed to read file header from image");
313 if ((FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA32
) && (FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA64
)) {
314 Error (NULL
, 0, 0, InFileName
, "image is of an unsupported machine type 0x%X", (UINT32
) FileHeader
.Machine
);
318 // Calculate the total number of bytes we're going to strip off. The '4' is for the
319 // PE signature PE\0\0. Then sanity check the size.
321 BytesStripped
= PESigOffset
+ 4 + sizeof (EFI_IMAGE_FILE_HEADER
) + FileHeader
.SizeOfOptionalHeader
;
322 if (BytesStripped
>= FileSize
) {
323 Error (NULL
, 0, 0, InFileName
, "attempt to strip more bytes than the total file size");
327 if (BytesStripped
&~0xFFFF) {
328 Error (NULL
, 0, 0, InFileName
, "attempt to strip more than 64K bytes", NULL
);
332 TEImageHeader
.StrippedSize
= (UINT16
) BytesStripped
;
335 // Read in the optional header. Assume PE32, and if not, then re-read as PE32+
337 SaveFilePosition
= ftell (InFptr
);
338 if (fread (&OptionalHeader32
, sizeof (EFI_IMAGE_OPTIONAL_HEADER32
), 1, InFptr
) != 1) {
339 Error (NULL
, 0, 0, InFileName
, "failed to read optional header from input file");
343 if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
345 // Fill in our new header with required data directory entries
347 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader32
.AddressOfEntryPoint
;
349 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
351 // We're going to pack the subsystem into 1 byte. Make sure it fits
353 if (OptionalHeader32
.Subsystem
&~0xFF) {
360 "image subsystem 0x%X cannot be packed into 1 byte",
361 (UINT32
) OptionalHeader32
.Subsystem
366 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader32
.Subsystem
;
367 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
368 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader32
.ImageBase
+ TEImageHeader
.StrippedSize
- sizeof (EFI_TE_IMAGE_HEADER
));
369 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
370 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
371 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
374 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
375 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
376 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
378 } else if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
380 // Rewind and re-read the optional header
382 fseek (InFptr
, SaveFilePosition
, SEEK_SET
);
383 if (fread (&OptionalHeader64
, sizeof (EFI_IMAGE_OPTIONAL_HEADER64
), 1, InFptr
) != 1) {
384 Error (NULL
, 0, 0, InFileName
, "failed to re-read optional header from input file");
388 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader64
.AddressOfEntryPoint
;
390 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
392 // We're going to pack the subsystem into 1 byte. Make sure it fits
394 if (OptionalHeader64
.Subsystem
&~0xFF) {
401 "image subsystem 0x%X cannot be packed into 1 byte",
402 (UINT32
) OptionalHeader64
.Subsystem
407 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader64
.Subsystem
;
408 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
409 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader64
.ImageBase
+ TEImageHeader
.StrippedSize
- sizeof (EFI_TE_IMAGE_HEADER
));
410 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
411 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
412 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
415 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
416 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
417 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
425 "unsupported magic number 0x%X found in optional header",
426 (UINT32
) OptionalHeader32
.Magic
431 // Fill in the remainder of our new image header
433 TEImageHeader
.Signature
= EFI_TE_IMAGE_HEADER_SIGNATURE
;
434 TEImageHeader
.Machine
= FileHeader
.Machine
;
436 // We're going to pack the number of sections into a single byte. Make sure it fits.
438 if (FileHeader
.NumberOfSections
&~0xFF) {
445 "image's number of sections 0x%X cannot be packed into 1 byte",
446 (UINT32
) FileHeader
.NumberOfSections
451 TEImageHeader
.NumberOfSections
= (UINT8
) FileHeader
.NumberOfSections
;
454 // Now open our output file
456 if ((OutFptr
= fopen (OutFileName
, "wb")) == NULL
) {
457 Error (NULL
, 0, 0, OutFileName
, "failed to open output file for writing");
461 // Write the TE header
463 if (fwrite (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, OutFptr
) != 1) {
464 Error (NULL
, 0, 0, "failed to write image header to output file", NULL
);
468 // Position into the input file, read the part we're not stripping, and
471 fseek (InFptr
, BytesStripped
, SEEK_SET
);
472 Buffer
= (UINT8
*) malloc (FileSize
- BytesStripped
);
473 if (Buffer
== NULL
) {
474 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
478 if (fread (Buffer
, FileSize
- BytesStripped
, 1, InFptr
) != 1) {
479 Error (NULL
, 0, 0, InFileName
, "failed to read remaining contents of input file");
483 if (fwrite (Buffer
, FileSize
- BytesStripped
, 1, OutFptr
) != 1) {
484 Error (NULL
, 0, 0, OutFileName
, "failed to write all bytes to output file");
488 Status
= STATUS_SUCCESS
;
491 if (InFptr
!= NULL
) {
495 // Close the output file. If there was an error, delete the output file so
496 // that a subsequent build will rebuild it.
498 if (OutFptr
!= NULL
) {
500 if (GetUtilityStatus () == STATUS_ERROR
) {
501 remove (OutFileName
);
506 // Free up our buffer
508 if (Buffer
!= NULL
) {
527 GC_TODO: Add function description
531 FileName - GC_TODO: add argument description
532 Fptr - GC_TODO: add argument description
533 MachineType - GC_TODO: add argument description
534 SubSystem - GC_TODO: add argument description
538 GC_TODO: add return values
546 Given a file pointer to a supposed PE32 image file, verify that it is indeed a
547 PE32 image file, and then return the machine type in the supplied pointer.
551 Fptr File pointer to the already-opened PE32 file
552 MachineType Location to stuff the machine type of the PE32 file. This is needed
553 because the image may be Itanium-based, IA32, or EBC.
561 EFI_IMAGE_DOS_HEADER DosHeader
;
562 EFI_IMAGE_FILE_HEADER FileHdr
;
563 EFI_IMAGE_OPTIONAL_HEADER OptionalHdr
;
567 Status
= STATUS_ERROR
;
569 // Position to the start of the file
571 fseek (Fptr
, 0, SEEK_SET
);
573 // Read the DOS header
575 if (fread (&DosHeader
, sizeof (DosHeader
), 1, Fptr
) != 1) {
576 Error (NULL
, 0, 0, FileName
, "failed to read the DOS stub from the input file");
580 // Check the magic number (0x5A4D)
582 if (DosHeader
.e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
583 Error (NULL
, 0, 0, FileName
, "input file does not appear to be a PE32 image (magic number)");
587 // Position into the file and check the PE signature
589 fseek (Fptr
, (long) DosHeader
.e_lfanew
, SEEK_SET
);
590 if (fread (&PESig
, sizeof (PESig
), 1, Fptr
) != 1) {
591 Error (NULL
, 0, 0, FileName
, "failed to read PE signature bytes");
595 // Check the PE signature in the header "PE\0\0"
597 if (PESig
!= EFI_IMAGE_NT_SIGNATURE
) {
598 Error (NULL
, 0, 0, FileName
, "file does not appear to be a PE32 image (signature)");
602 // Read the file header
604 if (fread (&FileHdr
, sizeof (FileHdr
), 1, Fptr
) != 1) {
605 Error (NULL
, 0, 0, FileName
, "failed to read PE file header from input file");
609 // Read the optional header so we can get the subsystem
611 if (fread (&OptionalHdr
, sizeof (OptionalHdr
), 1, Fptr
) != 1) {
612 Error (NULL
, 0, 0, FileName
, "failed to read COFF optional header from input file");
616 *SubSystem
= OptionalHdr
.Subsystem
;
617 if (mOptions
.Verbose
) {
618 fprintf (stdout
, " Got subsystem = 0x%X from image\n", (int) *SubSystem
);
623 Status
= STATUS_SUCCESS
;
625 fseek (Fptr
, 0, SEEK_SET
);
639 Given the Argc/Argv program arguments, and a pointer to an options structure,
640 parse the command-line options and check their validity.
645 Argc - standard C main() argument count
646 Argv - standard C main() argument list
650 STATUS_SUCCESS success
654 // GC_TODO: ] - add argument and description to function comment
657 // Clear out the options
659 memset ((char *) &mOptions
, 0, sizeof (mOptions
));
661 // Skip over the program name
666 // If no arguments, assume they want usage info
673 // Process until no more arguments
675 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
676 if (stricmp (Argv
[0], "-o") == 0) {
678 // Output filename specified with -o
679 // Make sure there's another parameter
682 strcpy (mOptions
.OutFileName
, Argv
[1]);
684 Error (NULL
, 0, 0, Argv
[0], "missing output file name with option");
691 } else if ((stricmp (Argv
[0], "-h") == 0) || (strcmp (Argv
[0], "-?") == 0)) {
697 } else if (stricmp (Argv
[0], "-v") == 0) {
701 mOptions
.Verbose
= 1;
702 } else if (stricmp (Argv
[0], "-dump") == 0) {
704 // -dump for dumping an image
708 Error (NULL
, 0, 0, Argv
[0], "unrecognized option");
719 // Better be one more arg for input file name
722 Error (NULL
, 0, 0, "input file name required", NULL
);
728 Error (NULL
, 0, 0, Argv
[1], "extra arguments on command line");
732 strcpy (mOptions
.InFileName
, Argv
[0]);
733 return STATUS_SUCCESS
;
745 Print usage information for this utility.
758 static const char *Msg
[] = {
759 UTILITY_NAME
" version "UTILITY_VERSION
" - TE image utility",
760 " Generate a TE image from an EFI PE32 image",
761 " Usage: "UTILITY_NAME
" {-v} {-dump} {-h|-?} {-o OutFileName} InFileName",
762 " [-e|-b] [FileName(s)]",
764 " -v - for verbose output",
765 " -dump - to dump the input file to a text file",
766 " -h -? - for this help information",
767 " -o OutFileName - to write output to OutFileName rather than InFileName"DEFAULT_OUTPUT_EXTENSION
,
768 " InFileName - name of the input PE32 file",
772 for (Index
= 0; Msg
[Index
] != NULL
; Index
++) {
773 fprintf (stdout
, "%s\n", Msg
[Index
]);
786 Dump a specified image information
790 FileName - File name pointer to the image to dump
799 EFI_TE_IMAGE_HEADER TEImageHeader
;
803 // Open the input file
807 if ((InFptr
= fopen (FileName
, "rb")) == NULL
) {
808 Error (NULL
, 0, 0, FileName
, "failed to open input file for reading");
812 if (fread (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, InFptr
) != 1) {
813 Error (NULL
, 0, 0, FileName
, "failed to read image header from input file");
817 if (TEImageHeader
.Signature
!= EFI_TE_IMAGE_HEADER_SIGNATURE
) {
818 Error (NULL
, 0, 0, FileName
, "Image does not appear to be a TE image (bad signature)");
824 fprintf (stdout
, "Header (%d bytes):\n", sizeof (EFI_TE_IMAGE_HEADER
));
825 fprintf (stdout
, " Signature: 0x%04X (TE)\n", (UINT32
) TEImageHeader
.Signature
);
826 NamePtr
= GetMachineTypeStr (TEImageHeader
.Machine
);
827 fprintf (stdout
, " Machine: 0x%04X (%s)\n", (UINT32
) TEImageHeader
.Machine
, NamePtr
);
828 NamePtr
= GetSubsystemTypeStr (TEImageHeader
.Subsystem
);
829 fprintf (stdout
, " Subsystem: 0x%02X (%s)\n", (UINT32
) TEImageHeader
.Subsystem
, NamePtr
);
830 fprintf (stdout
, " Number of sections 0x%02X\n", (UINT32
) TEImageHeader
.NumberOfSections
);
831 fprintf (stdout
, " Stripped size: 0x%04X\n", (UINT32
) TEImageHeader
.StrippedSize
);
832 fprintf (stdout
, " Entry point: 0x%08X\n", TEImageHeader
.AddressOfEntryPoint
);
833 fprintf (stdout
, " Base of code: 0x%08X\n", TEImageHeader
.BaseOfCode
);
834 fprintf (stdout
, " Data directories:\n");
837 " %8X [%8X] RVA [size] of Base Relocation Directory\n",
838 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
,
839 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
843 " %8X [%8X] RVA [size] of Debug Directory\n",
844 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
,
845 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
849 if (InFptr
!= NULL
) {
863 GC_TODO: Add function description
867 MachineType - GC_TODO: add argument description
871 GC_TODO: add return values
877 for (Index
= 0; mMachineTypes
[Index
].Name
!= NULL
; Index
++) {
878 if (mMachineTypes
[Index
].Value
== MachineType
) {
879 return mMachineTypes
[Index
].Name
;
888 GetSubsystemTypeStr (
895 GC_TODO: Add function description
899 SubsystemType - GC_TODO: add argument description
903 GC_TODO: add return values
909 for (Index
= 0; mSubsystemTypes
[Index
].Name
!= NULL
; Index
++) {
910 if (mSubsystemTypes
[Index
].Value
== SubsystemType
) {
911 return mSubsystemTypes
[Index
].Name
;