3 Copyright (c) 1999 - 2007, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this 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.
28 #include "TianoCommon.h"
29 #include "EfiImage.h" // for PE32 structure definitions
30 #include "EfiUtilityMsgs.h"
33 // Version of this utility
35 #define UTILITY_NAME "GenTEImage"
36 #define UTILITY_VERSION "v0.11"
39 // Define the max length of a filename
42 #define DEFAULT_OUTPUT_EXTENSION ".te"
45 // Use this to track our command-line options and globals
48 INT8 OutFileName
[MAX_PATH
];
49 INT8 InFileName
[MAX_PATH
];
55 // Use these to convert from machine type value to a named type
62 static STRING_LOOKUP mMachineTypes
[] = {
63 EFI_IMAGE_MACHINE_IA32
,
65 EFI_IMAGE_MACHINE_IA64
,
67 EFI_IMAGE_MACHINE_X64
,
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 (
147 Argc - standard C main() argument count
149 Argv - standard C main() argument list
157 // GC_TODO: ] - add argument and description to function comment
162 SetUtilityName (UTILITY_NAME
);
164 // Parse the command line arguments
166 if (ParseCommandLine (Argc
, Argv
)) {
170 // If dumping an image, then do that and quit
173 DumpImage (mOptions
.InFileName
);
177 // Determine the output filename. Either what they specified on
178 // the command line, or the first input filename with a different extension.
180 if (!mOptions
.OutFileName
[0]) {
181 strcpy (mOptions
.OutFileName
, mOptions
.InFileName
);
183 // Find the last . on the line and replace the filename extension with
186 for (Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
) - 1;
187 (Ext
>= mOptions
.OutFileName
) && (*Ext
!= '.') && (*Ext
!= '\\');
192 // If dot here, then insert extension here, otherwise append
195 Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
);
198 strcpy (Ext
, DEFAULT_OUTPUT_EXTENSION
);
201 // Make sure we don't have the same filename for input and output files
203 if (_stricmp (mOptions
.OutFileName
, mOptions
.InFileName
) == 0) {
204 Error (NULL
, 0, 0, mOptions
.OutFileName
, "input and output file names must be different");
210 ProcessFile (mOptions
.InFileName
, mOptions
.OutFileName
);
212 Status
= GetUtilityStatus ();
226 Process a PE32 EFI file.
230 InFileName - the file name pointer to the input file
231 OutFileName - the file name pointer to the output file
235 STATUS_SUCCESS - the process has been finished successfully
236 STATUS_ERROR - error occured during the processing
245 EFI_TE_IMAGE_HEADER TEImageHeader
;
247 EFI_IMAGE_FILE_HEADER FileHeader
;
248 EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader32
;
249 EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader64
;
250 UINT32 BytesStripped
;
253 long SaveFilePosition
;
258 Status
= STATUS_ERROR
;
261 // Try to open the input file
263 if ((InFptr
= fopen (InFileName
, "rb")) == NULL
) {
264 Error (NULL
, 0, 0, InFileName
, "failed to open input file for reading");
268 // Double-check the file to make sure it's what we expect it to be
270 if (CheckPE32File (InFileName
, InFptr
, &MachineType
, &SubSystem
) != STATUS_SUCCESS
) {
274 // Initialize our new header
276 memset (&TEImageHeader
, 0, sizeof (EFI_TE_IMAGE_HEADER
));
279 // Seek to the end to get the file size
281 fseek (InFptr
, 0, SEEK_END
);
282 FileSize
= ftell (InFptr
);
283 fseek (InFptr
, 0, SEEK_SET
);
286 // Per the PE/COFF specification, at offset 0x3C in the file is a 32-bit
287 // offset (from the start of the file) to the PE signature, which always
288 // follows the MSDOS stub. The PE signature is immediately followed by the
292 if (fseek (InFptr
, 0x3C, SEEK_SET
) != 0) {
293 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature in file", NULL
);
297 if (fread (&PESigOffset
, sizeof (PESigOffset
), 1, InFptr
) != 1) {
298 Error (NULL
, 0, 0, InFileName
, "failed to read PE signature offset from file");
302 if (fseek (InFptr
, PESigOffset
+ 4, SEEK_SET
) != 0) {
303 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature");
307 // We should now be at the COFF file header. Read it in and verify it's
308 // of an image type we support.
310 if (fread (&FileHeader
, sizeof (EFI_IMAGE_FILE_HEADER
), 1, InFptr
) != 1) {
311 Error (NULL
, 0, 0, InFileName
, "failed to read file header from image");
315 if ((FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA32
) &&
316 (FileHeader
.Machine
!= EFI_IMAGE_MACHINE_X64
) &&
317 (FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA64
)) {
318 Error (NULL
, 0, 0, InFileName
, "image is of an unsupported machine type 0x%X", (UINT32
) FileHeader
.Machine
);
322 // Calculate the total number of bytes we're going to strip off. The '4' is for the
323 // PE signature PE\0\0. Then sanity check the size.
325 BytesStripped
= PESigOffset
+ 4 + sizeof (EFI_IMAGE_FILE_HEADER
) + FileHeader
.SizeOfOptionalHeader
;
326 if (BytesStripped
>= FileSize
) {
327 Error (NULL
, 0, 0, InFileName
, "attempt to strip more bytes than the total file size");
331 if (BytesStripped
&~0xFFFF) {
332 Error (NULL
, 0, 0, InFileName
, "attempt to strip more than 64K bytes", NULL
);
336 TEImageHeader
.StrippedSize
= (UINT16
) BytesStripped
;
339 // Read in the optional header. Assume PE32, and if not, then re-read as PE32+
341 SaveFilePosition
= ftell (InFptr
);
342 if (fread (&OptionalHeader32
, sizeof (EFI_IMAGE_OPTIONAL_HEADER32
), 1, InFptr
) != 1) {
343 Error (NULL
, 0, 0, InFileName
, "failed to read optional header from input file");
347 if (OptionalHeader32
.SectionAlignment
!= OptionalHeader32
.FileAlignment
) {
348 Error (NULL
, 0, 0, InFileName
, "Section alignment is not same to file alignment.");
352 if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
354 // Fill in our new header with required data directory entries
356 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader32
.AddressOfEntryPoint
;
358 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
360 // We're going to pack the subsystem into 1 byte. Make sure it fits
362 if (OptionalHeader32
.Subsystem
&~0xFF) {
369 "image subsystem 0x%X cannot be packed into 1 byte",
370 (UINT32
) OptionalHeader32
.Subsystem
375 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader32
.Subsystem
;
376 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
377 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader32
.ImageBase
);
378 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
379 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
380 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
383 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
384 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
385 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
387 } else if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
389 // Rewind and re-read the optional header
391 fseek (InFptr
, SaveFilePosition
, SEEK_SET
);
392 if (fread (&OptionalHeader64
, sizeof (EFI_IMAGE_OPTIONAL_HEADER64
), 1, InFptr
) != 1) {
393 Error (NULL
, 0, 0, InFileName
, "failed to re-read optional header from input file");
397 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader64
.AddressOfEntryPoint
;
399 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
401 // We're going to pack the subsystem into 1 byte. Make sure it fits
403 if (OptionalHeader64
.Subsystem
&~0xFF) {
410 "image subsystem 0x%X cannot be packed into 1 byte",
411 (UINT32
) OptionalHeader64
.Subsystem
416 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader64
.Subsystem
;
417 TEImageHeader
.BaseOfCode
= OptionalHeader64
.BaseOfCode
;
418 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader64
.ImageBase
);
419 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
420 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
421 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
424 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
425 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
426 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
434 "unsupported magic number 0x%X found in optional header",
435 (UINT32
) OptionalHeader32
.Magic
440 // Fill in the remainder of our new image header
442 TEImageHeader
.Signature
= EFI_TE_IMAGE_HEADER_SIGNATURE
;
443 TEImageHeader
.Machine
= FileHeader
.Machine
;
445 // We're going to pack the number of sections into a single byte. Make sure it fits.
447 if (FileHeader
.NumberOfSections
&~0xFF) {
454 "image's number of sections 0x%X cannot be packed into 1 byte",
455 (UINT32
) FileHeader
.NumberOfSections
460 TEImageHeader
.NumberOfSections
= (UINT8
) FileHeader
.NumberOfSections
;
463 // Now open our output file
465 if ((OutFptr
= fopen (OutFileName
, "wb")) == NULL
) {
466 Error (NULL
, 0, 0, OutFileName
, "failed to open output file for writing");
470 // Write the TE header
472 if (fwrite (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, OutFptr
) != 1) {
473 Error (NULL
, 0, 0, "failed to write image header to output file", NULL
);
477 // Position into the input file, read the part we're not stripping, and
480 fseek (InFptr
, BytesStripped
, SEEK_SET
);
481 Buffer
= (UINT8
*) malloc (FileSize
- BytesStripped
);
482 if (Buffer
== NULL
) {
483 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
487 if (fread (Buffer
, FileSize
- BytesStripped
, 1, InFptr
) != 1) {
488 Error (NULL
, 0, 0, InFileName
, "failed to read remaining contents of input file");
492 if (fwrite (Buffer
, FileSize
- BytesStripped
, 1, OutFptr
) != 1) {
493 Error (NULL
, 0, 0, OutFileName
, "failed to write all bytes to output file");
497 Status
= STATUS_SUCCESS
;
500 if (InFptr
!= NULL
) {
504 // Close the output file. If there was an error, delete the output file so
505 // that a subsequent build will rebuild it.
507 if (OutFptr
!= NULL
) {
509 if (GetUtilityStatus () == STATUS_ERROR
) {
510 remove (OutFileName
);
515 // Free up our buffer
517 if (Buffer
!= NULL
) {
536 GC_TODO: Add function description
540 FileName - GC_TODO: add argument description
541 Fptr - GC_TODO: add argument description
542 MachineType - GC_TODO: add argument description
543 SubSystem - GC_TODO: add argument description
547 GC_TODO: add return values
555 Given a file pointer to a supposed PE32 image file, verify that it is indeed a
556 PE32 image file, and then return the machine type in the supplied pointer.
560 Fptr File pointer to the already-opened PE32 file
561 MachineType Location to stuff the machine type of the PE32 file. This is needed
562 because the image may be Itanium-based, IA32, or EBC.
570 EFI_IMAGE_DOS_HEADER DosHeader
;
571 EFI_IMAGE_FILE_HEADER FileHdr
;
572 EFI_IMAGE_OPTIONAL_HEADER OptionalHdr
;
576 Status
= STATUS_ERROR
;
578 // Position to the start of the file
580 fseek (Fptr
, 0, SEEK_SET
);
582 // Read the DOS header
584 if (fread (&DosHeader
, sizeof (DosHeader
), 1, Fptr
) != 1) {
585 Error (NULL
, 0, 0, FileName
, "failed to read the DOS stub from the input file");
589 // Check the magic number (0x5A4D)
591 if (DosHeader
.e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
592 Error (NULL
, 0, 0, FileName
, "input file does not appear to be a PE32 image (magic number)");
596 // Position into the file and check the PE signature
598 fseek (Fptr
, (long) DosHeader
.e_lfanew
, SEEK_SET
);
599 if (fread (&PESig
, sizeof (PESig
), 1, Fptr
) != 1) {
600 Error (NULL
, 0, 0, FileName
, "failed to read PE signature bytes");
604 // Check the PE signature in the header "PE\0\0"
606 if (PESig
!= EFI_IMAGE_NT_SIGNATURE
) {
607 Error (NULL
, 0, 0, FileName
, "file does not appear to be a PE32 image (signature)");
611 // Read the file header
613 if (fread (&FileHdr
, sizeof (FileHdr
), 1, Fptr
) != 1) {
614 Error (NULL
, 0, 0, FileName
, "failed to read PE file header from input file");
618 // Read the optional header so we can get the subsystem
620 if (fread (&OptionalHdr
, sizeof (OptionalHdr
), 1, Fptr
) != 1) {
621 Error (NULL
, 0, 0, FileName
, "failed to read COFF optional header from input file");
625 *SubSystem
= OptionalHdr
.Subsystem
;
626 if (mOptions
.Verbose
) {
627 fprintf (stdout
, " Got subsystem = 0x%X from image\n", (int) *SubSystem
);
632 Status
= STATUS_SUCCESS
;
634 fseek (Fptr
, 0, SEEK_SET
);
648 Given the Argc/Argv program arguments, and a pointer to an options structure,
649 parse the command-line options and check their validity.
654 Argc - standard C main() argument count
655 Argv - standard C main() argument list
659 STATUS_SUCCESS success
663 // GC_TODO: ] - add argument and description to function comment
666 // Clear out the options
668 memset ((char *) &mOptions
, 0, sizeof (mOptions
));
670 // Skip over the program name
675 // If no arguments, assume they want usage info
682 // Process until no more arguments
684 while ((Argc
> 0) && ((Argv
[0][0] == '-') || (Argv
[0][0] == '/'))) {
686 // To simplify string comparisons, replace slashes with dashes
689 if (_stricmp (Argv
[0], "-o") == 0) {
691 // Output filename specified with -o
692 // Make sure there's another parameter
695 strcpy (mOptions
.OutFileName
, Argv
[1]);
697 Error (NULL
, 0, 0, Argv
[0], "missing output file name with option");
704 } else if ((_stricmp (Argv
[0], "-h") == 0) || (strcmp (Argv
[0], "-?") == 0)) {
710 } else if (_stricmp (Argv
[0], "-v") == 0) {
714 mOptions
.Verbose
= 1;
715 } else if (_stricmp (Argv
[0], "-dump") == 0) {
717 // -dump for dumping an image
721 Error (NULL
, 0, 0, Argv
[0], "unrecognized option");
732 // Better be one more arg for input file name
735 Error (NULL
, 0, 0, "input file name required", NULL
);
741 Error (NULL
, 0, 0, Argv
[1], "extra arguments on command line");
745 strcpy (mOptions
.InFileName
, Argv
[0]);
746 return STATUS_SUCCESS
;
758 Print usage information for this utility.
771 static const char *Msg
[] = {
772 UTILITY_NAME
" version "UTILITY_VERSION
" - TE image utility",
773 " Generate a TE image from an EFI PE32 image",
774 " Usage: "UTILITY_NAME
" {-v} {-dump} {-h|-?} {-o OutFileName} InFileName",
775 " [-e|-b] [FileName(s)]",
777 " -v - for verbose output",
778 " -dump - to dump the input file to a text file",
779 " -h -? - for this help information",
780 " -o OutFileName - to write output to OutFileName rather than InFileName"DEFAULT_OUTPUT_EXTENSION
,
781 " InFileName - name of the input PE32 file",
785 for (Index
= 0; Msg
[Index
] != NULL
; Index
++) {
786 fprintf (stdout
, "%s\n", Msg
[Index
]);
799 Dump a specified image information
803 FileName - File name pointer to the image to dump
812 EFI_TE_IMAGE_HEADER TEImageHeader
;
816 // Open the input file
820 if ((InFptr
= fopen (FileName
, "rb")) == NULL
) {
821 Error (NULL
, 0, 0, FileName
, "failed to open input file for reading");
825 if (fread (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, InFptr
) != 1) {
826 Error (NULL
, 0, 0, FileName
, "failed to read image header from input file");
830 if (TEImageHeader
.Signature
!= EFI_TE_IMAGE_HEADER_SIGNATURE
) {
831 Error (NULL
, 0, 0, FileName
, "Image does not appear to be a TE image (bad signature)");
837 fprintf (stdout
, "Header (%d bytes):\n", sizeof (EFI_TE_IMAGE_HEADER
));
838 fprintf (stdout
, " Signature: 0x%04X (TE)\n", (UINT32
) TEImageHeader
.Signature
);
839 NamePtr
= GetMachineTypeStr (TEImageHeader
.Machine
);
840 fprintf (stdout
, " Machine: 0x%04X (%s)\n", (UINT32
) TEImageHeader
.Machine
, NamePtr
);
841 NamePtr
= GetSubsystemTypeStr (TEImageHeader
.Subsystem
);
842 fprintf (stdout
, " Subsystem: 0x%02X (%s)\n", (UINT32
) TEImageHeader
.Subsystem
, NamePtr
);
843 fprintf (stdout
, " Number of sections 0x%02X\n", (UINT32
) TEImageHeader
.NumberOfSections
);
844 fprintf (stdout
, " Stripped size: 0x%04X\n", (UINT32
) TEImageHeader
.StrippedSize
);
845 fprintf (stdout
, " Entry point: 0x%08X\n", TEImageHeader
.AddressOfEntryPoint
);
846 fprintf (stdout
, " Base of code: 0x%08X\n", TEImageHeader
.BaseOfCode
);
847 fprintf (stdout
, " Data directories:\n");
850 " %8X [%8X] RVA [size] of Base Relocation Directory\n",
851 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
,
852 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
856 " %8X [%8X] RVA [size] of Debug Directory\n",
857 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
,
858 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
862 if (InFptr
!= NULL
) {
876 GC_TODO: Add function description
880 MachineType - GC_TODO: add argument description
884 GC_TODO: add return values
890 for (Index
= 0; mMachineTypes
[Index
].Name
!= NULL
; Index
++) {
891 if (mMachineTypes
[Index
].Value
== MachineType
) {
892 return mMachineTypes
[Index
].Name
;
901 GetSubsystemTypeStr (
908 GC_TODO: Add function description
912 SubsystemType - GC_TODO: add argument description
916 GC_TODO: add return values
922 for (Index
= 0; mSubsystemTypes
[Index
].Name
!= NULL
; Index
++) {
923 if (mSubsystemTypes
[Index
].Value
== SubsystemType
) {
924 return mSubsystemTypes
[Index
].Name
;