/** @file
UEFI Image Loader library implementation for PE/COFF Images.
Copyright (c) 2021, Marvin Häuser. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "PeSupport.h"
RETURN_STATUS
UefiImageInitializeContextPreHashPe (
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN CONST VOID *FileBuffer,
IN UINT32 FileSize,
IN UINT8 ImageOrigin
)
{
return PeCoffInitializeContext (&Context->Ctx.Pe, FileBuffer, FileSize, ImageOrigin);
}
BOOLEAN
UefiImageHashImageDefaultPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN OUT VOID *HashContext,
IN UEFI_IMAGE_LOADER_HASH_UPDATE HashUpdate
)
{
return PeCoffHashImageAuthenticode (&Context->Ctx.Pe, HashContext, HashUpdate);
}
RETURN_STATUS
UefiImageInitializeContextPostHashPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return RETURN_SUCCESS;
}
RETURN_STATUS
UefiImageLoadImagePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT VOID *Destination,
IN UINT32 DestinationSize
)
{
return PeCoffLoadImage (&Context->Ctx.Pe, Destination, DestinationSize);
}
BOOLEAN
UefiImageImageIsInplacePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffImageIsInplace (&Context->Ctx.Pe);
}
RETURN_STATUS
UefiImageLoadImageInplacePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffLoadImageInplace (&Context->Ctx.Pe);
}
RETURN_STATUS
UefiImageRelocateImageInplacePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffRelocateImageInplace (&Context->Ctx.Pe);
}
RETURN_STATUS
UefiImageLoaderGetRuntimeContextSizePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *Size
)
{
return PeCoffLoaderGetRuntimeContextSize (&Context->Ctx.Pe, Size);
}
RETURN_STATUS
UefiImageRelocateImagePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN UINT64 BaseAddress,
OUT VOID *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
)
{
return PeCoffRelocateImage (
&Context->Ctx.Pe,
BaseAddress,
(PE_COFF_LOADER_RUNTIME_CONTEXT *)RuntimeContext,
RuntimeContextSize
);
}
RETURN_STATUS
UefiImageRuntimeRelocateImagePe (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN UINT64 BaseAddress,
IN CONST VOID *RuntimeContext
)
{
return PeCoffRuntimeRelocateImage (
Image,
ImageSize,
BaseAddress,
(CONST PE_COFF_LOADER_RUNTIME_CONTEXT *)RuntimeContext
);
}
VOID
UefiImageDiscardSegmentsPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
PeCoffDiscardSections (&Context->Ctx.Pe);
}
RETURN_STATUS
UefiImageGetSymbolsPathPe (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST CHAR8 **SymbolsPath,
OUT UINT32 *SymbolsPathSize
)
{
return PeCoffGetPdbPath (&Context->Ctx.Pe, SymbolsPath, SymbolsPathSize);
}
RETURN_STATUS
UefiImageGetFirstCertificatePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT CONST WIN_CERTIFICATE **Certificate
)
{
return PeCoffGetFirstCertificate (&Context->Ctx.Pe, Certificate);
}
RETURN_STATUS
UefiImageGetNextCertificatePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN OUT CONST WIN_CERTIFICATE **Certificate
)
{
return PeCoffGetNextCertificate (&Context->Ctx.Pe, Certificate);
}
RETURN_STATUS
UefiImageGetHiiDataRvaPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *HiiRva,
OUT UINT32 *HiiSize
)
{
return PeCoffGetHiiDataRva (&Context->Ctx.Pe, HiiRva, HiiSize);
}
UINT32
UefiImageGetEntryPointAddressPe (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetAddressOfEntryPoint (&Context->Ctx.Pe);
}
UINT16
UefiImageGetMachinePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetMachine (&Context->Ctx.Pe);
}
UINT16
UefiImageGetSubsystemPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetSubsystem (&Context->Ctx.Pe);
}
UINT32
UefiImageGetSegmentAlignmentPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetSectionAlignment (&Context->Ctx.Pe);
}
UINT32
UefiImageGetImageSizePe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetSizeOfImage (&Context->Ctx.Pe);
}
UINT64
UefiImageGetBaseAddressPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetImageBase (&Context->Ctx.Pe);
}
BOOLEAN
UefiImageGetRelocsStrippedPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffGetRelocsStripped (&Context->Ctx.Pe);
}
UINTN
UefiImageLoaderGetImageAddressPe (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
}
UINTN
UefiImageLoaderGetDebugAddressPe (
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
return PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
}
/**
Retrieves the memory protection attributes corresponding to PE/COFF Image
section permissions.
@param[in] Characteristics The PE/COFF Image section permissions
@returns The memory protection attributes corresponding to the PE/COFF Image
section permissions.
**/
STATIC
UINT32
InternalCharacteristicsToAttributes (
IN UINT32 Characteristics
)
{
UINT32 Attributes;
if (PcdGetBool (PcdImageLoaderRemoveXForWX) && (Characteristics & (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_WRITE)) == (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_WRITE)) {
Characteristics &= ~EFI_IMAGE_SCN_MEM_EXECUTE;
}
Attributes = 0;
if ((Characteristics & EFI_IMAGE_SCN_MEM_READ) == 0) {
Attributes |= EFI_MEMORY_RP;
}
if ((Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) {
Attributes |= EFI_MEMORY_XP;
}
if ((Characteristics & EFI_IMAGE_SCN_MEM_WRITE) == 0) {
Attributes |= EFI_MEMORY_RO;
}
return Attributes;
}
/**
Index the read-only padding following an Image record section, if existent.
@param[in,out] RecordSegment The Image record section for the current memory
protection range. May be extended if it is of
the same type as the adjacent padding. At least
one more record section must be reserved after
it in order to index the read-only padding.
@param[in] NextAddress The start address of the next memory permission
range. This may be the end address of the Image
in order to cover the Image trailer.
@param[in] EndAddress The end address of the current memory permission
range. This also denotes the start of the added
read-only padding.
@param[in] Attributes The memory protection attributes of the current
memory permission range.
@returns The amount of Image record sections that have been appended.
**/
STATIC
UINT8
InternalInsertImageRecordSegmentPadding (
IN OUT UEFI_IMAGE_RECORD_SEGMENT *RecordSegment,
IN UINT32 EndAddress,
IN UINT32 NextAddress,
IN UINT32 Attributes
)
{
ASSERT (RecordSegment != NULL);
ASSERT (EndAddress <= NextAddress);
if (NextAddress == EndAddress) {
return 0;
}
//
// Add a new Image record section or expand the previous one depending on
// the the permissions of the previous Image record section.
//
if (Attributes == (EFI_MEMORY_XP | EFI_MEMORY_RO)) {
RecordSegment->Size += NextAddress - EndAddress;
return 0;
}
++RecordSegment;
RecordSegment->Size = NextAddress - EndAddress;
RecordSegment->Attributes = EFI_MEMORY_XP | EFI_MEMORY_RO;
return 1;
}
UEFI_IMAGE_RECORD *
UefiImageLoaderGetImageRecordPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
UEFI_IMAGE_RECORD *ImageRecord;
UINT32 MaxNumRecordSegments;
UINT32 NumRecordSegments;
UEFI_IMAGE_RECORD_SEGMENT *RecordSegment;
UINTN ImageAddress;
UINT32 SizeOfImage;
UINT32 SectionAlignment;
CONST EFI_IMAGE_SECTION_HEADER *Sections;
UINT16 NumberOfSections;
UINT16 SectionIndex;
CONST EFI_IMAGE_SECTION_HEADER *Section;
UINT32 SectionAddress;
UINT32 SectionSize;
UINT32 SectionCharacteristics;
UINT32 StartAddress;
UINT32 EndAddress;
UINT32 Characteristics;
UINT32 Attributes;
ASSERT (Context != NULL);
//
// Determine the maximum amount of Image record sections and allocate the
// Image record.
//
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
STATIC_ASSERT (
MAX_UINT16 <= MAX_UINT32 / 2 - 1,
"The following arithmetic may overflow."
);
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) == 0) {
//
// In case of contiguous Image sections, there can be two additional record
// sections (Image Headers and trailer, e.g. debug information).
//
MaxNumRecordSegments = (UINT32) NumberOfSections + 2;
} else {
//
// In case of possibly non-contiguous Image sections, there can be a trailer
// per Image section (the last Image section's trailer is the same as the
// Image trailer), as well as additionally the Image Headers.
//
MaxNumRecordSegments = (UINT32) NumberOfSections * 2 + 1;
}
ImageRecord = AllocatePool (
sizeof (*ImageRecord)
+ MaxNumRecordSegments * sizeof (*ImageRecord->Segments)
);
if (ImageRecord == NULL) {
return NULL;
}
ImageRecord->Signature = UEFI_IMAGE_RECORD_SIGNATURE;
InitializeListHead (&ImageRecord->Link);
SectionAlignment = PeCoffGetSectionAlignment (&Context->Ctx.Pe);
//
// Map the Image Headers as read-only data. If the first Image section is
// loaded at the start of the Image memory space, the condition
// SectionAddress != StartAddress does not hold and these definitions will be
// ignored.
//
StartAddress = 0;
EndAddress = PeCoffGetSizeOfHeaders (&Context->Ctx.Pe);
Characteristics = EFI_IMAGE_SCN_MEM_READ;
Attributes = EFI_MEMORY_XP | EFI_MEMORY_RO;
ASSERT (Attributes == InternalCharacteristicsToAttributes (Characteristics));
//
// Create an Image record section for every permission-distinct range of the
// Image. The current range [StartAddress, EndAddress) shares the memory
// permissions Charactersitics/Attributes and is extended till a new
// memory permission configuration is required. Headers and trailers treated
// as read-only data.
//
NumRecordSegments = 0;
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
Section = Sections + SectionIndex;
//
// Skip empty Image sections to avoid unnecessary splitting.
//
if (Section->VirtualSize == 0) {
continue;
}
//
// These arithmetics are safe as guaranteed by PeCoffInitializeContext().
//
SectionAddress = Section->VirtualAddress;
SectionSize = ALIGN_VALUE (Section->VirtualSize, SectionAlignment);
SectionCharacteristics = Section->Characteristics & (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_MEM_WRITE);
//
// Skip Image sections with the same memory permissions as the current range
// as they can be merged. For this, the Image sections must be adjacent, or
// the range must have the same memory permissions as the padding inbetween
// (read-only).
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) == 0
|| SectionAddress == EndAddress
|| Characteristics == EFI_IMAGE_SCN_MEM_READ) {
if (SectionCharacteristics == Characteristics) {
EndAddress = SectionAddress + SectionSize;
continue;
}
}
//
// Only create an entry if the range is not empty, otherwise discard it and
// start a new one. Even with skipping empty Image sections, this can still
// happen for the Image Headers when the first Image section starts at 0.
//
if (SectionAddress != StartAddress) {
//
// Create an Image record section for the current memory permission range.
//
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
RecordSegment->Size = EndAddress - StartAddress;
RecordSegment->Attributes = Attributes;
++NumRecordSegments;
//
// If the previous range is not adjacent to the current Image section,
// report the padding as read-only data.
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) != 0) {
NumRecordSegments += InternalInsertImageRecordSegmentPadding (
RecordSegment,
EndAddress,
SectionAddress,
Attributes
);
}
StartAddress = SectionAddress;
}
//
// Start a Image record section with the current Image section.
//
EndAddress = SectionAddress + SectionSize;
Characteristics = SectionCharacteristics;
Attributes = InternalCharacteristicsToAttributes (Characteristics);
}
//
// Image Record sections are only created once a non-empty Image section is
// encountered that requests a different memory permission configuration.
// As such, the last memory permission range is never converted in the loop.
// If the loop never produced such, this is true for the Image Headers, which
// cannot be empty.
//
ASSERT (StartAddress < EndAddress);
//
// Create an Image record section for the last Image memory permission range.
//
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
RecordSegment->Size = EndAddress - StartAddress;
RecordSegment->Attributes = Attributes;
++NumRecordSegments;
ImageAddress = PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
SizeOfImage = PeCoffGetSizeOfImage (&Context->Ctx.Pe);
//
// The Image trailer, if existent, is treated as padding and as such is
// reported as read-only data, as intended. Because it is not part of the
// original Image memory space, this needs to happen whether Image sections
// are guaranteed to be contiguously form the entire Image memory space or
// not.
//
NumRecordSegments += InternalInsertImageRecordSegmentPadding (
RecordSegment,
EndAddress,
SizeOfImage,
Attributes
);
ImageRecord->NumSegments = NumRecordSegments;
ImageRecord->StartAddress = ImageAddress;
ImageRecord->EndAddress = ImageAddress + SizeOfImage;
//
// Zero the remaining array entries to avoid uninitialised data.
//
ZeroMem (
ImageRecord->Segments + NumRecordSegments,
(MaxNumRecordSegments - NumRecordSegments) * sizeof (*ImageRecord->Segments)
);
return ImageRecord;
}
// FIXME: Docs
STATIC
RETURN_STATUS
InternalDebugLocateImage (
OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN CHAR8 *Buffer,
IN UINTN Address,
IN BOOLEAN Recurse,
IN UINT8 ImageOrigin
)
{
RETURN_STATUS Status;
RETURN_STATUS DosStatus;
PE_COFF_LOADER_IMAGE_CONTEXT DosContext;
ASSERT (((UINTN) Buffer & 3U) == 0);
//
// Align the search buffer to a 4 Byte boundary.
//
// Search for the Image Header in 4 Byte steps. All dynamically loaded
// Images start at a page boundary to allow for Image section protection,
// but XIP Images may not. As all Image Headers are at least 4 Byte aligned
// due to natural alignment, even XIP TE Image Headers should start at a
// 4 Byte boundary.
//
// Do not attempt to access memory of the first page as it may be protected as
// part of NULL dereference detection.
//
for (; EFI_PAGE_SIZE <= (UINTN) Buffer; Buffer -= 4) {
//
// Try to parse the current memory as PE/COFF or TE Image. Pass MAX_UINT32
// as the file size as there isn't any more information available. Only the
// Image Header memory will be accessed as part of initialisation.
//
Status = PeCoffInitializeContext (
Context,
Buffer,
MAX_UINT32,
ImageOrigin
);
if (RETURN_ERROR (Status)) {
continue;
}
if (!Recurse) {
//
// For PE/COFF Images, the PE/COFF Image Header may be discovered while
// there may still be a preceeding DOS Image Header. All RVAs are relatvie
// to the start of the Image memory space, of which the DOS Image Header
// is a part of, if existent. Allow one level of recursion to find a lower
// Image Base including the DOS Image Header.
//
if (Context->ExeHdrOffset == 0) {
DosStatus = InternalDebugLocateImage (
&DosContext,
Buffer - 4,
Address,
TRUE,
ImageOrigin
);
if (!RETURN_ERROR (DosStatus)) {
Buffer = DosContext.ImageBuffer;
CopyMem (Context, &DosContext, sizeof (*Context));
}
}
}
//
// We know that (UINTN) Buffer <= Address from the initialisation.
//
// FIXME: Set to non-stripped base for XIP TE Images.
if (Address < (UINTN) Buffer + PeCoffGetSizeOfImage (Context)) {
Context->ImageBuffer = Buffer;
//
// Zero the raw file information as we are initialising from a potentially
// non-XIP in-memory Image.
//
Context->FileBuffer = NULL;
Context->FileSize = 0;
return RETURN_SUCCESS;
}
//
// Continue for the unlikely case that a PE/COFF or TE Image embeds another
// one within its data, the outer Image may still follow.
//
}
return RETURN_NOT_FOUND;
}
RETURN_STATUS
UefiImageDebugLocateImagePe (
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
IN UINTN Address,
IN UINT8 ImageOrigin
)
{
RETURN_STATUS Status;
Status = RETURN_NOT_FOUND;
//
// As this function is intrinsically unsafe, do not allow its usage outside of
// DEBUG-enabled code.
//
DEBUG_CODE_BEGIN ();
//
// If the Image Headers are not loaded explicitly, only XIP Images and Images
// that embed the Image Header in the first Image section can be located. As
// this is not the case for the majority of Images, don't attempt to locate
// the Image base to not access too much (potentially protected) memory.
//
if (!PcdGetBool (PcdImageLoaderLoadHeader)) {
return RETURN_NOT_FOUND;
}
Context->FormatIndex = UefiImageFormatPe;
//
// Align the search buffer to a 4 Byte boundary.
//
Status = InternalDebugLocateImage (
&Context->Ctx.Pe,
(CHAR8 *) (Address & ~(UINTN) 3U),
Address,
FALSE,
ImageOrigin
);
DEBUG_CODE_END ();
return Status;
}
RETURN_STATUS
UefiImageGetFixedAddressPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
OUT UINT64 *Address
)
{
UINT32 SectionAlignment;
CONST EFI_IMAGE_SECTION_HEADER *Sections;
UINT16 NumberOfSections;
UINT16 SectionIndex;
UINT64 FixedAddress;
ASSERT (Address != NULL);
SectionAlignment = PeCoffGetSectionAlignment (&Context->Ctx.Pe);
//
// If this feature is enabled, the build tool will save the address in the
// PointerToRelocations and PointerToLineNumbers fields of the first Image
// section header that doesn't hold code. The 64-bit value across those fields
// will be non-zero if and only if the module has been assigned an address.
//
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
if ((Sections[SectionIndex].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
continue;
}
FixedAddress = ReadUnaligned64 (
(CONST VOID *) &Sections[SectionIndex].PointerToRelocations
);
if (FixedAddress != 0) {
if (!IS_ALIGNED (FixedAddress, SectionAlignment)) {
return RETURN_UNSUPPORTED;
}
*Address = FixedAddress;
return RETURN_SUCCESS;
}
break;
}
return RETURN_NOT_FOUND;
}
VOID
UefiImageDebugPrintSegmentsPe (
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
)
{
CONST EFI_IMAGE_SECTION_HEADER *Sections;
UINT16 NumberOfSections;
UINT16 SectionIndex;
CONST UINT8 *Name;
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
Name = Sections[SectionIndex].Name;
DEBUG ((
DEBUG_VERBOSE,
" Section - '%c%c%c%c%c%c%c%c'\n"
" VirtualSize - 0x%08x\n"
" VirtualAddress - 0x%08x\n"
" SizeOfRawData - 0x%08x\n"
" PointerToRawData - 0x%08x\n"
" PointerToRelocations - 0x%08x\n"
" PointerToLinenumbers - 0x%08x\n"
" NumberOfRelocations - 0x%08x\n"
" NumberOfLinenumbers - 0x%08x\n"
" Characteristics - 0x%08x\n",
Name[0], Name[1], Name[2], Name[3], Name[4], Name[5], Name[6], Name[7],
Sections[SectionIndex].VirtualSize,
Sections[SectionIndex].VirtualAddress,
Sections[SectionIndex].SizeOfRawData,
Sections[SectionIndex].PointerToRawData,
Sections[SectionIndex].PointerToRelocations,
Sections[SectionIndex].PointerToLinenumbers,
Sections[SectionIndex].NumberOfRelocations,
Sections[SectionIndex].NumberOfLinenumbers,
Sections[SectionIndex].Characteristics
));
}
}
GLOBAL_REMOVE_IF_UNREFERENCED CONST UEFI_IMAGE_FORMAT_SUPPORT mPeSupport = {
UefiImageInitializeContextPreHashPe,
UefiImageHashImageDefaultPe,
UefiImageInitializeContextPostHashPe,
UefiImageLoadImagePe,
UefiImageImageIsInplacePe,
UefiImageLoadImageInplacePe,
UefiImageRelocateImageInplacePe,
UefiImageLoaderGetRuntimeContextSizePe,
UefiImageRelocateImagePe,
UefiImageRuntimeRelocateImagePe,
UefiImageDiscardSegmentsPe,
UefiImageGetSymbolsPathPe,
UefiImageGetFirstCertificatePe,
UefiImageGetNextCertificatePe,
UefiImageGetHiiDataRvaPe,
UefiImageGetEntryPointAddressPe,
UefiImageGetMachinePe,
UefiImageGetSubsystemPe,
UefiImageGetSegmentAlignmentPe,
UefiImageGetImageSizePe,
UefiImageGetBaseAddressPe,
UefiImageGetRelocsStrippedPe,
UefiImageLoaderGetImageAddressPe,
UefiImageLoaderGetDebugAddressPe,
UefiImageLoaderGetImageRecordPe,
UefiImageDebugLocateImagePe,
UefiImageGetFixedAddressPe,
UefiImageDebugPrintSegmentsPe
};