/** @file
Converts a pe32+ image to an FW, Te image type, or other specific image.

Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "WinNtInclude.h"

#ifndef __GNUC__
#include <windows.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include <Common/UefiBaseTypes.h>
#include <IndustryStandard/PeImage.h>
#include <Common/UefiInternalFormRepresentation.h>

//
// Acpi Table definition
//
#include <IndustryStandard/Acpi.h>
#include <IndustryStandard/Acpi1_0.h>
#include <IndustryStandard/Acpi2_0.h>
#include <IndustryStandard/Acpi3_0.h>
#include <IndustryStandard/MemoryMappedConfigurationSpaceAccessTable.h>

#include "CommonLib.h"
#include "PeCoffLib.h"
#include "ParseInf.h"
#include "EfiUtilityMsgs.h"

#include "GenFw.h"

//
// Version of this utility
//
#define UTILITY_NAME "GenFw"
#define UTILITY_MAJOR_VERSION 0
#define UTILITY_MINOR_VERSION 2

#define HII_RESOURCE_SECTION_INDEX  1
#define HII_RESOURCE_SECTION_NAME   "HII"

#define DEFAULT_MC_PAD_BYTE_VALUE  0xFF
#define DEFAULT_MC_ALIGNMENT       16

#define STATUS_IGNORE 0xA
//
// Structure definition for a microcode header
//
typedef struct {
  UINT32  HeaderVersion;
  UINT32  PatchId;
  UINT32  Date;
  UINT32  CpuId;
  UINT32  Checksum;
  UINT32  LoaderVersion;
  UINT32  PlatformId;
  UINT32  DataSize;   // if 0, then TotalSize = 2048, and TotalSize field is invalid
  UINT32  TotalSize;  // number of bytes
  UINT32  Reserved[3];
} MICROCODE_IMAGE_HEADER;

static EFI_GUID mZeroGuid = {0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};

static const char *gHiiPackageRCFileHeader[] = {
  "//",
  "//  DO NOT EDIT -- auto-generated file",
  "//",
  NULL
};

//
// Module image information
//
CHAR8  *mInImageName;
UINT32 mImageTimeStamp = 0;
UINT32 mImageSize = 0;
UINT32 mOutImageType = FW_DUMMY_IMAGE;
BOOLEAN mIsConvertXip = FALSE;


STATIC
EFI_STATUS
ZeroDebugData (
  IN OUT UINT8   *FileBuffer,
  BOOLEAN        ZeroDebug
  );

STATIC
EFI_STATUS
SetStamp (
  IN OUT UINT8  *FileBuffer,
  IN     CHAR8  *TimeStamp
  );

STATIC
STATUS
MicrocodeReadData (
  FILE          *InFptr,
  UINT32        *Data
  );

STATIC
VOID
Version (
  VOID
  )
/*++

Routine Description:

  Print out version information for this utility.

Arguments:

  None

Returns:

  None

--*/
{
  fprintf (stdout, "%s Version %d.%d %s \n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION);
}

STATIC
VOID
Usage (
  VOID
  )
/*++

Routine Description:

  Print Help message.

Arguments:

  VOID

Returns:

  None

--*/
{
  //
  // Summary usage
  //
  fprintf (stdout, "\nUsage: %s [options] <input_file>\n\n", UTILITY_NAME);

  //
  // Copyright declaration
  //
  fprintf (stdout, "Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.\n\n");

  //
  // Details Option
  //
  fprintf (stdout, "Options:\n");
  fprintf (stdout, "  -o FileName, --outputfile FileName\n\
                        File will be created to store the output content.\n");
  fprintf (stdout, "  -e EFI_FILETYPE, --efiImage EFI_FILETYPE\n\
                        Create Efi Image. EFI_FILETYPE is one of BASE,SMM_CORE,\n\
                        PEI_CORE, PEIM, DXE_CORE, DXE_DRIVER, UEFI_APPLICATION,\n\
                        SEC, DXE_SAL_DRIVER, UEFI_DRIVER, DXE_RUNTIME_DRIVER,\n\
                        DXE_SMM_DRIVER, SECURITY_CORE, COMBINED_PEIM_DRIVER,\n\
                        MM_STANDALONE, MM_CORE_STANDALONE,\n\
                        PIC_PEIM, RELOCATABLE_PEIM, BS_DRIVER, RT_DRIVER,\n\
                        APPLICATION, SAL_RT_DRIVER to support all module types\n\
                        It can only be used together with --keepexceptiontable,\n\
                        --keepzeropending, --keepoptionalheader, -r, -o option.\n\
                        It is a action option. If it is combined with other action options,\n\
                        the later input action option will override the previous one.\n");
  fprintf (stdout, "  -c, --acpi            Create Acpi table.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -t, --terse           Create Te Image.\n\
                        It can only be used together with --keepexceptiontable,\n\
                        --keepzeropending, --keepoptionalheader, -r, -o option.\n\
                        It is a action option. If it is combined with other action options,\n\
                        the later input action option will override the previous one.\n");
  fprintf (stdout, "  -u, --dump            Dump TeImage Header.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -z, --zero            Zero the Debug Data Fields in the PE input image file.\n\
                        It also zeros the time stamp fields.\n\
                        This option can be used to compare the binary efi image.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -b, --exe2bin         Convert the input EXE to the output BIN file.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");;
  fprintf (stdout, "  -l, --stripped        Strip off the relocation info from PE or TE image.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -s timedate, --stamp timedate\n\
                        timedate format is \"yyyy-mm-dd 00:00:00\". if timedata \n\
                        is set to NOW, current system time is used. The support\n\
                        date scope is 1970-01-01 00+timezone:00:00\n\
                        ~ 2038-01-19 03+timezone:14:07\n\
                        The scope is adjusted according to the different zones.\n\
                        It can't be combined with other action options\n\
                        except for -o, -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -m, --mcifile         Convert input microcode txt file to microcode bin file.\n\
                        It can't be combined with other action options\n\
                        except for -o option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -j, --join            Combine multi microcode bin files to one file.\n\
                        It can be specified with -a, -p, -o option.\n\
                        No other options can be combined with it.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -a NUM, --align NUM   NUM is one HEX or DEC format alignment value.\n\
                        This option is only used together with -j option.\n");
  fprintf (stdout, "  -p NUM, --pad NUM     NUM is one HEX or DEC format padding value.\n\
                        This option is only used together with -j option.\n");
  fprintf (stdout, "  --keepexceptiontable  Don't clear exception table.\n\
                        This option can be used together with -e or -t.\n\
                        It doesn't work for other options.\n");
  fprintf (stdout, "  --keepoptionalheader  Don't zero PE/COFF optional header fields.\n\
                        This option can be used together with -e or -t.\n\
                        It doesn't work for other options.\n");
  fprintf (stdout, "  --keepzeropending     Don't strip zero pending of .reloc.\n\
                        This option can be used together with -e or -t.\n\
                        It doesn't work for other options.\n");
  fprintf (stdout, "  -r, --replace         Overwrite the input file with the output content.\n\
                        If more input files are specified,\n\
                        the last input file will be as the output file.\n");
  fprintf (stdout, "  -g HiiPackageListGuid, --hiiguid HiiPackageListGuid\n\
                        Guid is used to specify hii package list guid.\n\
                        Its format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\
                        If not specified, the first Form FormSet guid is used.\n");
  fprintf (stdout, "  --hiipackage          Combine all input binary hii packages into \n\
                        a single package list as the text resource data(RC).\n\
                        It can't be combined with other action options\n\
                        except for -o option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  --hiibinpackage       Combine all input binary hii packages into \n\
                        a single package list as the binary resource section.\n\
                        It can't be combined with other action options\n\
                        except for -o option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  --rebase NewAddress   Rebase image to new base address. New address \n\
                        is also set to the first none code section header.\n\
                        It can't be combined with other action options\n\
                        except for -o or -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  --address NewAddress  Set new address into the first none code \n\
                        section header of the input image.\n\
                        It can't be combined with other action options\n\
                        except for -o or -r option. It is a action option.\n\
                        If it is combined with other action options, the later\n\
                        input action option will override the previous one.\n");
  fprintf (stdout, "  -v, --verbose         Turn on verbose output with informational messages.\n");
  fprintf (stdout, "  -q, --quiet           Disable all messages except key message and fatal error\n");
  fprintf (stdout, "  -d, --debug level     Enable debug messages, at input debug level.\n");
  fprintf (stdout, "  --version             Show program's version number and exit\n");
  fprintf (stdout, "  -h, --help            Show this help message and exit\n");
}

STATIC
STATUS
CheckAcpiTable (
  VOID      *AcpiTable,
  UINT32    Length
  )
/*++

Routine Description:

  Check Acpi Table

Arguments:

  AcpiTable     Buffer for AcpiSection
  Length        AcpiSection Length

Returns:

  0             success
  non-zero      otherwise

--*/
{
  EFI_ACPI_DESCRIPTION_HEADER                   *AcpiHeader;
  EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
  UINT32                                        ExpectedLength;

  AcpiHeader = (EFI_ACPI_DESCRIPTION_HEADER *)AcpiTable;

  //
  // Generic check for AcpiTable length.
  //
  if (AcpiHeader->Length > Length) {
    Error (NULL, 0, 3000, "Invalid", "AcpiTable length check failed.", NULL);
    return STATUS_ERROR;
  }

  //
  // Currently, we only check must-have tables: FADT, FACS, DSDT,
  // and some important tables: MADT, MCFG.
  //
  switch (AcpiHeader->Signature) {

  //
  // "FACP" Fixed ACPI Description Table
  //
  case EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE:
    switch (AcpiHeader->Revision) {
    case EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION:
      ExpectedLength = sizeof(EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE);
      break;
    case EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION:
      ExpectedLength = sizeof(EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE);
      break;
    case EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION:
      ExpectedLength = sizeof(EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE);
      break;
    default:
      if (AcpiHeader->Revision > EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
        ExpectedLength = AcpiHeader->Length;
        break;
      }
      Error (NULL, 0, 3000, "Invalid", "FACP revision check failed.");
      return STATUS_ERROR;
    }
    if (ExpectedLength != AcpiHeader->Length) {
      Error (NULL, 0, 3000, "Invalid", "FACP length check failed.");
      return STATUS_ERROR;
    }
    break;

  //
  // "FACS" Firmware ACPI Control Structure
  //
  case EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE:
    Facs = (EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)AcpiTable;
    if (Facs->Version > EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) {
      break;
    }
    if ((Facs->Version != EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
        (Facs->Version != EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
        (Facs->Version != EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION)){
      Error (NULL, 0, 3000, "Invalid", "FACS version check failed.");
      return STATUS_ERROR;
    }
    if ((Facs->Length != sizeof(EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE)) &&
        (Facs->Length != sizeof(EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE)) &&
        (Facs->Length != sizeof(EFI_ACPI_3_0_FIRMWARE_ACPI_CONTROL_STRUCTURE))) {
      Error (NULL, 0, 3000, "Invalid", "FACS length check failed.");
      return STATUS_ERROR;
    }
    break;

  //
  // "DSDT" Differentiated System Description Table
  //
  case EFI_ACPI_3_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:
    if (AcpiHeader->Revision > EFI_ACPI_3_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_REVISION) {
      break;
    }
    if (AcpiHeader->Length <= sizeof(EFI_ACPI_DESCRIPTION_HEADER)) {
      Error (NULL, 0, 3000, "Invalid", "DSDT length check failed.");
      return STATUS_ERROR;
    }
    break;

  //
  // "APIC" Multiple APIC Description Table
  //
  case EFI_ACPI_3_0_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE:
    if (AcpiHeader->Revision > EFI_ACPI_3_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) {
      break;
    }
    if ((AcpiHeader->Revision != EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) &&
        (AcpiHeader->Revision != EFI_ACPI_2_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) &&
        (AcpiHeader->Revision != EFI_ACPI_3_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION)) {
      Error (NULL, 0, 3000, "Invalid", "APIC revision check failed.");
      return STATUS_ERROR;
    }
    if (AcpiHeader->Length <= sizeof(EFI_ACPI_DESCRIPTION_HEADER) + sizeof(UINT32) + sizeof(UINT32)) {
      Error (NULL, 0, 3000, "Invalid", "APIC length check failed.");
      return STATUS_ERROR;
    }
    break;

  //
  // "MCFG" PCI Express Memory Mapped Configuration Space Base Address Description Table
  //
  case EFI_ACPI_3_0_PCI_EXPRESS_MEMORY_MAPPED_CONFIGURATION_SPACE_BASE_ADDRESS_DESCRIPTION_TABLE_SIGNATURE:
    if (AcpiHeader->Revision > EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION) {
      break;
    }
    if (AcpiHeader->Revision != EFI_ACPI_MEMORY_MAPPED_CONFIGURATION_SPACE_ACCESS_TABLE_REVISION) {
      Error (NULL, 0, 3000, "Invalid", "MCFG revision check failed.");
      return STATUS_ERROR;
    }
    if (AcpiHeader->Length <= sizeof(EFI_ACPI_DESCRIPTION_HEADER) + sizeof(UINT64)) {
      Error (NULL, 0, 3000, "Invalid", "MCFG length check failed.");
      return STATUS_ERROR;
    }
    break;

  //
  // Other table pass check
  //
  default:
    break;
  }

  return STATUS_SUCCESS;
}

VOID
SetHiiResourceHeader (
  UINT8   *HiiBinData,
  UINT32  OffsetToFile
  )
{
  UINT32  Index;
  EFI_IMAGE_RESOURCE_DIRECTORY        *ResourceDirectory;
  EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY  *ResourceDirectoryEntry;
  EFI_IMAGE_RESOURCE_DIRECTORY_STRING *ResourceDirectoryString;
  EFI_IMAGE_RESOURCE_DATA_ENTRY       *ResourceDataEntry;

  //
  // Fill Resource section entry
  //
  ResourceDirectory      = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiBinData);
  ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1);
  for (Index = 0; Index < ResourceDirectory->NumberOfNamedEntries; Index ++) {
    if (ResourceDirectoryEntry->u1.s.NameIsString) {
      ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (HiiBinData + ResourceDirectoryEntry->u1.s.NameOffset);

      if (ResourceDirectoryString->Length == 3 &&
          ResourceDirectoryString->String[0] == L'H' &&
          ResourceDirectoryString->String[1] == L'I' &&
          ResourceDirectoryString->String[2] == L'I') {
        //
        // Resource Type "HII" found
        //
        if (ResourceDirectoryEntry->u2.s.DataIsDirectory) {
          //
          // Move to next level - resource Name
          //
          ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiBinData + ResourceDirectoryEntry->u2.s.OffsetToDirectory);
          ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1);

          if (ResourceDirectoryEntry->u2.s.DataIsDirectory) {
            //
            // Move to next level - resource Language
            //
            ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiBinData + ResourceDirectoryEntry->u2.s.OffsetToDirectory);
            ResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (ResourceDirectory + 1);
          }
        }

        //
        // Now it ought to be resource Data and update its OffsetToData value
        //
        if (!ResourceDirectoryEntry->u2.s.DataIsDirectory) {
          ResourceDataEntry = (EFI_IMAGE_RESOURCE_DATA_ENTRY *) (HiiBinData + ResourceDirectoryEntry->u2.OffsetToData);
          ResourceDataEntry->OffsetToData = ResourceDataEntry->OffsetToData + OffsetToFile;
          break;
        }
      }
    }
    ResourceDirectoryEntry++;
  }

  return;
}

EFI_IMAGE_OPTIONAL_HEADER_UNION *
GetPeCoffHeader (
  void *Data
  )
{
  EFI_IMAGE_DOS_HEADER             *DosHdr;
  EFI_IMAGE_OPTIONAL_HEADER_UNION  *PeHdr;

  //
  // Read the dos & pe hdrs of the image
  //
  DosHdr = (EFI_IMAGE_DOS_HEADER *)Data;
  if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
    // NO DOS header, check for PE/COFF header
    PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(Data);
    if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
      return NULL;
    }
  } else {

    PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(((UINT8 *)Data) + DosHdr->e_lfanew);
    if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
      return NULL;
    }
  }

  return PeHdr;
}

void
PeCoffConvertImageToXip (
  UINT8  **FileBuffer,
  UINT32 *FileLength
  )
{
  EFI_IMAGE_OPTIONAL_HEADER_UNION  *PeHdr;
  EFI_IMAGE_OPTIONAL_HEADER_UNION  *NewPeHdr;
  EFI_IMAGE_SECTION_HEADER         *SectionHeader;
  UINTN                            TotalNecessaryFileSize;
  UINTN                            SectionSize;
  UINT8                            *XipFile;
  UINT32                           XipLength;
  UINTN                            Index;
  UINTN                            FirstSectionOffset;
  BOOLEAN                          ConversionNeeded;

  PeHdr = GetPeCoffHeader ((void *) *FileBuffer);
  if (PeHdr == NULL) {
    return;
  }

  if (PeHdr->Pe32.OptionalHeader.SectionAlignment != PeHdr->Pe32.OptionalHeader.FileAlignment) {
    //
    // The only reason to expand zero fill sections is to make them compatible with XIP images.
    // If SectionAlignment is not equal to FileAlignment then it is not an XIP type image.
    //
    return;
  }

  //
  // Calculate size of XIP file, and determine if the conversion is needed.
  //
  ConversionNeeded = FALSE;
  XipLength = 0;
  FirstSectionOffset = *FileLength;
  TotalNecessaryFileSize = 0;
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
  for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    SectionSize = MAX (SectionHeader->Misc.VirtualSize, SectionHeader->SizeOfRawData);
    TotalNecessaryFileSize += SectionSize;
    if (SectionSize > 0) {
      FirstSectionOffset = MIN (FirstSectionOffset, SectionHeader->VirtualAddress);
      XipLength = MAX (XipLength, SectionHeader->VirtualAddress + SectionSize);
      if (SectionHeader->VirtualAddress != SectionHeader->PointerToRawData) {
        ConversionNeeded = TRUE;
      }
    }
    if (SectionHeader->Misc.VirtualSize > SectionHeader->SizeOfRawData) {
      ConversionNeeded = TRUE;
    }
  }

  if (FirstSectionOffset < PeHdr->Pe32.OptionalHeader.SizeOfHeaders) {
    //
    // If one of the sections should be loaded to an offset overlapping with
    // the executable header, then it cannot be made into an XIP image.
    //
    VerboseMsg ("PE/COFF conversion to XIP is impossible due to overlap");
    VerboseMsg ("of section data with the executable header.");
    return;
  }

  if (FirstSectionOffset == *FileLength) {
    //
    // If we never found a section with a non-zero size, then we
    // skip the conversion.
    //
    return;
  }

  TotalNecessaryFileSize += FirstSectionOffset;

  if (!ConversionNeeded) {
    return;
  }

  if (XipLength > (2 * TotalNecessaryFileSize)) {
    VerboseMsg ("PE/COFF conversion to XIP appears to be larger than necessary.");
    VerboseMsg ("The image linking process may have left unused memory ranges.");
  }

  if (PeHdr->Pe32.FileHeader.PointerToSymbolTable != 0) {
    //
    // This field is obsolete and should be zero
    //
    PeHdr->Pe32.FileHeader.PointerToSymbolTable = 0;
  }

  //
  // Allocate the extra space that we need to grow the image
  //
  XipFile = malloc (XipLength);
  if (XipFile == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
    return;
  }
  memset (XipFile, 0, XipLength);

  //
  // Copy the file headers
  //
  memcpy (XipFile, *FileBuffer, PeHdr->Pe32.OptionalHeader.SizeOfHeaders);

  NewPeHdr = GetPeCoffHeader ((void *)XipFile);
  if (NewPeHdr == NULL) {
    free (XipFile);
    return;
  }

  //
  // Copy the section data over to the appropriate XIP offsets
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(NewPeHdr->Pe32.OptionalHeader) + NewPeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
  for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    if (SectionHeader->SizeOfRawData > 0) {
      memcpy (
        XipFile + SectionHeader->VirtualAddress,
        *FileBuffer + SectionHeader->PointerToRawData,
        SectionHeader->SizeOfRawData
        );
    }
    //
    // Make the size of raw data in section header alignment.
    //
    SectionHeader->SizeOfRawData = (SectionHeader->Misc.VirtualSize + PeHdr->Pe32.OptionalHeader.FileAlignment - 1) & (~(PeHdr->Pe32.OptionalHeader.FileAlignment - 1));
    SectionHeader->PointerToRawData = SectionHeader->VirtualAddress;
  }

  free (*FileBuffer);
  *FileLength = XipLength;
  *FileBuffer = XipFile;

  mIsConvertXip = TRUE;
}

UINT8 *
CreateHiiResouceSectionHeader (
  UINT32 *pSectionHeaderSize,
  UINT32 HiiDataSize
  )
/*++

Routine Description:

  Create COFF resource section header

Arguments:

  pSectionHeaderSize - Pointer to section header size.
  HiiDataSize        - Size of the total HII data in section.

Returns:
  The created section header buffer.

--*/
{
  UINT32  HiiSectionHeaderSize;
  UINT32  HiiSectionOffset;
  UINT8   *HiiSectionHeader;
  EFI_IMAGE_RESOURCE_DIRECTORY        *ResourceDirectory;
  EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY  *TypeResourceDirectoryEntry;
  EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY  *NameResourceDirectoryEntry;
  EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY  *LanguageResourceDirectoryEntry;
  EFI_IMAGE_RESOURCE_DIRECTORY_STRING *ResourceDirectoryString;
  EFI_IMAGE_RESOURCE_DATA_ENTRY       *ResourceDataEntry;

  //
  // Calculate the total size for the resource header (include Type, Name and Language)
  // then allocate memory for the resource header.
  //
  HiiSectionHeaderSize = 3 * (sizeof (EFI_IMAGE_RESOURCE_DIRECTORY) + sizeof (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY))
                          + 3 * (sizeof (UINT16) + 3 * sizeof (CHAR16))
                          + sizeof (EFI_IMAGE_RESOURCE_DATA_ENTRY);
  HiiSectionHeader = malloc (HiiSectionHeaderSize);
  if (HiiSectionHeader == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
    return NULL;
  }
  memset (HiiSectionHeader, 0, HiiSectionHeaderSize);

  HiiSectionOffset = 0;
  //
  // Create Type entry
  //
  ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY);
  ResourceDirectory->NumberOfNamedEntries = 1;
  TypeResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY);
  TypeResourceDirectoryEntry->u1.s.NameIsString      = 1;
  TypeResourceDirectoryEntry->u2.s.DataIsDirectory   = 1;
  TypeResourceDirectoryEntry->u2.s.OffsetToDirectory = HiiSectionOffset;
  //
  // Create Name entry
  //
  ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY);
  ResourceDirectory->NumberOfNamedEntries = 1;
  NameResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY);
  NameResourceDirectoryEntry->u1.s.NameIsString      = 1;
  NameResourceDirectoryEntry->u2.s.DataIsDirectory   = 1;
  NameResourceDirectoryEntry->u2.s.OffsetToDirectory = HiiSectionOffset;
  //
  // Create Language entry
  //
  ResourceDirectory = (EFI_IMAGE_RESOURCE_DIRECTORY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY);
  ResourceDirectory->NumberOfNamedEntries = 1;
  LanguageResourceDirectoryEntry = (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY);
  LanguageResourceDirectoryEntry->u1.s.NameIsString = 1;
  //
  // Create string entry for Type
  //
  TypeResourceDirectoryEntry->u1.s.NameOffset = HiiSectionOffset;
  ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (HiiSectionHeader + HiiSectionOffset);
  ResourceDirectoryString->Length = 3;
  ResourceDirectoryString->String[0] = L'H';
  ResourceDirectoryString->String[1] = L'I';
  ResourceDirectoryString->String[2] = L'I';
  HiiSectionOffset = HiiSectionOffset + sizeof (ResourceDirectoryString->Length) + ResourceDirectoryString->Length * sizeof (ResourceDirectoryString->String[0]);
  //
  // Create string entry for Name
  //
  NameResourceDirectoryEntry->u1.s.NameOffset = HiiSectionOffset;
  ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (HiiSectionHeader + HiiSectionOffset);
  ResourceDirectoryString->Length = 3;
  ResourceDirectoryString->String[0] = L'E';
  ResourceDirectoryString->String[1] = L'F';
  ResourceDirectoryString->String[2] = L'I';
  HiiSectionOffset = HiiSectionOffset + sizeof (ResourceDirectoryString->Length) + ResourceDirectoryString->Length * sizeof (ResourceDirectoryString->String[0]);
  //
  // Create string entry for Language
  //
  LanguageResourceDirectoryEntry->u1.s.NameOffset = HiiSectionOffset;
  ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (HiiSectionHeader + HiiSectionOffset);
  ResourceDirectoryString->Length = 3;
  ResourceDirectoryString->String[0] = L'B';
  ResourceDirectoryString->String[1] = L'I';
  ResourceDirectoryString->String[2] = L'N';
  HiiSectionOffset = HiiSectionOffset + sizeof (ResourceDirectoryString->Length) + ResourceDirectoryString->Length * sizeof (ResourceDirectoryString->String[0]);
  //
  // Create Leaf data
  //
  LanguageResourceDirectoryEntry->u2.OffsetToData = HiiSectionOffset;
  ResourceDataEntry = (EFI_IMAGE_RESOURCE_DATA_ENTRY *) (HiiSectionHeader + HiiSectionOffset);
  HiiSectionOffset += sizeof (EFI_IMAGE_RESOURCE_DATA_ENTRY);
  ResourceDataEntry->OffsetToData = HiiSectionOffset;
  ResourceDataEntry->Size = HiiDataSize;

  *pSectionHeaderSize = HiiSectionHeaderSize;
  return HiiSectionHeader;
}

EFI_STATUS
RebaseImageRead (
  IN     VOID    *FileHandle,
  IN     UINTN   FileOffset,
  IN OUT UINT32  *ReadSize,
  OUT    VOID    *Buffer
  )
/*++

Routine Description:

  Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file

Arguments:

  FileHandle - The handle to the PE/COFF file

  FileOffset - The offset, in bytes, into the file to read

  ReadSize   - The number of bytes to read from the file starting at FileOffset

  Buffer     - A pointer to the buffer to read the data into.

Returns:

  EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset

--*/
{
  CHAR8   *Destination8;
  CHAR8   *Source8;
  UINT32  Length;

  Destination8  = Buffer;
  Source8       = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
  Length        = *ReadSize;
  while (Length--) {
    *(Destination8++) = *(Source8++);
  }

  return EFI_SUCCESS;
}

EFI_STATUS
SetAddressToSectionHeader (
  IN     CHAR8   *FileName,
  IN OUT UINT8   *FileBuffer,
  IN     UINT64  NewPe32BaseAddress
  )
/*++

Routine Description:

  Set new base address into the section header of PeImage

Arguments:

  FileName           - Name of file
  FileBuffer         - Pointer to PeImage.
  NewPe32BaseAddress - New Base Address for PE image.

Returns:

  EFI_SUCCESS          Set new base address into this image successfully.

--*/
{
  EFI_STATUS                            Status;
  PE_COFF_LOADER_IMAGE_CONTEXT          ImageContext;
  UINTN                                 Index;
  EFI_IMAGE_OPTIONAL_HEADER_UNION       *ImgHdr;
  EFI_IMAGE_SECTION_HEADER              *SectionHeader;

  //
  // Initialize context
  //
  memset (&ImageContext, 0, sizeof (ImageContext));
  ImageContext.Handle     = (VOID *) FileBuffer;
  ImageContext.ImageRead  = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
  Status                  = PeCoffLoaderGetImageInfo (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
    return Status;
  }

  if (ImageContext.RelocationsStripped) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
    return Status;
  }

  //
  // Get PeHeader pointer
  //
  ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);

  //
  // Get section header list
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
    (UINTN) ImgHdr +
    sizeof (UINT32) +
    sizeof (EFI_IMAGE_FILE_HEADER) +
    ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
    );

  //
  // Set base address into the first section header that doesn't point to code section.
  //
  for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    if ((SectionHeader->Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
      *(UINT64 *) &SectionHeader->PointerToRelocations = NewPe32BaseAddress;
      break;
    }
  }

  //
  // BaseAddress is set to section header.
  //
  return EFI_SUCCESS;
}

EFI_STATUS
RebaseImage (
  IN     CHAR8   *FileName,
  IN OUT UINT8   *FileBuffer,
  IN     UINT64  NewPe32BaseAddress
  )
/*++

Routine Description:

  Set new base address into PeImage, and fix up PeImage based on new address.

Arguments:

  FileName           - Name of file
  FileBuffer         - Pointer to PeImage.
  NewPe32BaseAddress - New Base Address for PE image.

Returns:

  EFI_INVALID_PARAMETER   - BaseAddress is not valid.
  EFI_SUCCESS             - Update PeImage is correctly.

--*/
{
  EFI_STATUS                            Status;
  PE_COFF_LOADER_IMAGE_CONTEXT          ImageContext;
  UINTN                                 Index;
  EFI_IMAGE_OPTIONAL_HEADER_UNION       *ImgHdr;
  UINT8                                 *MemoryImagePointer;
  EFI_IMAGE_SECTION_HEADER              *SectionHeader;

  //
  // Initialize context
  //
  memset (&ImageContext, 0, sizeof (ImageContext));
  ImageContext.Handle     = (VOID *) FileBuffer;
  ImageContext.ImageRead  = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
  Status                  = PeCoffLoaderGetImageInfo (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
    return Status;
  }

  if (ImageContext.RelocationsStripped) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
    return Status;
  }

  //
  // Get PeHeader pointer
  //
  ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);

  //
  // Load and Relocate Image Data
  //
  MemoryImagePointer = (UINT8 *) malloc ((UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
  if (MemoryImagePointer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated on rebase of %s", FileName);
    return EFI_OUT_OF_RESOURCES;
  }
  memset ((VOID *) MemoryImagePointer, 0, (UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
  ImageContext.ImageAddress = ((UINTN) MemoryImagePointer + ImageContext.SectionAlignment - 1) & (~((INT64)ImageContext.SectionAlignment - 1));

  Status =  PeCoffLoaderLoadImage (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "LocateImage() call failed on rebase of %s", FileName);
    free ((VOID *) MemoryImagePointer);
    return Status;
  }

  ImageContext.DestinationAddress = NewPe32BaseAddress;
  Status                          = PeCoffLoaderRelocateImage (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "RelocateImage() call failed on rebase of %s", FileName);
    free ((VOID *) MemoryImagePointer);
    return Status;
  }

  //
  // Copy Relocated data to raw image file.
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
    (UINTN) ImgHdr +
    sizeof (UINT32) +
    sizeof (EFI_IMAGE_FILE_HEADER) +
    ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
    );

  for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    CopyMem (
      FileBuffer + SectionHeader->PointerToRawData,
      (VOID*) (UINTN) (ImageContext.ImageAddress + SectionHeader->VirtualAddress),
      SectionHeader->SizeOfRawData
      );
  }

  free ((VOID *) MemoryImagePointer);

  //
  // Update Image Base Address
  //
  if (ImgHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
    ImgHdr->Pe32.OptionalHeader.ImageBase = (UINT32) NewPe32BaseAddress;
  } else if (ImgHdr->Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
    ImgHdr->Pe32Plus.OptionalHeader.ImageBase = NewPe32BaseAddress;
  } else {
    Error (NULL, 0, 3000, "Invalid", "unknown PE magic signature %X in PE32 image %s",
      ImgHdr->Pe32.OptionalHeader.Magic,
      FileName
      );
    return EFI_ABORTED;
  }

  //
  // Set new base address into section header
  //
  Status = SetAddressToSectionHeader (FileName, FileBuffer, NewPe32BaseAddress);

  return Status;
}

int
main (
  int  argc,
  char *argv[]
  )
/*++

Routine Description:

  Main function.

Arguments:

  argc - Number of command line parameters.
  argv - Array of pointers to command line parameter strings.

Returns:
  STATUS_SUCCESS - Utility exits successfully.
  STATUS_ERROR   - Some error occurred during execution.

--*/
{
  UINT32                           Type;
  UINT32                           InputFileNum;
  CHAR8                            **InputFileName;
  char                             *OutImageName;
  char                             *ModuleType;
  CHAR8                            *TimeStamp;
  FILE                             *fpIn;
  FILE                             *fpOut;
  FILE                             *fpInOut;
  UINT32                           Data;
  UINT32                           *DataPointer;
  UINT32                           *OldDataPointer;
  UINT32                           CheckSum;
  UINT32                           Index;
  UINT32                           Index1;
  UINT32                           Index2;
  UINT64                           Temp64;
  UINT32                           MciAlignment;
  UINT8                            MciPadValue;
  UINT32                           AllignedRelocSize;
  UINT8                            *FileBuffer;
  UINT32                           FileLength;
  UINT8                            *OutputFileBuffer;
  UINT32                           OutputFileLength;
  UINT8                            *InputFileBuffer;
  UINT32                           InputFileLength;
  RUNTIME_FUNCTION                 *RuntimeFunction;
  UNWIND_INFO                      *UnwindInfo;
  STATUS                           Status;
  BOOLEAN                          ReplaceFlag;
  BOOLEAN                          KeepExceptionTableFlag;
  BOOLEAN                          KeepOptionalHeaderFlag;
  BOOLEAN                          KeepZeroPendingFlag;
  UINT64                           LogLevel;
  EFI_TE_IMAGE_HEADER              TEImageHeader;
  EFI_TE_IMAGE_HEADER              *TeHdr;
  EFI_IMAGE_SECTION_HEADER         *SectionHeader;
  EFI_IMAGE_DOS_HEADER             *DosHdr;
  EFI_IMAGE_OPTIONAL_HEADER_UNION  *PeHdr;
  EFI_IMAGE_OPTIONAL_HEADER32      *Optional32;
  EFI_IMAGE_OPTIONAL_HEADER64      *Optional64;
  EFI_IMAGE_DOS_HEADER             BackupDosHdr;
  MICROCODE_IMAGE_HEADER           *MciHeader;
  UINT8                            *HiiPackageListBuffer;
  UINT8                            *HiiPackageDataPointer;
  EFI_GUID                         HiiPackageListGuid;
  EFI_HII_PACKAGE_LIST_HEADER      HiiPackageListHeader;
  EFI_HII_PACKAGE_HEADER           HiiPackageHeader;
  EFI_IFR_FORM_SET                 IfrFormSet;
  UINT8                            NumberOfFormPackage;
  EFI_HII_PACKAGE_HEADER           EndPackage;
  UINT32                           HiiSectionHeaderSize;
  UINT8                            *HiiSectionHeader;
  UINT64                           NewBaseAddress;
  BOOLEAN                          NegativeAddr;
  FILE                             *ReportFile;
  CHAR8                            *ReportFileName;
  UINTN                            FileLen;
  time_t                           InputFileTime;
  time_t                           OutputFileTime;
  struct stat                      Stat_Buf;

  SetUtilityName (UTILITY_NAME);

  //
  // Assign to fix compile warning
  //
  FileLen           = 0;
  InputFileNum      = 0;
  InputFileName     = NULL;
  mInImageName      = NULL;
  OutImageName      = NULL;
  ModuleType        = NULL;
  Type              = 0;
  Status            = STATUS_SUCCESS;
  FileBuffer        = NULL;
  fpIn              = NULL;
  fpOut             = NULL;
  fpInOut           = NULL;
  TimeStamp         = NULL;
  MciAlignment      = DEFAULT_MC_ALIGNMENT;
  MciPadValue       = DEFAULT_MC_PAD_BYTE_VALUE;
  FileLength        = 0;
  MciHeader         = NULL;
  CheckSum          = 0;
  ReplaceFlag       = FALSE;
  LogLevel          = 0;
  OutputFileBuffer  = NULL;
  OutputFileLength  = 0;
  InputFileBuffer   = NULL;
  InputFileLength   = 0;
  Optional32        = NULL;
  Optional64        = NULL;
  KeepExceptionTableFlag = FALSE;
  KeepOptionalHeaderFlag = FALSE;
  KeepZeroPendingFlag    = FALSE;
  NumberOfFormPackage    = 0;
  HiiPackageListBuffer   = NULL;
  HiiPackageDataPointer  = NULL;
  EndPackage.Length      = sizeof (EFI_HII_PACKAGE_HEADER);
  EndPackage.Type        = EFI_HII_PACKAGE_END;
  memset (&HiiPackageListGuid, 0, sizeof (HiiPackageListGuid));
  HiiSectionHeaderSize   = 0;
  HiiSectionHeader       = NULL;
  NewBaseAddress         = 0;
  NegativeAddr           = FALSE;
  InputFileTime          = 0;
  OutputFileTime         = 0;

  if (argc == 1) {
    Error (NULL, 0, 1001, "Missing options", "No input options.");
    Usage ();
    return STATUS_ERROR;
  }

  argc --;
  argv ++;

  if ((stricmp (argv[0], "-h") == 0) || (stricmp (argv[0], "--help") == 0)) {
    Version ();
    Usage ();
    return STATUS_SUCCESS;
  }

  if (stricmp (argv[0], "--version") == 0) {
    Version ();
    return STATUS_SUCCESS;
  }

  while (argc > 0) {
    if ((stricmp (argv[0], "-o") == 0) || (stricmp (argv[0], "--outputfile") == 0)) {
      if (argv[1] == NULL || argv[1][0] == '-') {
        Error (NULL, 0, 1003, "Invalid option value", "Output file name is missing for -o option");
        goto Finish;
      }
      OutImageName = argv[1];
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-e") == 0) || (stricmp (argv[0], "--efiImage") == 0)) {
      if (argv[1] == NULL || argv[1][0] == '-') {
        Error (NULL, 0, 1003, "Invalid option value", "Module Type is missing for -o option");
        goto Finish;
      }
      ModuleType = argv[1];
      if (mOutImageType != FW_TE_IMAGE) {
        mOutImageType = FW_EFI_IMAGE;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-l") == 0) || (stricmp (argv[0], "--stripped") == 0)) {
      mOutImageType = FW_RELOC_STRIPEED_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-c") == 0) || (stricmp (argv[0], "--acpi") == 0)) {
      mOutImageType = FW_ACPI_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-t") == 0) || (stricmp (argv[0], "--terse") == 0)) {
      mOutImageType = FW_TE_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-u") == 0) || (stricmp (argv[0], "--dump") == 0)) {
      mOutImageType = DUMP_TE_HEADER;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-b") == 0) || (stricmp (argv[0], "--exe2bin") == 0)) {
      mOutImageType = FW_BIN_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-z") == 0) || (stricmp (argv[0], "--zero") == 0)) {
      mOutImageType = FW_ZERO_DEBUG_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-s") == 0) || (stricmp (argv[0], "--stamp") == 0)) {
      mOutImageType = FW_SET_STAMP_IMAGE;
      if (argv[1] == NULL || argv[1][0] == '-') {
        Error (NULL, 0, 1003, "Invalid option value", "time stamp is missing for -s option");
        goto Finish;
      }
      TimeStamp = argv[1];
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-r") == 0) || (stricmp (argv[0], "--replace") == 0)) {
      ReplaceFlag = TRUE;
      argc --;
      argv ++;
      continue;
    }

    if (stricmp (argv[0], "--keepexceptiontable") == 0) {
      KeepExceptionTableFlag = TRUE;
      argc --;
      argv ++;
      continue;
    }

    if (stricmp(argv[0], "--keepoptionalheader") == 0) {
      KeepOptionalHeaderFlag = TRUE;
      argc--;
      argv++;
      continue;
    }

    if (stricmp (argv[0], "--keepzeropending") == 0) {
      KeepZeroPendingFlag = TRUE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-m") == 0) || (stricmp (argv[0], "--mcifile") == 0)) {
      mOutImageType = FW_MCI_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-j") == 0) || (stricmp (argv[0], "--join") == 0)) {
      mOutImageType = FW_MERGE_IMAGE;
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-a") == 0) || (stricmp (argv[0], "--align") == 0)) {
      if (AsciiStringToUint64 (argv[1], FALSE, &Temp64) != EFI_SUCCESS) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      MciAlignment = (UINT32) Temp64;
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "--rebase") == 0)) {
      if (argv[1][0] == '-') {
        NegativeAddr = TRUE;
        Status = AsciiStringToUint64 (argv[1] + 1, FALSE, &Temp64);
      } else {
        NegativeAddr = FALSE;
        Status = AsciiStringToUint64 (argv[1], FALSE, &Temp64);
      }
      if (Status != EFI_SUCCESS) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      mOutImageType = FW_REBASE_IMAGE;
      NewBaseAddress = (UINT64) Temp64;
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "--address") == 0)) {
      if (argv[1][0] == '-') {
        NegativeAddr = TRUE;
        Status = AsciiStringToUint64 (argv[1] + 1, FALSE, &Temp64);
      } else {
        NegativeAddr = FALSE;
        Status = AsciiStringToUint64 (argv[1], FALSE, &Temp64);
      }
      if (Status != EFI_SUCCESS) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      mOutImageType = FW_SET_ADDRESS_IMAGE;
      NewBaseAddress = (UINT64) Temp64;
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-p") == 0) || (stricmp (argv[0], "--pad") == 0)) {
      if (AsciiStringToUint64 (argv[1], FALSE, &Temp64) != EFI_SUCCESS) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      MciPadValue = (UINT8) Temp64;
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
      SetPrintLevel (VERBOSE_LOG_LEVEL);
      VerboseMsg ("Verbose output Mode Set!");
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
      SetPrintLevel (KEY_LOG_LEVEL);
      KeyMsg ("Quiet output Mode Set!");
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) {
      Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      if (LogLevel > 9) {
        Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current input level is %d", (int) LogLevel);
        goto Finish;
      }
      SetPrintLevel (LogLevel);
      DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-g") == 0) || (stricmp (argv[0], "--hiiguid") == 0)) {
      Status = StringToGuid (argv[1], &HiiPackageListGuid);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if (stricmp (argv[0], "--hiipackage") == 0) {
      mOutImageType = FW_HII_PACKAGE_LIST_RCIMAGE;
      argc --;
      argv ++;
      continue;
    }

    if (stricmp (argv[0], "--hiibinpackage") == 0) {
      mOutImageType = FW_HII_PACKAGE_LIST_BINIMAGE;
      argc --;
      argv ++;
      continue;
    }

    if (argv[0][0] == '-') {
      Error (NULL, 0, 1000, "Unknown option", argv[0]);
      goto Finish;
    }
    //
    // Get Input file name
    //
    if ((InputFileNum == 0) && (InputFileName == NULL)) {
      InputFileName = (CHAR8 **) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *));
      if (InputFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        goto Finish;
      }

      memset (InputFileName, 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
    } else if (InputFileNum % MAXIMUM_INPUT_FILE_NUM == 0) {
      //
      // InputFileName buffer too small, need to realloc
      //
      InputFileName = (CHAR8 **) realloc (
        InputFileName,
        (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (CHAR8 *)
        );

      if (InputFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        goto Finish;
      }

      memset (&(InputFileName[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
    }

    InputFileName [InputFileNum ++] = argv[0];
    argc --;
    argv ++;
  }

  VerboseMsg ("%s tool start.", UTILITY_NAME);

  if (mOutImageType == FW_DUMMY_IMAGE) {
    Error (NULL, 0, 1001, "Missing option", "No create file action specified; pls specify -e, -c or -t option to create efi image, or acpi table or TeImage!");
    if (ReplaceFlag) {
      Error (NULL, 0, 1001, "Missing option", "-r option is not supported as the independent option. It can be used together with other create file option specified at the above.");
    }
    goto Finish;
  }

  //
  // check input files
  //
  if (InputFileNum == 0) {
    Error (NULL, 0, 1001, "Missing option", "Input files");
    goto Finish;
  }

  //
  // Combine MciBinary files to one file
  //
  if ((mOutImageType == FW_MERGE_IMAGE) && ReplaceFlag) {
    Error (NULL, 0, 1002, "Conflicting option", "-r replace option cannot be used with -j merge files option.");
    goto Finish;
  }

  //
  // Combine HiiBinary packages to a single package list
  //
  if ((mOutImageType == FW_HII_PACKAGE_LIST_RCIMAGE) && ReplaceFlag) {
    Error (NULL, 0, 1002, "Conflicting option", "-r replace option cannot be used with --hiipackage merge files option.");
    goto Finish;
  }

  if ((mOutImageType == FW_HII_PACKAGE_LIST_BINIMAGE) && ReplaceFlag) {
    Error (NULL, 0, 1002, "Conflicting option", "-r replace option cannot be used with --hiibinpackage merge files option.");
    goto Finish;
  }

  //
  // Input image file
  //
  mInImageName = InputFileName [InputFileNum - 1];
  VerboseMsg ("the input file name is %s", mInImageName);

  //
  // Action will be taken for the input file.
  //
  switch (mOutImageType) {
  case FW_EFI_IMAGE:
    VerboseMsg ("Create efi image on module type %s based on the input PE image.", ModuleType);
    break;
  case FW_TE_IMAGE:
    VerboseMsg ("Create Te Image based on the input PE image.");
    break;
  case FW_ACPI_IMAGE:
    VerboseMsg ("Get acpi table data from the input PE image.");
    break;
  case FW_RELOC_STRIPEED_IMAGE:
    VerboseMsg ("Remove relocation section from Pe or Te image.");
    break;
  case FW_BIN_IMAGE:
    VerboseMsg ("Convert the input EXE to the output BIN file.");
    break;
  case FW_ZERO_DEBUG_IMAGE:
    VerboseMsg ("Zero the Debug Data Fields and Time Stamp in input PE image.");
    break;
  case FW_SET_STAMP_IMAGE:
    VerboseMsg ("Set new time stamp %s in the input PE image.", TimeStamp);
    break;
  case DUMP_TE_HEADER:
    VerboseMsg ("Dump the TE header information of the input TE image.");
    break;
  case FW_MCI_IMAGE:
    VerboseMsg ("Convert input MicroCode.txt file to MicroCode.bin file.");
    break;
  case FW_MERGE_IMAGE:
    VerboseMsg ("Combine the input multi microcode bin files to one bin file.");
    break;
  case FW_HII_PACKAGE_LIST_RCIMAGE:
    VerboseMsg ("Combine the input multi hii bin packages to one text package list RC file.");
    break;
  case FW_HII_PACKAGE_LIST_BINIMAGE:
    VerboseMsg ("Combine the input multi hii bin packages to one binary package list file.");
    break;
  case FW_REBASE_IMAGE:
    VerboseMsg ("Rebase the input image to new base address.");
    break;
  case FW_SET_ADDRESS_IMAGE:
    VerboseMsg ("Set the preferred address into the section header of the input image");
    break;
  default:
    break;
  }

  if (ReplaceFlag) {
    VerboseMsg ("Overwrite the input file with the output content.");
  }

  //
  // Open output file and Write image into the output file.
  //
  if (OutImageName != NULL) {
    fpOut = fopen (LongFilePath (OutImageName), "rb");
    if (fpOut != NULL) {
      //
      // Get Output file time stamp
      //
      fstat(fileno (fpOut), &Stat_Buf);
      OutputFileTime = Stat_Buf.st_mtime;
      //
      // Get Output file data
      //
      OutputFileLength = _filelength (fileno (fpOut));
      OutputFileBuffer = malloc (OutputFileLength);
      if (OutputFileBuffer == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        fclose (fpOut);
        fpOut = NULL;
        goto Finish;
      }
      fread (OutputFileBuffer, 1, OutputFileLength, fpOut);
      fclose (fpOut);
      fpOut = NULL;
    }
    VerboseMsg ("Output file name is %s", OutImageName);
  } else if (!ReplaceFlag && mOutImageType != DUMP_TE_HEADER) {
    Error (NULL, 0, 1001, "Missing option", "output file");
    goto Finish;
  }

  //
  // Open input file and read file data into file buffer.
  //
  fpIn = fopen (LongFilePath (mInImageName), "rb");
  if (fpIn == NULL) {
    Error (NULL, 0, 0001, "Error opening file", mInImageName);
    goto Finish;
  }
  //
  // Get Iutput file time stamp
  //
  fstat(fileno (fpIn), &Stat_Buf);
  InputFileTime = Stat_Buf.st_mtime;
  //
  // Get Input file data
  //
  InputFileLength = _filelength (fileno (fpIn));
  InputFileBuffer = malloc (InputFileLength);
  if (InputFileBuffer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
    fclose (fpIn);
    goto Finish;
  }
  fread (InputFileBuffer, 1, InputFileLength, fpIn);
  fclose (fpIn);
  DebugMsg (NULL, 0, 9, "input file info", "the input file size is %u bytes", (unsigned) InputFileLength);

  //
  // Combine multi binary HII package files.
  //
  if (mOutImageType == FW_HII_PACKAGE_LIST_RCIMAGE || mOutImageType == FW_HII_PACKAGE_LIST_BINIMAGE) {
    //
    // Open output file handle.
    //
    fpOut = fopen (LongFilePath (OutImageName), "wb");
    if (!fpOut) {
      Error (NULL, 0, 0001, "Error opening output file", OutImageName);
      goto Finish;
    }
    //
    // Get hii package list length
    //
    HiiPackageListHeader.PackageLength = sizeof (EFI_HII_PACKAGE_LIST_HEADER);
    for (Index = 0; Index < InputFileNum; Index ++) {
      fpIn = fopen (LongFilePath (InputFileName [Index]), "rb");
      if (fpIn == NULL) {
        Error (NULL, 0, 0001, "Error opening file", InputFileName [Index]);
        goto Finish;
      }
      FileLength = _filelength (fileno (fpIn));
      fread (&HiiPackageHeader, 1, sizeof (HiiPackageHeader), fpIn);
      if (HiiPackageHeader.Type == EFI_HII_PACKAGE_FORM) {
        if (HiiPackageHeader.Length != FileLength) {
          Error (NULL, 0, 3000, "Invalid", "The wrong package size is in HII package file %s", InputFileName [Index]);
          fclose (fpIn);
          goto Finish;
        }
        if (memcmp (&HiiPackageListGuid, &mZeroGuid, sizeof (EFI_GUID)) == 0) {
          fread (&IfrFormSet, 1, sizeof (IfrFormSet), fpIn);
          memcpy (&HiiPackageListGuid, &IfrFormSet.Guid, sizeof (EFI_GUID));
        }
        NumberOfFormPackage ++;
      }
      HiiPackageListHeader.PackageLength += FileLength;
      fclose (fpIn);
    }
    HiiPackageListHeader.PackageLength += sizeof (EndPackage);
    //
    // Check whether hii packages are valid
    //
    if (NumberOfFormPackage > 1) {
      Error (NULL, 0, 3000, "Invalid", "The input hii packages contains more than one hii form package");
      goto Finish;
    }
    if (memcmp (&HiiPackageListGuid, &mZeroGuid, sizeof (EFI_GUID)) == 0) {
      Error (NULL, 0, 3000, "Invalid", "HII package list guid is not specified!");
      goto Finish;
    }
    memcpy (&HiiPackageListHeader.PackageListGuid, &HiiPackageListGuid, sizeof (EFI_GUID));
    //
    // read hii packages
    //
    HiiPackageListBuffer = malloc (HiiPackageListHeader.PackageLength);
    if (HiiPackageListBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
      goto Finish;
    }
    memcpy (HiiPackageListBuffer, &HiiPackageListHeader, sizeof (HiiPackageListHeader));
    HiiPackageDataPointer = HiiPackageListBuffer + sizeof (HiiPackageListHeader);
    for (Index = 0; Index < InputFileNum; Index ++) {
      fpIn = fopen (LongFilePath (InputFileName [Index]), "rb");
      if (fpIn == NULL) {
        Error (NULL, 0, 0001, "Error opening file", InputFileName [Index]);
        free (HiiPackageListBuffer);
        goto Finish;
      }

      FileLength = _filelength (fileno (fpIn));
      fread (HiiPackageDataPointer, 1, FileLength, fpIn);
      fclose (fpIn);
      HiiPackageDataPointer = HiiPackageDataPointer + FileLength;
    }
    memcpy (HiiPackageDataPointer, &EndPackage, sizeof (EndPackage));

    //
    // write the hii package into the binary package list file with the resource section header
    //
    if (mOutImageType == FW_HII_PACKAGE_LIST_BINIMAGE) {
      //
      // Create the resource section header
      //
      HiiSectionHeader = CreateHiiResouceSectionHeader (&HiiSectionHeaderSize, HiiPackageListHeader.PackageLength);
      if (HiiSectionHeader == NULL) {
        free (HiiPackageListBuffer);
        goto Finish;
      }
      //
      // Wrtie section header and HiiData into File.
      //
      fwrite (HiiSectionHeader, 1, HiiSectionHeaderSize, fpOut);
      fwrite (HiiPackageListBuffer, 1, HiiPackageListHeader.PackageLength, fpOut);
      //
      // Free allocated resources.
      //
      free (HiiSectionHeader);
      free (HiiPackageListBuffer);
      //
      // Done successfully
      //
      goto Finish;
    }

    //
    // write the hii package into the text package list rc file.
    //
    if (mOutImageType == FW_HII_PACKAGE_LIST_RCIMAGE) {
      for (Index = 0; gHiiPackageRCFileHeader[Index] != NULL; Index++) {
        fprintf (fpOut, "%s\n", gHiiPackageRCFileHeader[Index]);
      }
      fprintf (fpOut, "\n%d %s\n{", HII_RESOURCE_SECTION_INDEX, HII_RESOURCE_SECTION_NAME);

      HiiPackageDataPointer = HiiPackageListBuffer;
      for (Index = 0; Index + 2 < HiiPackageListHeader.PackageLength; Index += 2) {
        if (Index % 16 == 0) {
          fprintf (fpOut, "\n ");
        }
        fprintf (fpOut, " 0x%04X,", *(UINT16 *) HiiPackageDataPointer);
        HiiPackageDataPointer += 2;
      }

      if (Index % 16 == 0) {
        fprintf (fpOut, "\n ");
      }
      if ((Index + 2) == HiiPackageListHeader.PackageLength) {
        fprintf (fpOut, " 0x%04X\n}\n", *(UINT16 *) HiiPackageDataPointer);
      }
      if ((Index + 1) == HiiPackageListHeader.PackageLength) {
        fprintf (fpOut, " 0x%04X\n}\n", *(UINT8 *) HiiPackageDataPointer);
      }
      free (HiiPackageListBuffer);
      //
      // Done successfully
      //
      goto Finish;
    }
  }

  //
  // Combine MciBinary files to one file
  //
  if (mOutImageType == FW_MERGE_IMAGE) {
    //
    // Open output file handle.
    //
    fpOut = fopen (LongFilePath (OutImageName), "wb");
    if (!fpOut) {
      Error (NULL, 0, 0001, "Error opening output file", OutImageName);
      goto Finish;
    }
    for (Index = 0; Index < InputFileNum; Index ++) {
      fpIn = fopen (LongFilePath (InputFileName [Index]), "rb");
      if (!fpIn) {
        Error (NULL, 0, 0001, "Error opening file", InputFileName [Index]);
        goto Finish;
      }

      FileLength = _filelength (fileno (fpIn));
      FileBuffer = malloc (FileLength);
      if (FileBuffer == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        fclose (fpIn);
        goto Finish;
      }

      fread (FileBuffer, 1, FileLength, fpIn);
      fclose (fpIn);
      //
      // write input file to out file
      //
      fwrite (FileBuffer, 1, FileLength, fpOut);
      //
      // write pad value to out file.
      //
      while (FileLength ++ % MciAlignment != 0) {
        fwrite (&MciPadValue, 1, 1, fpOut);
      }
      //
      // free allocated memory space
      //
      free (FileBuffer);
      FileBuffer = NULL;
    }
    //
    // Done successfully
    //
    goto Finish;
  }

  //
  // Convert MicroCode.txt file to MicroCode.bin file
  //
  if (mOutImageType == FW_MCI_IMAGE) {
    fpIn = fopen (LongFilePath (mInImageName), "r");
    if (fpIn == NULL) {
      Error (NULL, 0, 0001, "Error opening file", mInImageName);
      goto Finish;
    }

    //
    // The first pass is to determine
    // how much data is in the file so we can allocate a working buffer.
    //
    FileLength = 0;
    do {
      Status = MicrocodeReadData (fpIn, &Data);
      if (Status == STATUS_SUCCESS) {
        FileLength += sizeof (Data);
      }
      if (Status == STATUS_IGNORE) {
        Status = STATUS_SUCCESS;
      }
    } while (Status == STATUS_SUCCESS);
    //
    // Error if no data.
    //
    if (FileLength == 0) {
      Error (NULL, 0, 3000, "Invalid", "no parseable data found in file %s", mInImageName);
      goto Finish;
    }
    if (FileLength < sizeof (MICROCODE_IMAGE_HEADER)) {
      Error (NULL, 0, 3000, "Invalid", "amount of parseable data in %s is insufficient to contain a microcode header", mInImageName);
      goto Finish;
    }

    //
    // Allocate a buffer for the data
    //
    FileBuffer = malloc (FileLength);
    if (FileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
      goto Finish;
    }
    //
    // Re-read the file, storing the data into our buffer
    //
    fseek (fpIn, 0, SEEK_SET);
    DataPointer = (UINT32 *) FileBuffer;
    OldDataPointer = DataPointer;
    do {
      OldDataPointer = DataPointer;
      Status = MicrocodeReadData (fpIn, DataPointer++);
      if (Status == STATUS_IGNORE) {
        DataPointer = OldDataPointer;
        Status = STATUS_SUCCESS;
      }
    } while (Status == STATUS_SUCCESS);
    //
    // close input file after read data
    //
    fclose (fpIn);

    //
    // Can't do much checking on the header because, per the spec, the
    // DataSize field may be 0, which means DataSize = 2000 and TotalSize = 2K,
    // and the TotalSize field is invalid (actually missing). Thus we can't
    // even verify the Reserved fields are 0.
    //
    MciHeader = (MICROCODE_IMAGE_HEADER *) FileBuffer;
    if (MciHeader->DataSize == 0) {
      Index = 2048;
    } else {
      Index = MciHeader->TotalSize;
    }

    if (Index != FileLength) {
      Error (NULL, 0, 3000, "Invalid", "file length of %s (0x%x) does not equal expected TotalSize: 0x%04X.", mInImageName, (unsigned) FileLength, (unsigned) Index);
      goto Finish;
    }

    //
    // Checksum the contents
    //
    DataPointer = (UINT32 *) FileBuffer;
    CheckSum  = 0;
    Index     = 0;
    while (Index < FileLength) {
      CheckSum    += *DataPointer;
      DataPointer ++;
      Index       += sizeof (*DataPointer);
    }
    if (CheckSum != 0) {
      Error (NULL, 0, 3000, "Invalid", "checksum (0x%x) failed on file %s.", (unsigned) CheckSum, mInImageName);
      goto Finish;
    }
    //
    // Open the output file and write the buffer contents
    //
    VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    goto WriteFile;
  }

  //
  // Open input file and read file data into file buffer.
  //
  FileLength = InputFileLength;
  FileBuffer = malloc (FileLength);
  if (FileBuffer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
    goto Finish;
  }
  memcpy (FileBuffer, InputFileBuffer, InputFileLength);

  //
  // Dump TeImage Header into output file.
  //
  if (mOutImageType == DUMP_TE_HEADER) {
    memcpy (&TEImageHeader, FileBuffer, sizeof (TEImageHeader));
    if (TEImageHeader.Signature != EFI_TE_IMAGE_HEADER_SIGNATURE) {
      Error (NULL, 0, 3000, "Invalid", "TE header signature of file %s is not correct.", mInImageName);
      goto Finish;
    }
    //
    // Open the output file handle.
    //
    if (ReplaceFlag) {
      fpInOut = fopen (LongFilePath (mInImageName), "wb");
      if (fpInOut == NULL) {
        Error (NULL, 0, 0001, "Error opening file", mInImageName);
        goto Finish;
      }
    } else {
      if (OutImageName != NULL) {
        fpOut = fopen (LongFilePath (OutImageName), "wb");
      } else {
        fpOut = stdout;
      }
      if (fpOut == NULL) {
        Error (NULL, 0, 0001, "Error opening output file", OutImageName);
        goto Finish;
      }
    }
    if (fpInOut != NULL) {
      fprintf (fpInOut, "Dump of file %s\n\n", mInImageName);
      fprintf (fpInOut, "TE IMAGE HEADER VALUES\n");
      fprintf (fpInOut, "%17X machine\n", TEImageHeader.Machine);
      fprintf (fpInOut, "%17X number of sections\n", TEImageHeader.NumberOfSections);
      fprintf (fpInOut, "%17X subsystems\n", TEImageHeader.Subsystem);
      fprintf (fpInOut, "%17X stripped size\n", TEImageHeader.StrippedSize);
      fprintf (fpInOut, "%17X entry point\n", (unsigned) TEImageHeader.AddressOfEntryPoint);
      fprintf (fpInOut, "%17X base of code\n", (unsigned) TEImageHeader.BaseOfCode);
      fprintf (fpInOut, "%17llX image base\n", (unsigned long long)TEImageHeader.ImageBase);
      fprintf (fpInOut, "%17X [%8X] RVA [size] of Base Relocation Directory\n", (unsigned) TEImageHeader.DataDirectory[0].VirtualAddress, (unsigned) TEImageHeader.DataDirectory[0].Size);
      fprintf (fpInOut, "%17X [%8X] RVA [size] of Debug Directory\n", (unsigned) TEImageHeader.DataDirectory[1].VirtualAddress, (unsigned) TEImageHeader.DataDirectory[1].Size);
    }
    if (fpOut != NULL) {
      fprintf (fpOut, "Dump of file %s\n\n", mInImageName);
      fprintf (fpOut, "TE IMAGE HEADER VALUES\n");
      fprintf (fpOut, "%17X machine\n", TEImageHeader.Machine);
      fprintf (fpOut, "%17X number of sections\n", TEImageHeader.NumberOfSections);
      fprintf (fpOut, "%17X subsystems\n", TEImageHeader.Subsystem);
      fprintf (fpOut, "%17X stripped size\n", TEImageHeader.StrippedSize);
      fprintf (fpOut, "%17X entry point\n", (unsigned) TEImageHeader.AddressOfEntryPoint);
      fprintf (fpOut, "%17X base of code\n", (unsigned) TEImageHeader.BaseOfCode);
      fprintf (fpOut, "%17llX image base\n", (unsigned long long)TEImageHeader.ImageBase);
      fprintf (fpOut, "%17X [%8X] RVA [size] of Base Relocation Directory\n", (unsigned) TEImageHeader.DataDirectory[0].VirtualAddress, (unsigned) TEImageHeader.DataDirectory[0].Size);
      fprintf (fpOut, "%17X [%8X] RVA [size] of Debug Directory\n", (unsigned) TEImageHeader.DataDirectory[1].VirtualAddress, (unsigned) TEImageHeader.DataDirectory[1].Size);
    }
    goto Finish;
  }

  //
  // Following code to convert dll to efi image or te image.
  // Get new image type
  //
  if ((mOutImageType == FW_EFI_IMAGE) || (mOutImageType == FW_TE_IMAGE)) {
    if (ModuleType == NULL) {
      if (mOutImageType == FW_EFI_IMAGE) {
        Error (NULL, 0, 1001, "Missing option", "EFI_FILETYPE");
        goto Finish;
      } else if (mOutImageType == FW_TE_IMAGE) {
        //
        // Default TE Image Type is Boot service driver
        //
        Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;
        VerboseMsg ("Efi Image subsystem type is efi boot service driver.");
      }
    } else {
      if (stricmp (ModuleType, "BASE") == 0 ||
        stricmp (ModuleType, "SEC") == 0 ||
        stricmp (ModuleType, "SECURITY_CORE") == 0 ||
        stricmp (ModuleType, "PEI_CORE") == 0 ||
        stricmp (ModuleType, "PEIM") == 0 ||
        stricmp (ModuleType, "COMBINED_PEIM_DRIVER") == 0 ||
        stricmp (ModuleType, "PIC_PEIM") == 0 ||
        stricmp (ModuleType, "RELOCATABLE_PEIM") == 0 ||
        stricmp (ModuleType, "DXE_CORE") == 0 ||
        stricmp (ModuleType, "BS_DRIVER") == 0  ||
        stricmp (ModuleType, "DXE_DRIVER") == 0 ||
        stricmp (ModuleType, "DXE_SMM_DRIVER") == 0  ||
        stricmp (ModuleType, "UEFI_DRIVER") == 0 ||
        stricmp (ModuleType, "SMM_CORE") == 0 ||
        stricmp (ModuleType, "MM_STANDALONE") == 0 ||
        stricmp (ModuleType, "MM_CORE_STANDALONE") == 0) {
          Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;
          VerboseMsg ("Efi Image subsystem type is efi boot service driver.");

      } else if (stricmp (ModuleType, "UEFI_APPLICATION") == 0 ||
        stricmp (ModuleType, "APPLICATION") == 0) {
          Type = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION;
          VerboseMsg ("Efi Image subsystem type is efi application.");

      } else if (stricmp (ModuleType, "DXE_RUNTIME_DRIVER") == 0 ||
        stricmp (ModuleType, "RT_DRIVER") == 0) {
          Type = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
          VerboseMsg ("Efi Image subsystem type is efi runtime driver.");

      } else if (stricmp (ModuleType, "DXE_SAL_DRIVER") == 0 ||
        stricmp (ModuleType, "SAL_RT_DRIVER") == 0) {
          Type = EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER;
          VerboseMsg ("Efi Image subsystem type is efi sal runtime driver.");

      } else {
        Error (NULL, 0, 1003, "Invalid option value", "EFI_FILETYPE = %s", ModuleType);
        goto Finish;
      }
    }
  }

  //
  // Convert ELF image to PeImage
  //
  if (IsElfHeader(FileBuffer)) {
    VerboseMsg ("Convert %s from ELF to PE/COFF.", mInImageName);
    if (!ConvertElf(&FileBuffer, &FileLength)) {
      Error (NULL, 0, 3000, "Invalid", "Unable to convert %s from ELF to PE/COFF.", mInImageName);
      goto Finish;
    }
  }

  //
  // Make sure File Offsets and Virtual Offsets are the same in the image so it is XIP
  // XIP == eXecute In Place
  //
  PeCoffConvertImageToXip (&FileBuffer, &FileLength);

  //
  // Remove reloc section from PE or TE image
  //
  if (mOutImageType == FW_RELOC_STRIPEED_IMAGE) {
    //
    // Check TeImage
    //
    TeHdr = (EFI_TE_IMAGE_HEADER *) FileBuffer;
    if (TeHdr->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
      SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (TeHdr + 1);
      for (Index = 0; Index < TeHdr->NumberOfSections; Index ++, SectionHeader ++) {
        if (strcmp ((char *)SectionHeader->Name, ".reloc") == 0) {
          //
          // Check the reloc section is in the end of image.
          //
          if ((SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData) ==
            (FileLength + TeHdr->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER))) {
              //
              // Remove .reloc section and update TeImage Header
              //
              FileLength = FileLength - SectionHeader->SizeOfRawData;
              SectionHeader->SizeOfRawData = 0;
              SectionHeader->Misc.VirtualSize = 0;
              TeHdr->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
              TeHdr->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size           = 0;
              break;
          }
        }
      }
    } else {
      //
      // Check PE Image
      //
      DosHdr = (EFI_IMAGE_DOS_HEADER *) FileBuffer;
      if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
        PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer);
        if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
          Error (NULL, 0, 3000, "Invalid", "TE and DOS header signatures were not found in %s image.", mInImageName);
          goto Finish;
        }
        DosHdr = NULL;
      } else {
        PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + DosHdr->e_lfanew);
        if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
          Error (NULL, 0, 3000, "Invalid", "PE header signature was not found in %s image.", mInImageName);
          goto Finish;
        }
      }
      SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
      for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
        if (strcmp ((char *)SectionHeader->Name, ".reloc") == 0) {
          //
          // Check the reloc section is in the end of image.
          //
          if ((SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData) == FileLength) {
            //
            // Remove .reloc section and update PeImage Header
            //
            FileLength = FileLength - SectionHeader->SizeOfRawData;

            PeHdr->Pe32.FileHeader.Characteristics |= EFI_IMAGE_FILE_RELOCS_STRIPPED;
            if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
              Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->Pe32.OptionalHeader;
              Optional32->SizeOfImage -= SectionHeader->SizeOfRawData;
              Optional32->SizeOfInitializedData -= SectionHeader->SizeOfRawData;
              if (Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
                Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
                Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
              }
            }
            if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
              Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->Pe32.OptionalHeader;
              Optional64->SizeOfImage -= SectionHeader->SizeOfRawData;
              Optional64->SizeOfInitializedData -= SectionHeader->SizeOfRawData;
              if (Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
                Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
                Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
              }
            }
            SectionHeader->Misc.VirtualSize = 0;
            SectionHeader->SizeOfRawData = 0;
            break;
          }
        }
      }
    }
    //
    // Write file
    //
    goto WriteFile;
  }
  //
  // Read the dos & pe hdrs of the image
  //
  DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
  if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
    // NO DOS header, check for PE/COFF header
    PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer);
    if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
      Error (NULL, 0, 3000, "Invalid", "DOS header signature was not found in %s image.", mInImageName);
      goto Finish;
    }
    DosHdr = NULL;
  } else {

    PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + DosHdr->e_lfanew);
    if (PeHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
      Error (NULL, 0, 3000, "Invalid", "PE header signature was not found in %s image.", mInImageName);
      goto Finish;
    }
  }

  if (PeHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_ARM) {
    // Some tools kick out IMAGE_FILE_MACHINE_ARM (0x1c0) vs IMAGE_FILE_MACHINE_ARMT (0x1c2)
    // so patch back to the official UEFI value.
    PeHdr->Pe32.FileHeader.Machine = IMAGE_FILE_MACHINE_ARMT;
  }

  //
  // Set new base address into image
  //
  if (mOutImageType == FW_REBASE_IMAGE || mOutImageType == FW_SET_ADDRESS_IMAGE) {
    if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
      if (NewBaseAddress >= 0x100000000ULL) {
        Error (NULL, 0, 3000, "Invalid", "New base address is larger than 4G for 32bit PE image");
        goto Finish;
      }
    }

    if (NegativeAddr) {
      //
      // Set Base Address to a negative value.
      //
      NewBaseAddress = (UINT64) (0 - NewBaseAddress);
    }
    if (mOutImageType == FW_REBASE_IMAGE) {
      Status = RebaseImage (mInImageName, FileBuffer, NewBaseAddress);
    } else {
      Status = SetAddressToSectionHeader (mInImageName, FileBuffer, NewBaseAddress);
    }
    if (EFI_ERROR (Status)) {
      if (NegativeAddr) {
        Error (NULL, 0, 3000, "Invalid", "Rebase/Set Image %s to Base address -0x%llx can't success", mInImageName, 0 - NewBaseAddress);
      } else {
        Error (NULL, 0, 3000, "Invalid", "Rebase/Set Image %s to Base address 0x%llx can't success", mInImageName, NewBaseAddress);
      }
      goto Finish;
    }

    //
    // Write file
    //
    goto WriteFile;
  }

  //
  // Extract bin data from Pe image.
  //
  if (mOutImageType == FW_BIN_IMAGE) {
    if (FileLength < PeHdr->Pe32.OptionalHeader.SizeOfHeaders) {
      Error (NULL, 0, 3000, "Invalid", "FileSize of %s is not a legal size.", mInImageName);
      goto Finish;
    }
    //
    // Output bin data from exe file
    //
    FileLength = FileLength - PeHdr->Pe32.OptionalHeader.SizeOfHeaders;
    memmove (FileBuffer, FileBuffer + PeHdr->Pe32.OptionalHeader.SizeOfHeaders, FileLength);
    VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    goto WriteFile;
  }

  //
  // Zero Debug Information of Pe Image
  //
  if (mOutImageType == FW_ZERO_DEBUG_IMAGE) {
    Status = ZeroDebugData (FileBuffer, TRUE);
    if (EFI_ERROR (Status)) {
      Error (NULL, 0, 3000, "Invalid", "Zero DebugData Error status is 0x%x", (int) Status);
      goto Finish;
    }

    //
    // Write the updated Image
    //
    VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    goto WriteFile;
  }

  //
  // Set Time Stamp of Pe Image
  //
  if (mOutImageType == FW_SET_STAMP_IMAGE) {
    Status = SetStamp (FileBuffer, TimeStamp);
    if (EFI_ERROR (Status)) {
      goto Finish;
    }

    //
    // Write the updated Image
    //
    VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    goto WriteFile;
  }

  //
  // Extract acpi data from pe image.
  //
  if (mOutImageType == FW_ACPI_IMAGE) {
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
    for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
      if (strcmp ((char *)SectionHeader->Name, ".data") == 0 || strcmp ((char *)SectionHeader->Name, ".sdata") == 0) {
        //
        // Check Acpi Table
        //
        if (SectionHeader->Misc.VirtualSize < SectionHeader->SizeOfRawData) {
          FileLength = SectionHeader->Misc.VirtualSize;
        } else {
          FileLength = SectionHeader->SizeOfRawData;
        }

        if (CheckAcpiTable (FileBuffer + SectionHeader->PointerToRawData, FileLength) != STATUS_SUCCESS) {
          Error (NULL, 0, 3000, "Invalid", "ACPI table check failed in %s.", mInImageName);
          goto Finish;
        }

        //
        // Output Apci data to file
        //
        memmove (FileBuffer, FileBuffer + SectionHeader->PointerToRawData, FileLength);
        VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
        goto WriteFile;
      }
    }
    Error (NULL, 0, 3000, "Invalid", "failed to get ACPI table from %s.", mInImageName);
    goto Finish;
  }
  //
  // Zero all unused fields of the DOS header
  //
  if (DosHdr != NULL) {
    memcpy (&BackupDosHdr, DosHdr, sizeof (EFI_IMAGE_DOS_HEADER));
    memset (DosHdr, 0, sizeof (EFI_IMAGE_DOS_HEADER));
    DosHdr->e_magic  = BackupDosHdr.e_magic;
    DosHdr->e_lfanew = BackupDosHdr.e_lfanew;

    for (Index = sizeof (EFI_IMAGE_DOS_HEADER); Index < (UINT32 ) DosHdr->e_lfanew; Index++) {
      FileBuffer[Index] = (UINT8) DosHdr->e_cp;
    }
  }

  //
  // Initialize TeImage Header
  //
  memset (&TEImageHeader, 0, sizeof (EFI_TE_IMAGE_HEADER));
  TEImageHeader.Signature        = EFI_TE_IMAGE_HEADER_SIGNATURE;
  TEImageHeader.Machine          = PeHdr->Pe32.FileHeader.Machine;
  TEImageHeader.NumberOfSections = (UINT8) PeHdr->Pe32.FileHeader.NumberOfSections;
  TEImageHeader.StrippedSize     = (UINT16) ((UINTN) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader) - (UINTN) FileBuffer);
  TEImageHeader.Subsystem        = (UINT8) Type;

  //
  // Patch the PE header
  //
  PeHdr->Pe32.OptionalHeader.Subsystem = (UINT16) Type;

  if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
    Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->Pe32.OptionalHeader;
    if (!KeepOptionalHeaderFlag) {
      Optional32->MajorOperatingSystemVersion = 0;
      Optional32->MinorOperatingSystemVersion = 0;
      Optional32->MajorImageVersion = 0;
      Optional32->MinorImageVersion = 0;
      Optional32->MajorSubsystemVersion = 0;
      Optional32->MinorSubsystemVersion = 0;
      Optional32->Win32VersionValue = 0;
      Optional32->CheckSum = 0;
      Optional32->SizeOfStackReserve = 0;
      Optional32->SizeOfStackCommit = 0;
      Optional32->SizeOfHeapReserve = 0;
      Optional32->SizeOfHeapCommit = 0;
    }
    TEImageHeader.AddressOfEntryPoint = Optional32->AddressOfEntryPoint;
    TEImageHeader.BaseOfCode          = Optional32->BaseOfCode;
    TEImageHeader.ImageBase           = (UINT64) (Optional32->ImageBase);

    if (Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    }

    if (Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
    }

    //
    // Zero .pdata section data.
    //
    if (!KeepExceptionTableFlag && Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION &&
      Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress != 0 &&
      Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size != 0) {
        SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
        for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++, SectionHeader++) {
          if (SectionHeader->VirtualAddress == Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress) {
            //
            // Zero .pdata Section data
            //
            memset (FileBuffer + SectionHeader->PointerToRawData, 0, SectionHeader->SizeOfRawData);
            //
            // Zero .pdata Section header name
            //
            memset (SectionHeader->Name, 0, sizeof (SectionHeader->Name));
            //
            // Zero Exception Table
            //
            Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = 0;
            Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size           = 0;
            DebugMsg (NULL, 0, 9, "Zero the .pdata section for PE image", NULL);
            break;
          }
        }
    }

    //
    // Strip zero padding at the end of the .reloc section
    //
    if (!KeepZeroPendingFlag && Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
      if (Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size != 0) {
        SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
        for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++, SectionHeader++) {
          //
          // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory
          //
          if (SectionHeader->VirtualAddress == Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) {
            SectionHeader->Misc.VirtualSize = Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
            AllignedRelocSize = (Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size + Optional32->FileAlignment - 1) & (~(Optional32->FileAlignment - 1));
            //
            // Check to see if there is zero padding at the end of the base relocations
            //
            if (AllignedRelocSize < SectionHeader->SizeOfRawData) {
              //
              // Check to see if the base relocations are at the end of the file
              //
              if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional32->SizeOfImage) {
                //
                // All the required conditions are met to strip the zero padding of the end of the base relocations section
                //
                Optional32->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);
                Optional32->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);
                SectionHeader->SizeOfRawData = AllignedRelocSize;
                FileLength = Optional32->SizeOfImage;
                DebugMsg (NULL, 0, 9, "Remove the zero padding bytes at the end of the base relocations", "The size of padding bytes is %u", (unsigned) (SectionHeader->SizeOfRawData - AllignedRelocSize));
              }
            }
          }
        }
      }
    }
  } else if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
    Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->Pe32.OptionalHeader;
    if (!KeepOptionalHeaderFlag) {
      Optional64->MajorOperatingSystemVersion = 0;
      Optional64->MinorOperatingSystemVersion = 0;
      Optional64->MajorImageVersion = 0;
      Optional64->MinorImageVersion = 0;
      Optional64->MajorSubsystemVersion = 0;
      Optional64->MinorSubsystemVersion = 0;
      Optional64->Win32VersionValue = 0;
      Optional64->CheckSum = 0;
      Optional64->SizeOfStackReserve = 0;
      Optional64->SizeOfStackCommit = 0;
      Optional64->SizeOfHeapReserve = 0;
      Optional64->SizeOfHeapCommit = 0;
    }
    TEImageHeader.AddressOfEntryPoint = Optional64->AddressOfEntryPoint;
    TEImageHeader.BaseOfCode          = Optional64->BaseOfCode;
    TEImageHeader.ImageBase           = (UINT64) (Optional64->ImageBase);

    if (Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    }

    if (Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
    }

    //
    // Zero the .pdata section for X64 machine and don't check the Debug Directory is empty
    // For Itaninum and X64 Image, remove .pdata section.
    //
    if ((!KeepExceptionTableFlag && PeHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64)) {
      if (Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION &&
        Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress != 0 &&
        Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size != 0) {
          SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
          for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++, SectionHeader++) {
            if (SectionHeader->VirtualAddress == Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress) {
              //
              // Zero .pdata Section header name
              //
              memset (SectionHeader->Name, 0, sizeof (SectionHeader->Name));

              RuntimeFunction = (RUNTIME_FUNCTION *)(FileBuffer + SectionHeader->PointerToRawData);
              for (Index1 = 0; Index1 < Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size / sizeof (RUNTIME_FUNCTION); Index1++, RuntimeFunction++) {
                SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
                for (Index2 = 0; Index2 < PeHdr->Pe32.FileHeader.NumberOfSections; Index2++, SectionHeader++) {
                  if (RuntimeFunction->UnwindInfoAddress >= SectionHeader->VirtualAddress && RuntimeFunction->UnwindInfoAddress < (SectionHeader->VirtualAddress + SectionHeader->SizeOfRawData)) {
                    UnwindInfo = (UNWIND_INFO *)(FileBuffer + SectionHeader->PointerToRawData + (RuntimeFunction->UnwindInfoAddress - SectionHeader->VirtualAddress));
                    if (UnwindInfo->Version == 1) {
                      memset (UnwindInfo + 1, 0, UnwindInfo->CountOfUnwindCodes * sizeof (UINT16));
                      memset (UnwindInfo, 0, sizeof (UNWIND_INFO));
                    }
                    break;
                  }
                }
                memset (RuntimeFunction, 0, sizeof (RUNTIME_FUNCTION));
              }
              //
              // Zero Exception Table
              //
              Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = 0;
              Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = 0;
              DebugMsg (NULL, 0, 9, "Zero the .pdata section if the machine type is X64 for PE32+ image", NULL);
              break;
            }
          }
      }
    }

    //
    // Strip zero padding at the end of the .reloc section
    //
    if (!KeepZeroPendingFlag && Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
      if (Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size != 0) {
        SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
        for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++, SectionHeader++) {
          //
          // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory
          //
          if (SectionHeader->VirtualAddress == Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) {
            SectionHeader->Misc.VirtualSize = Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
            AllignedRelocSize = (Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size + Optional64->FileAlignment - 1) & (~(Optional64->FileAlignment - 1));
            //
            // Check to see if there is zero padding at the end of the base relocations
            //
            if (AllignedRelocSize < SectionHeader->SizeOfRawData) {
              //
              // Check to see if the base relocations are at the end of the file
              //
              if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional64->SizeOfImage) {
                //
                // All the required conditions are met to strip the zero padding of the end of the base relocations section
                //
                Optional64->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);
                Optional64->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);
                SectionHeader->SizeOfRawData = AllignedRelocSize;
                FileLength = Optional64->SizeOfImage;
                DebugMsg (NULL, 0, 9, "Remove the zero padding bytes at the end of the base relocations", "The size of padding bytes is %u", (unsigned) (SectionHeader->SizeOfRawData - AllignedRelocSize));
              }
            }
          }
        }
      }
    }
  } else {
    Error (NULL, 0, 3000, "Invalid", "Magic 0x%x of PeImage %s is unknown.", PeHdr->Pe32.OptionalHeader.Magic, mInImageName);
    goto Finish;
  }

  if (((PeHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) == 0) && \
    (TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0) && \
    (TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size == 0)) {
      //
      // PeImage can be loaded into memory, but it has no relocation section.
      // Fix TeImage Header to set VA of relocation data directory to not zero, the size is still zero.
      //
      if (Optional32 != NULL) {
        TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = Optional32->SizeOfImage - sizeof (EFI_IMAGE_BASE_RELOCATION);
      } else if (Optional64 != NULL) {
        TEImageHeader.DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = Optional64->SizeOfImage - sizeof (EFI_IMAGE_BASE_RELOCATION);
      }
  }

  //
  // Fill HII section data
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
  for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++) {
    if (stricmp ((char *)SectionHeader[Index].Name, ".hii") == 0) {
      //
      // Update resource section header offset
      //
      SetHiiResourceHeader ((UINT8*) FileBuffer + SectionHeader[Index].PointerToRawData, SectionHeader[Index].VirtualAddress);
      //
      // Update resource section name
      //
      strcpy((char *) SectionHeader[Index].Name, ".rsrc");
      //
      // Update resource data directory.
      //
      if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
        Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->Pe32.OptionalHeader;
        Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = SectionHeader[Index].VirtualAddress;
        Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = SectionHeader[Index].Misc.VirtualSize;
      } else if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
        Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->Pe32.OptionalHeader;
        Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = SectionHeader[Index].VirtualAddress;
        Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = SectionHeader[Index].Misc.VirtualSize;
      }
      break;
    }
  }

  //
  // Zero ExceptionTable Xdata
  //
  if (!KeepExceptionTableFlag) {
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) &(PeHdr->Pe32.OptionalHeader) + PeHdr->Pe32.FileHeader.SizeOfOptionalHeader);
    for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++) {
      if (stricmp ((char *)SectionHeader[Index].Name, ".xdata") == 0) {
        //
        // zero .xdata section
        //
        memset (FileBuffer + SectionHeader[Index].PointerToRawData, 0, SectionHeader[Index].SizeOfRawData);
        DebugMsg (NULL, 0, 9, NULL, "Zero the .xdata section for PE image at Offset 0x%x and Length 0x%x", (unsigned) SectionHeader[Index].PointerToRawData, (unsigned) SectionHeader[Index].SizeOfRawData);
        break;
      }
    }
  }

  //
  // Zero Time/Data field
  //
  ZeroDebugData (FileBuffer, FALSE);

  if (mOutImageType == FW_TE_IMAGE) {
    if ((PeHdr->Pe32.FileHeader.NumberOfSections &~0xFF) || (Type &~0xFF)) {
      //
      // Pack the subsystem and NumberOfSections into 1 byte. Make sure they fit both.
      //
      Error (NULL, 0, 3000, "Invalid", "Image's subsystem or NumberOfSections of PeImage %s cannot be packed into 1 byte.", mInImageName);
      goto Finish;
    }

    if ((PeHdr->Pe32.OptionalHeader.SectionAlignment != PeHdr->Pe32.OptionalHeader.FileAlignment)) {
      //
      // TeImage has the same section alignment and file alignment.
      //
      Error (NULL, 0, 3000, "Invalid", "Section-Alignment and File-Alignment of PeImage %s do not match, they must be equal for a TeImage.", mInImageName);
      goto Finish;
    }

    DebugMsg (NULL, 0, 9, "TeImage Header Info", "Machine type is %X, Number of sections is %X, Stripped size is %X, EntryPoint is %X, BaseOfCode is %X, ImageBase is %llX",
      TEImageHeader.Machine, TEImageHeader.NumberOfSections, TEImageHeader.StrippedSize, (unsigned) TEImageHeader.AddressOfEntryPoint, (unsigned) TEImageHeader.BaseOfCode, (unsigned long long) TEImageHeader.ImageBase);
    //
    // Update Image to TeImage
    //
    FileLength = FileLength - TEImageHeader.StrippedSize;
    memmove (FileBuffer + sizeof (EFI_TE_IMAGE_HEADER), FileBuffer + TEImageHeader.StrippedSize, FileLength);
    FileLength = FileLength + sizeof (EFI_TE_IMAGE_HEADER);
    memcpy (FileBuffer, &TEImageHeader, sizeof (EFI_TE_IMAGE_HEADER));
    VerboseMsg ("the size of output file is %u bytes", (unsigned) (FileLength));
  } else {

    //
    // Following codes are to fix the objcopy's issue:
    // objcopy in binutil 2.50.18 will set PE image's charactices to "RELOC_STRIPPED" if image has no ".reloc" section
    // It cause issue for EFI image which has no ".reloc" sections.
    // Following codes will be removed when objcopy in binutil fix this problem for PE image.
    //
    if ((PeHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0) {
      if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
        Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->Pe32.OptionalHeader;
        if (Optional32->ImageBase == 0) {
          PeHdr->Pe32.FileHeader.Characteristics &= ~EFI_IMAGE_FILE_RELOCS_STRIPPED;
        }
      } else if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
        Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->Pe32.OptionalHeader;
        if (Optional64->ImageBase == 0) {
          PeHdr->Pe32.FileHeader.Characteristics &= ~EFI_IMAGE_FILE_RELOCS_STRIPPED;
        }
      }
    }
  }

WriteFile:
  //
  // Update Image to EfiImage or TE image
  //
  if (ReplaceFlag) {
    if ((FileLength != InputFileLength) || (memcmp (FileBuffer, InputFileBuffer, FileLength) != 0)) {
      //
      // Update File when File is changed.
      //
      fpInOut = fopen (LongFilePath (mInImageName), "wb");
      if (fpInOut == NULL) {
        Error (NULL, 0, 0001, "Error opening file", mInImageName);
        goto Finish;
      }
      fwrite (FileBuffer, 1, FileLength, fpInOut);
      VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    }
  } else {
    if ((OutputFileTime < InputFileTime) || (FileLength != OutputFileLength) || (memcmp (FileBuffer, OutputFileBuffer, FileLength) != 0)) {
      //
      // Update File when File is changed or File is old.
      //
      fpOut = fopen (LongFilePath (OutImageName), "wb");
      if (fpOut == NULL) {
        Error (NULL, 0, 0001, "Error opening output file", OutImageName);
        goto Finish;
      }
      fwrite (FileBuffer, 1, FileLength, fpOut);
      VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);
    }
  }
  mImageSize = FileLength;

Finish:
  if (fpInOut != NULL) {
    if (GetUtilityStatus () != STATUS_SUCCESS) {
      //
      // when file updates failed, original file is still recovered.
      //
      fwrite (InputFileBuffer, 1, InputFileLength, fpInOut);
    }
    //
    // Write converted data into fpInOut file and close input file.
    //
    fclose (fpInOut);
  }

  if (FileBuffer != NULL) {
    free (FileBuffer);
  }

  if (InputFileName != NULL) {
    free (InputFileName);
  }

  if (fpOut != NULL) {
    //
    // Write converted data into fpOut file and close output file.
    //
    fclose (fpOut);
    if (GetUtilityStatus () != STATUS_SUCCESS) {
      if (OutputFileBuffer == NULL) {
        remove (OutImageName);
      } else {
        fpOut = fopen (LongFilePath (OutImageName), "wb");
        fwrite (OutputFileBuffer, 1, OutputFileLength, fpOut);
        fclose (fpOut);
      }
    }
  }

  if (InputFileBuffer != NULL) {
    free (InputFileBuffer);
  }

  if (OutputFileBuffer != NULL) {
    free (OutputFileBuffer);
  }

  //
  // Write module size and time stamp to report file.
  //
  if (OutImageName != NULL) {
    FileLen = strlen (OutImageName);
  }
  if (FileLen >= 4 && strcmp (OutImageName + (FileLen - 4), ".efi") == 0) {
    ReportFileName = (CHAR8 *) malloc (FileLen + 1);
    if (ReportFileName != NULL) {
      strcpy (ReportFileName, OutImageName);
      strcpy (ReportFileName + (FileLen - 4), ".txt");
      ReportFile = fopen (LongFilePath (ReportFileName), "w+");
      if (ReportFile != NULL) {
        fprintf (ReportFile, "MODULE_SIZE = %u\n", (unsigned) mImageSize);
        fprintf (ReportFile, "TIME_STAMP = %u\n", (unsigned) mImageTimeStamp);
        fclose(ReportFile);
      }
      free (ReportFileName);
    }
  }
  VerboseMsg ("%s tool done with return code is 0x%x.", UTILITY_NAME, GetUtilityStatus ());

  return GetUtilityStatus ();
}

STATIC
EFI_STATUS
ZeroDebugData (
  IN OUT UINT8   *FileBuffer,
  BOOLEAN        ZeroDebugFlag
  )
/*++

Routine Description:

  Zero debug information in PeImage.

Arguments:

  FileBuffer    - Pointer to PeImage.
  ZeroDebugFlag - TRUE to zero Debug information, FALSE to only zero time/stamp

Returns:

  EFI_ABORTED   - PeImage is invalid.
  EFI_SUCCESS   - Zero debug data successfully.

--*/
{
  UINT32                           Index;
  UINT32                           DebugDirectoryEntryRva;
  UINT32                           DebugDirectoryEntrySize;
  UINT32                           DebugDirectoryEntryFileOffset;
  UINT32                           ExportDirectoryEntryRva;
  UINT32                           ExportDirectoryEntryFileOffset;
  UINT32                           ResourceDirectoryEntryRva;
  UINT32                           ResourceDirectoryEntryFileOffset;
  EFI_IMAGE_DOS_HEADER            *DosHdr;
  EFI_IMAGE_FILE_HEADER           *FileHdr;
  EFI_IMAGE_OPTIONAL_HEADER32     *Optional32Hdr;
  EFI_IMAGE_OPTIONAL_HEADER64     *Optional64Hdr;
  EFI_IMAGE_SECTION_HEADER        *SectionHeader;
  EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
  EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY *RsdsEntry;
  UINT32                          *NewTimeStamp;

  //
  // Init variable.
  //
  DebugDirectoryEntryRva           = 0;
  DebugDirectoryEntrySize          = 0;
  ExportDirectoryEntryRva          = 0;
  ResourceDirectoryEntryRva        = 0;
  DebugDirectoryEntryFileOffset    = 0;
  ExportDirectoryEntryFileOffset   = 0;
  ResourceDirectoryEntryFileOffset = 0;
  DosHdr   = (EFI_IMAGE_DOS_HEADER *)  FileBuffer;
  FileHdr  = (EFI_IMAGE_FILE_HEADER *) (FileBuffer + DosHdr->e_lfanew + sizeof (UINT32));


  DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
  if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
    // NO DOS header, must start with PE/COFF header
    FileHdr  = (EFI_IMAGE_FILE_HEADER *)(FileBuffer + sizeof (UINT32));
  } else {
    FileHdr  = (EFI_IMAGE_FILE_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof (UINT32));
  }

  //
  // Get Debug, Export and Resource EntryTable RVA address.
  // Resource Directory entry need to review.
  //
  Optional32Hdr = (EFI_IMAGE_OPTIONAL_HEADER32 *) ((UINT8*) FileHdr + sizeof (EFI_IMAGE_FILE_HEADER));
  Optional64Hdr = (EFI_IMAGE_OPTIONAL_HEADER64 *) ((UINT8*) FileHdr + sizeof (EFI_IMAGE_FILE_HEADER));
  if (Optional32Hdr->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) Optional32Hdr +  FileHdr->SizeOfOptionalHeader);
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXPORT && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].Size != 0) {
      ExportDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    }
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size != 0) {
      ResourceDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    }
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size != 0) {
      DebugDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      DebugDirectoryEntrySize = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
      if (ZeroDebugFlag) {
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = 0;
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = 0;
      }
    }
  } else {
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) Optional64Hdr +  FileHdr->SizeOfOptionalHeader);
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXPORT && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].Size != 0) {
      ExportDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    }
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size != 0) {
      ResourceDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    }
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size != 0) {
      DebugDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
      DebugDirectoryEntrySize = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
      if (ZeroDebugFlag) {
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = 0;
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = 0;
      }
    }
  }

  //
  // Get DirectoryEntryTable file offset.
  //
  for (Index = 0; Index < FileHdr->NumberOfSections; Index ++, SectionHeader ++) {
    if (DebugDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        DebugDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        DebugDirectoryEntryFileOffset =
        DebugDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
    if (ExportDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        ExportDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        ExportDirectoryEntryFileOffset =
        ExportDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
    if (ResourceDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        ResourceDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        ResourceDirectoryEntryFileOffset =
        ResourceDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
  }

  //
  //Zero Debug Data and TimeStamp
  //
  FileHdr->TimeDateStamp = 0;
  mImageTimeStamp = 0;
  if (ExportDirectoryEntryFileOffset != 0) {
    NewTimeStamp  = (UINT32 *) (FileBuffer + ExportDirectoryEntryFileOffset + sizeof (UINT32));
    *NewTimeStamp = 0;
  }

  if (ResourceDirectoryEntryFileOffset != 0) {
    NewTimeStamp  = (UINT32 *) (FileBuffer + ResourceDirectoryEntryFileOffset + sizeof (UINT32));
    *NewTimeStamp = 0;
  }

  if (DebugDirectoryEntryFileOffset != 0) {
    DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (FileBuffer + DebugDirectoryEntryFileOffset);
    Index = 0;
    for (Index=0; Index < DebugDirectoryEntrySize / sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); Index ++, DebugEntry ++) {
      DebugEntry->TimeDateStamp = 0;
      if (mIsConvertXip) {
        DebugEntry->FileOffset = DebugEntry->RVA;
      }
      if (ZeroDebugFlag || DebugEntry->Type != EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
        memset (FileBuffer + DebugEntry->FileOffset, 0, DebugEntry->SizeOfData);
        memset (DebugEntry, 0, sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY));
      }
      if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
        RsdsEntry = (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY *) (FileBuffer + DebugEntry->FileOffset);
        if (RsdsEntry->Signature == CODEVIEW_SIGNATURE_MTOC) {
          // MTOC sets DebugDirectoryEntrySize to size of the .debug section, so fix it.
          if (!ZeroDebugFlag) {
            if (Optional32Hdr->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
              Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
            } else {
              Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
            }
          }
          break;
        }
      }
    }
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
SetStamp (
  IN OUT UINT8  *FileBuffer,
  IN     CHAR8  *TimeStamp
  )
/*++

Routine Description:

  Set new time stamp into PeImage FileHdr and Directory table:
  Debug, Export and Resource.

Arguments:

  FileBuffer    - Pointer to PeImage.
  TimeStamp     - Time stamp string.

Returns:

  EFI_INVALID_PARAMETER   - TimeStamp format is not recognized.
  EFI_SUCCESS             - Set new time stamp in this image successfully.

--*/
{
  struct tm                       stime;
  struct tm                       *ptime;
  time_t                          newtime;
  UINT32                          Index;
  UINT32                          DebugDirectoryEntryRva;
  UINT32                          DebugDirectoryEntryFileOffset;
  UINT32                          ExportDirectoryEntryRva;
  UINT32                          ExportDirectoryEntryFileOffset;
  UINT32                          ResourceDirectoryEntryRva;
  UINT32                          ResourceDirectoryEntryFileOffset;
  EFI_IMAGE_DOS_HEADER            *DosHdr;
  EFI_IMAGE_FILE_HEADER           *FileHdr;
  EFI_IMAGE_OPTIONAL_HEADER32     *Optional32Hdr;
  EFI_IMAGE_OPTIONAL_HEADER64     *Optional64Hdr;
  EFI_IMAGE_SECTION_HEADER        *SectionHeader;
  UINT32                          *NewTimeStamp;

  //
  // Init variable.
  //
  DebugDirectoryEntryRva           = 0;
  DebugDirectoryEntryFileOffset    = 0;
  ExportDirectoryEntryRva          = 0;
  ExportDirectoryEntryFileOffset   = 0;
  ResourceDirectoryEntryRva        = 0;
  ResourceDirectoryEntryFileOffset = 0;
  //
  // Get time and date that will be set.
  //
  if (TimeStamp == NULL) {
    Error (NULL, 0, 3000, "Invalid", "TimeStamp cannot be NULL.");
    return EFI_INVALID_PARAMETER;
  }
  //
  // compare the value with "NOW", if yes, current system time is set.
  //
  if (stricmp (TimeStamp, "NOW") == 0) {
    //
    // get system current time and date
    //
    time (&newtime);
  } else {
    //
    // Check Time Format strictly yyyy-mm-dd 00:00:00
    //
    for (Index = 0; TimeStamp[Index] != '\0' && Index < 20; Index ++) {
      if (Index == 4 || Index == 7) {
        if (TimeStamp[Index] == '-') {
          continue;
        }
      } else if (Index == 13 || Index == 16) {
        if (TimeStamp[Index] == ':') {
          continue;
        }
      } else if (Index == 10 && TimeStamp[Index] == ' ') {
        continue;
      } else if ((TimeStamp[Index] < '0') || (TimeStamp[Index] > '9')) {
        break;
      }
    }

    if (Index < 19 || TimeStamp[19] != '\0') {
      Error (NULL, 0, 1003, "Invalid option value", "Incorrect Time \"%s\"\n  Correct Format \"yyyy-mm-dd 00:00:00\"", TimeStamp);
      return EFI_INVALID_PARAMETER;
    }

    //
    // get the date and time from TimeStamp
    //
    if (sscanf (TimeStamp, "%d-%d-%d %d:%d:%d",
            &stime.tm_year,
            &stime.tm_mon,
            &stime.tm_mday,
            &stime.tm_hour,
            &stime.tm_min,
            &stime.tm_sec
            ) != 6) {
      Error (NULL, 0, 1003, "Invalid option value", "Incorrect Tiem \"%s\"\n  Correct Format \"yyyy-mm-dd 00:00:00\"", TimeStamp);
      return EFI_INVALID_PARAMETER;
    }

    //
    // in struct, Month (0 - 11; Jan = 0). So decrease 1 from it
    //
    if (stime.tm_mon <= 0 || stime.tm_mday <=0) {
      Error (NULL, 0, 3000, "Invalid", "%s Invalid date!", TimeStamp);
      return EFI_INVALID_PARAMETER;
    }
    stime.tm_mon -= 1;

    //
    // in struct, Year (current year minus 1900)
    // and only the dates can be handled from Jan 1, 1970 to Jan 18, 2038
    //
    //
    // convert 0 -> 100 (2000), 1 -> 101 (2001), ..., 38 -> 138 (2038)
    //
    if (stime.tm_year >= 1970 && stime.tm_year <= 2038) {
      //
      // convert 1970 -> 70, 2000 -> 100, ...
      //
      stime.tm_year -= 1900;
    } else {
      Error (NULL, 0, 3000, "Invalid", "%s Invalid or unsupported datetime!", TimeStamp);
      return EFI_INVALID_PARAMETER;
    }

    //
    // convert the date and time to time_t format
    //
    newtime = mktime (&stime);
    if (newtime == (time_t) - 1) {
      Error (NULL, 0, 3000, "Invalid", "%s Invalid or unsupported datetime!", TimeStamp);
      return EFI_INVALID_PARAMETER;
    }
  }

  ptime = localtime (&newtime);
  if (ptime != NULL) {
    DebugMsg (NULL, 0, 9, "New Image Time Stamp", "%04d-%02d-%02d %02d:%02d:%02d",
              ptime->tm_year + 1900, ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour, ptime->tm_min, ptime->tm_sec);
  }
  //
  // Set new time and data into PeImage.
  //
  DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
  if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
    // NO DOS header, must start with PE/COFF header
    FileHdr  = (EFI_IMAGE_FILE_HEADER *)(FileBuffer + sizeof (UINT32));
  } else {
    FileHdr  = (EFI_IMAGE_FILE_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof (UINT32));
  }

  //
  // Get Debug, Export and Resource EntryTable RVA address.
  // Resource Directory entry need to review.
  //
  if (FileHdr->Machine == EFI_IMAGE_MACHINE_IA32) {
    Optional32Hdr = (EFI_IMAGE_OPTIONAL_HEADER32 *) ((UINT8*) FileHdr + sizeof (EFI_IMAGE_FILE_HEADER));
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) Optional32Hdr +  FileHdr->SizeOfOptionalHeader);
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXPORT && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].Size != 0) {
      ExportDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    }
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size != 0) {
      ResourceDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    }
    if (Optional32Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG && \
        Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size != 0) {
      DebugDirectoryEntryRva = Optional32Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
    }
  } else {
    Optional64Hdr = (EFI_IMAGE_OPTIONAL_HEADER64 *) ((UINT8*) FileHdr + sizeof (EFI_IMAGE_FILE_HEADER));
    SectionHeader = (EFI_IMAGE_SECTION_HEADER *) ((UINT8 *) Optional64Hdr +  FileHdr->SizeOfOptionalHeader);
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_EXPORT && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].Size != 0) {
      ExportDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    }
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size != 0) {
      ResourceDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    }
    if (Optional64Hdr->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG && \
        Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size != 0) {
      DebugDirectoryEntryRva = Optional64Hdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
    }
  }

  //
  // Get DirectoryEntryTable file offset.
  //
  for (Index = 0; Index < FileHdr->NumberOfSections; Index ++, SectionHeader ++) {
    if (DebugDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        DebugDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        DebugDirectoryEntryFileOffset =
        DebugDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
    if (ExportDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        ExportDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        ExportDirectoryEntryFileOffset =
        ExportDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
    if (ResourceDirectoryEntryRva >= SectionHeader->VirtualAddress &&
        ResourceDirectoryEntryRva < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) {
        ResourceDirectoryEntryFileOffset =
        ResourceDirectoryEntryRva - SectionHeader->VirtualAddress + SectionHeader->PointerToRawData;
    }
  }

  //
  // Set new stamp
  //
  FileHdr->TimeDateStamp = (UINT32) newtime;
  mImageTimeStamp = (UINT32) newtime;
  if (ExportDirectoryEntryRva != 0) {
    NewTimeStamp  = (UINT32 *) (FileBuffer + ExportDirectoryEntryFileOffset + sizeof (UINT32));
    *NewTimeStamp = (UINT32) newtime;
  }

  if (ResourceDirectoryEntryRva != 0) {
    NewTimeStamp  = (UINT32 *) (FileBuffer + ResourceDirectoryEntryFileOffset + sizeof (UINT32));
    *NewTimeStamp = (UINT32) newtime;
  }

  if (DebugDirectoryEntryRva != 0) {
    NewTimeStamp  = (UINT32 *) (FileBuffer + DebugDirectoryEntryFileOffset + sizeof (UINT32));
    *NewTimeStamp = (UINT32) newtime;
  }

  return EFI_SUCCESS;
}

STATIC
STATUS
MicrocodeReadData (
  FILE          *InFptr,
  UINT32        *Data
  )
/*++

Routine Description:
  Read a 32-bit microcode data value from a text file and convert to raw binary form.

Arguments:
  InFptr    - file pointer to input text file
  Data      - pointer to where to return the data parsed

Returns:
  STATUS_SUCCESS    - no errors or warnings, Data contains valid information
  STATUS_ERROR      - errors were encountered

--*/
{
  CHAR8  Line[MAX_LINE_LEN];
  CHAR8  *cptr;
  int    ScannedData = 0;

  Line[MAX_LINE_LEN - 1]  = 0;
  while (1) {
    if (fgets (Line, MAX_LINE_LEN, InFptr) == NULL) {
      return STATUS_ERROR;
    }
    //
    // If it was a binary file, then it may have overwritten our null terminator
    //
    if (Line[MAX_LINE_LEN - 1] != 0) {
      return STATUS_ERROR;
    }

    //
    // strip space
    //
    for (cptr = Line; *cptr && isspace((int)*cptr); cptr++) {
    }

    // Skip Blank Lines and Comment Lines
    if ((strlen(cptr) != 0) && (*cptr != ';')) {
      break;
    }
  }

  // Look for
  // dd 000000001h ; comment
  // dd XXXXXXXX
  // DD  XXXXXXXXX
  //  DD XXXXXXXXX
  //
  if ((tolower((int)cptr[0]) == 'd') && (tolower((int)cptr[1]) == 'd') && isspace ((int)cptr[2])) {
    //
    // Skip blanks and look for a hex digit
    //
    cptr += 3;
    for (; *cptr && isspace((int)*cptr); cptr++) {
    }
    if (isxdigit ((int)*cptr)) {
      if (sscanf (cptr, "%X", &ScannedData) != 1) {
        return STATUS_ERROR;
      }
    }
    *Data = (UINT32) ScannedData;
    return STATUS_SUCCESS;
  }

  return STATUS_ERROR;
}
