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_MAJOR_VERSION 0
38 #define UTILITY_MINOR_VERSION 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
137 GetSubsystemTypeStr (
152 Argc - standard C main() argument count
154 Argv - standard C main() argument list
162 // GC_TODO: ] - add argument and description to function comment
167 SetUtilityName (UTILITY_NAME
);
169 // Parse the command line arguments
171 if (ParseCommandLine (Argc
, Argv
)) {
175 // If dumping an image, then do that and quit
178 DumpImage (mOptions
.InFileName
);
182 // Determine the output filename. Either what they specified on
183 // the command line, or the first input filename with a different extension.
185 if (!mOptions
.OutFileName
[0]) {
186 strcpy (mOptions
.OutFileName
, mOptions
.InFileName
);
188 // Find the last . on the line and replace the filename extension with
191 for (Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
) - 1;
192 (Ext
>= mOptions
.OutFileName
) && (*Ext
!= '.') && (*Ext
!= '\\');
197 // If dot here, then insert extension here, otherwise append
200 Ext
= mOptions
.OutFileName
+ strlen (mOptions
.OutFileName
);
203 strcpy (Ext
, DEFAULT_OUTPUT_EXTENSION
);
206 // Make sure we don't have the same filename for input and output files
208 if (stricmp (mOptions
.OutFileName
, mOptions
.InFileName
) == 0) {
209 Error (NULL
, 0, 0, mOptions
.OutFileName
, "input and output file names must be different");
215 ProcessFile (mOptions
.InFileName
, mOptions
.OutFileName
);
217 Status
= GetUtilityStatus ();
231 Process a PE32 EFI file.
235 InFileName - the file name pointer to the input file
236 OutFileName - the file name pointer to the output file
240 STATUS_SUCCESS - the process has been finished successfully
241 STATUS_ERROR - error occured during the processing
250 EFI_TE_IMAGE_HEADER TEImageHeader
;
252 EFI_IMAGE_FILE_HEADER FileHeader
;
253 EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader32
;
254 EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader64
;
255 UINT32 BytesStripped
;
258 long SaveFilePosition
;
263 Status
= STATUS_ERROR
;
266 // Try to open the input file
268 if ((InFptr
= fopen (InFileName
, "rb")) == NULL
) {
269 Error (NULL
, 0, 0, InFileName
, "failed to open input file for reading");
273 // Double-check the file to make sure it's what we expect it to be
276 if (CheckPE32File (InFileName
, InFptr
, &MachineType
, &SubSystem
) != STATUS_SUCCESS
) {
280 // Initialize our new header
282 memset (&TEImageHeader
, 0, sizeof (EFI_TE_IMAGE_HEADER
));
285 // Seek to the end to get the file size
287 fseek (InFptr
, 0, SEEK_END
);
288 FileSize
= ftell (InFptr
);
289 fseek (InFptr
, 0, SEEK_SET
);
292 // Per the PE/COFF specification, at offset 0x3C in the file is a 32-bit
293 // offset (from the start of the file) to the PE signature, which always
294 // follows the MSDOS stub. The PE signature is immediately followed by the
298 if (fseek (InFptr
, 0x3C, SEEK_SET
) != 0) {
299 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature in file", NULL
);
303 if (fread (&PESigOffset
, sizeof (PESigOffset
), 1, InFptr
) != 1) {
304 Error (NULL
, 0, 0, InFileName
, "failed to read PE signature offset from file");
308 if (fseek (InFptr
, PESigOffset
+ 4, SEEK_SET
) != 0) {
309 Error (NULL
, 0, 0, InFileName
, "failed to seek to PE signature");
313 // We should now be at the COFF file header. Read it in and verify it's
314 // of an image type we support.
316 if (fread (&FileHeader
, sizeof (EFI_IMAGE_FILE_HEADER
), 1, InFptr
) != 1) {
317 Error (NULL
, 0, 0, InFileName
, "failed to read file header from image");
321 if ((FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA32
) && (FileHeader
.Machine
!= EFI_IMAGE_MACHINE_IA64
)) {
322 Error (NULL
, 0, 0, InFileName
, "image is of an unsupported machine type 0x%X", (UINT32
) FileHeader
.Machine
);
326 // Calculate the total number of bytes we're going to strip off. The '4' is for the
327 // PE signature PE\0\0. Then sanity check the size.
329 BytesStripped
= PESigOffset
+ 4 + sizeof (EFI_IMAGE_FILE_HEADER
) + FileHeader
.SizeOfOptionalHeader
;
330 if (BytesStripped
>= FileSize
) {
331 Error (NULL
, 0, 0, InFileName
, "attempt to strip more bytes than the total file size");
335 if (BytesStripped
&~0xFFFF) {
336 Error (NULL
, 0, 0, InFileName
, "attempt to strip more than 64K bytes", NULL
);
340 TEImageHeader
.StrippedSize
= (UINT16
) BytesStripped
;
343 // Read in the optional header. Assume PE32, and if not, then re-read as PE32+
345 SaveFilePosition
= ftell (InFptr
);
346 if (fread (&OptionalHeader32
, sizeof (EFI_IMAGE_OPTIONAL_HEADER32
), 1, InFptr
) != 1) {
347 Error (NULL
, 0, 0, InFileName
, "failed to read optional header from input file");
351 if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
) {
353 // Fill in our new header with required data directory entries
355 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader32
.AddressOfEntryPoint
;
357 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
359 // We're going to pack the subsystem into 1 byte. Make sure it fits
361 if (OptionalHeader32
.Subsystem
&~0xFF) {
368 "image subsystem 0x%X cannot be packed into 1 byte",
369 (UINT32
) OptionalHeader32
.Subsystem
374 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader32
.Subsystem
;
375 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
376 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader32
.ImageBase
);
377 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
378 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
379 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
382 if (OptionalHeader32
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
383 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
384 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader32
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
386 } else if (OptionalHeader32
.Magic
== EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
388 // Rewind and re-read the optional header
390 fseek (InFptr
, SaveFilePosition
, SEEK_SET
);
391 if (fread (&OptionalHeader64
, sizeof (EFI_IMAGE_OPTIONAL_HEADER64
), 1, InFptr
) != 1) {
392 Error (NULL
, 0, 0, InFileName
, "failed to re-read optional header from input file");
396 TEImageHeader
.AddressOfEntryPoint
= OptionalHeader64
.AddressOfEntryPoint
;
398 // - BytesStripped + sizeof (EFI_TE_IMAGE_HEADER);
400 // We're going to pack the subsystem into 1 byte. Make sure it fits
402 if (OptionalHeader64
.Subsystem
&~0xFF) {
409 "image subsystem 0x%X cannot be packed into 1 byte",
410 (UINT32
) OptionalHeader64
.Subsystem
415 TEImageHeader
.Subsystem
= (UINT8
) OptionalHeader64
.Subsystem
;
416 TEImageHeader
.BaseOfCode
= OptionalHeader32
.BaseOfCode
;
417 TEImageHeader
.ImageBase
= (UINT64
) (OptionalHeader64
.ImageBase
);
418 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
419 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
;
420 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
;
423 if (OptionalHeader64
.NumberOfRvaAndSizes
> EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
) {
424 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
;
425 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
= OptionalHeader64
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
;
433 "unsupported magic number 0x%X found in optional header",
434 (UINT32
) OptionalHeader32
.Magic
439 // Fill in the remainder of our new image header
441 TEImageHeader
.Signature
= EFI_TE_IMAGE_HEADER_SIGNATURE
;
442 TEImageHeader
.Machine
= FileHeader
.Machine
;
444 // We're going to pack the number of sections into a single byte. Make sure it fits.
446 if (FileHeader
.NumberOfSections
&~0xFF) {
453 "image's number of sections 0x%X cannot be packed into 1 byte",
454 (UINT32
) FileHeader
.NumberOfSections
459 TEImageHeader
.NumberOfSections
= (UINT8
) FileHeader
.NumberOfSections
;
462 // Now open our output file
464 if ((OutFptr
= fopen (OutFileName
, "wb")) == NULL
) {
465 Error (NULL
, 0, 0, OutFileName
, "failed to open output file for writing");
469 // Write the TE header
471 if (fwrite (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, OutFptr
) != 1) {
472 Error (NULL
, 0, 0, "failed to write image header to output file", NULL
);
476 // Position into the input file, read the part we're not stripping, and
479 fseek (InFptr
, BytesStripped
, SEEK_SET
);
480 Buffer
= (UINT8
*) malloc (FileSize
- BytesStripped
);
481 if (Buffer
== NULL
) {
482 Error (NULL
, 0, 0, "application error", "failed to allocate memory");
486 if (fread (Buffer
, FileSize
- BytesStripped
, 1, InFptr
) != 1) {
487 Error (NULL
, 0, 0, InFileName
, "failed to read remaining contents of input file");
491 if (fwrite (Buffer
, FileSize
- BytesStripped
, 1, OutFptr
) != 1) {
492 Error (NULL
, 0, 0, OutFileName
, "failed to write all bytes to output file");
496 Status
= STATUS_SUCCESS
;
499 if (InFptr
!= NULL
) {
503 // Close the output file. If there was an error, delete the output file so
504 // that a subsequent build will rebuild it.
506 if (OutFptr
!= NULL
) {
508 if (GetUtilityStatus () == STATUS_ERROR
) {
509 remove (OutFileName
);
514 // Free up our buffer
516 if (Buffer
!= NULL
) {
535 GC_TODO: Add function description
539 FileName - GC_TODO: add argument description
540 Fptr - GC_TODO: add argument description
541 MachineType - GC_TODO: add argument description
542 SubSystem - GC_TODO: add argument description
546 GC_TODO: add return values
554 Given a file pointer to a supposed PE32 image file, verify that it is indeed a
555 PE32 image file, and then return the machine type in the supplied pointer.
559 Fptr File pointer to the already-opened PE32 file
560 MachineType Location to stuff the machine type of the PE32 file. This is needed
561 because the image may be Itanium-based, IA32, or EBC.
569 EFI_IMAGE_DOS_HEADER DosHeader
;
570 EFI_IMAGE_FILE_HEADER FileHdr
;
571 EFI_IMAGE_OPTIONAL_HEADER OptionalHdr
;
575 Status
= STATUS_ERROR
;
577 // Position to the start of the file
579 fseek (Fptr
, 0, SEEK_SET
);
581 // Read the DOS header
583 if (fread (&DosHeader
, sizeof (DosHeader
), 1, Fptr
) != 1) {
584 Error (NULL
, 0, 0, FileName
, "failed to read the DOS stub from the input file");
588 // Check the magic number (0x5A4D)
590 if (DosHeader
.e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
591 Error (NULL
, 0, 0, FileName
, "input file does not appear to be a PE32 image (magic number)");
595 // Position into the file and check the PE signature
597 fseek (Fptr
, (long) DosHeader
.e_lfanew
, SEEK_SET
);
598 if (fread (&PESig
, sizeof (PESig
), 1, Fptr
) != 1) {
599 Error (NULL
, 0, 0, FileName
, "failed to read PE signature bytes");
603 // Check the PE signature in the header "PE\0\0"
605 if (PESig
!= EFI_IMAGE_NT_SIGNATURE
) {
606 Error (NULL
, 0, 0, FileName
, "file does not appear to be a PE32 image (signature)");
610 // Read the file header
612 if (fread (&FileHdr
, sizeof (FileHdr
), 1, Fptr
) != 1) {
613 Error (NULL
, 0, 0, FileName
, "failed to read PE file header from input file");
617 // Read the optional header so we can get the subsystem
619 if (fread (&OptionalHdr
, sizeof (OptionalHdr
), 1, Fptr
) != 1) {
620 Error (NULL
, 0, 0, FileName
, "failed to read COFF optional header from input file");
625 // Check FileAlginment and SectionAlignment match or not
626 // Because TeImage header doesn't record filealginment and sectionalignment info,
627 // TeImage is used for PEIM and PeiCore XIP module.
628 // So, check alignment match before generate TeImage to check.
630 if (OptionalHdr
.SectionAlignment
!= OptionalHdr
.FileAlignment
) {
631 Error (NULL
, 0, 0, FileName
, "Section-Alignment and File-Alignment does not match");
635 *SubSystem
= OptionalHdr
.Subsystem
;
636 if (mOptions
.Verbose
) {
637 fprintf (stdout
, " Got subsystem = 0x%X from image\n", (int) *SubSystem
);
642 Status
= STATUS_SUCCESS
;
644 fseek (Fptr
, 0, SEEK_SET
);
658 Given the Argc/Argv program arguments, and a pointer to an options structure,
659 parse the command-line options and check their validity.
664 Argc - standard C main() argument count
665 Argv - standard C main() argument list
669 STATUS_SUCCESS success
673 // GC_TODO: ] - add argument and description to function comment
676 // Clear out the options
678 memset ((char *) &mOptions
, 0, sizeof (mOptions
));
680 // Skip over the program name
685 // If no arguments, assume they want usage info
692 if ((strcmp(Argv
[0], "-h") == 0) || (strcmp(Argv
[0], "--help") == 0) ||
693 (strcmp(Argv
[0], "-?") == 0) || (strcmp(Argv
[0], "/?") == 0)) {
698 if ((strcmp(Argv
[0], "-V") == 0) || (strcmp(Argv
[0], "--version") == 0)) {
704 // Process until no more arguments
706 while ((Argc
> 0) && (Argv
[0][0] == '-')) {
707 if (stricmp (Argv
[0], "-o") == 0) {
709 // Output filename specified with -o
710 // Make sure there's another parameter
713 strcpy (mOptions
.OutFileName
, Argv
[1]);
715 Error (NULL
, 0, 0, Argv
[0], "missing output file name with option");
722 } else if ((stricmp (Argv
[0], "-h") == 0) || (strcmp (Argv
[0], "-?") == 0)) {
728 } else if (stricmp (Argv
[0], "-v") == 0) {
732 mOptions
.Verbose
= 1;
733 } else if (stricmp (Argv
[0], "-dump") == 0) {
735 // -dump for dumping an image
739 Error (NULL
, 0, 0, Argv
[0], "unrecognized option");
750 // Better be one more arg for input file name
753 Error (NULL
, 0, 0, "input file name required", NULL
);
759 Error (NULL
, 0, 0, Argv
[1], "extra arguments on command line");
763 strcpy (mOptions
.InFileName
, Argv
[0]);
764 return STATUS_SUCCESS
;
776 Displays the standard utility information to SDTOUT
788 printf ("%s v%d.%d -Utility to generate a TE image from an EFI PE32 image.\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
);
789 printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n");
801 Print usage information for this utility.
814 static const char *Msg
[] = {
815 "\nUsage: "UTILITY_NAME
" {-v} {-dump} {-h|-?} {-o OutFileName} InFileName",
816 " [-e|-b] [FileName(s)]",
818 " -h,--help,-?,/? to display help messages",
819 " -V,--version to display version information",
820 " -v - for verbose output",
821 " -dump - to dump the input file to a text file",
822 " -o OutFileName - to write output to OutFileName rather than InFileName"DEFAULT_OUTPUT_EXTENSION
,
823 " InFileName - name of the input PE32 file",
830 for (Index
= 0; Msg
[Index
] != NULL
; Index
++) {
831 fprintf (stdout
, "%s\n", Msg
[Index
]);
844 Dump a specified image information
848 FileName - File name pointer to the image to dump
857 EFI_TE_IMAGE_HEADER TEImageHeader
;
861 // Open the input file
865 if ((InFptr
= fopen (FileName
, "rb")) == NULL
) {
866 Error (NULL
, 0, 0, FileName
, "failed to open input file for reading");
870 if (fread (&TEImageHeader
, sizeof (EFI_TE_IMAGE_HEADER
), 1, InFptr
) != 1) {
871 Error (NULL
, 0, 0, FileName
, "failed to read image header from input file");
875 if (TEImageHeader
.Signature
!= EFI_TE_IMAGE_HEADER_SIGNATURE
) {
876 Error (NULL
, 0, 0, FileName
, "Image does not appear to be a TE image (bad signature)");
882 fprintf (stdout
, "Header (%d bytes):\n", sizeof (EFI_TE_IMAGE_HEADER
));
883 fprintf (stdout
, " Signature: 0x%04X (TE)\n", (UINT32
) TEImageHeader
.Signature
);
884 NamePtr
= GetMachineTypeStr (TEImageHeader
.Machine
);
885 fprintf (stdout
, " Machine: 0x%04X (%s)\n", (UINT32
) TEImageHeader
.Machine
, NamePtr
);
886 NamePtr
= GetSubsystemTypeStr (TEImageHeader
.Subsystem
);
887 fprintf (stdout
, " Subsystem: 0x%02X (%s)\n", (UINT32
) TEImageHeader
.Subsystem
, NamePtr
);
888 fprintf (stdout
, " Number of sections 0x%02X\n", (UINT32
) TEImageHeader
.NumberOfSections
);
889 fprintf (stdout
, " Stripped size: 0x%04X\n", (UINT32
) TEImageHeader
.StrippedSize
);
890 fprintf (stdout
, " Entry point: 0x%08X\n", TEImageHeader
.AddressOfEntryPoint
);
891 fprintf (stdout
, " Base of code: 0x%08X\n", TEImageHeader
.BaseOfCode
);
892 fprintf (stdout
, " Data directories:\n");
895 " %8X [%8X] RVA [size] of Base Relocation Directory\n",
896 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].VirtualAddress
,
897 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
901 " %8X [%8X] RVA [size] of Debug Directory\n",
902 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].VirtualAddress
,
903 TEImageHeader
.DataDirectory
[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG
].Size
907 if (InFptr
!= NULL
) {
921 GC_TODO: Add function description
925 MachineType - GC_TODO: add argument description
929 GC_TODO: add return values
935 for (Index
= 0; mMachineTypes
[Index
].Name
!= NULL
; Index
++) {
936 if (mMachineTypes
[Index
].Value
== MachineType
) {
937 return mMachineTypes
[Index
].Name
;
946 GetSubsystemTypeStr (
953 GC_TODO: Add function description
957 SubsystemType - GC_TODO: add argument description
961 GC_TODO: add return values
967 for (Index
= 0; mSubsystemTypes
[Index
].Name
!= NULL
; Index
++) {
968 if (mSubsystemTypes
[Index
].Value
== SubsystemType
) {
969 return mSubsystemTypes
[Index
].Name
;