/** @file
This file contains implementation for additional PE/COFF functionality needed to use
Platform Runtime Mechanism (PRM) modules.
Copyright (c) Microsoft Corporation
Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#define _DBGMSGID_ "[PRMPECOFFLIB]"
/**
Gets a pointer to the export directory in a given PE/COFF image.
@param[in] ImageExportDirectory A pointer to an export directory table in a PE/COFF image.
@param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
PE/COFF image context for the Image containing the PRM Module Export
Descriptor table.
@param[out] ExportDescriptor A pointer to a pointer to the PRM Module Export Descriptor table found
in the ImageExportDirectory given.
@retval EFI_SUCCESS The PRM Module Export Descriptor table was found successfully.
@retval EFI_INVALID_PARAMETER A required parameter is NULL.
@retval EFI_NOT_FOUND The PRM Module Export Descriptor table was not found in the given
ImageExportDirectory.
**/
EFI_STATUS
GetPrmModuleExportDescriptorTable (
IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
OUT PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT **ExportDescriptor
)
{
UINTN Index;
EFI_PHYSICAL_ADDRESS CurrentImageAddress;
UINT16 PrmModuleExportDescriptorOrdinal;
CONST CHAR8 *CurrentExportName;
UINT16 *OrdinalTable;
UINT32 *ExportNamePointerTable;
UINT32 *ExportAddressTable;
PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *TempExportDescriptor;
DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __func__));
if ((ImageExportDirectory == NULL) ||
(PeCoffLoaderImageContext == NULL) ||
(PeCoffLoaderImageContext->ImageAddress == 0) ||
(ExportDescriptor == NULL))
{
return EFI_INVALID_PARAMETER;
}
*ExportDescriptor = NULL;
DEBUG ((
DEBUG_INFO,
" %a %a: %d exported names found in this image.\n",
_DBGMSGID_,
__func__,
ImageExportDirectory->NumberOfNames
));
//
// The export name pointer table and export ordinal table form two parallel arrays associated by index.
//
CurrentImageAddress = PeCoffLoaderImageContext->ImageAddress;
ExportAddressTable = (UINT32 *)((UINTN)CurrentImageAddress + ImageExportDirectory->AddressOfFunctions);
ExportNamePointerTable = (UINT32 *)((UINTN)CurrentImageAddress + ImageExportDirectory->AddressOfNames);
OrdinalTable = (UINT16 *)((UINTN)CurrentImageAddress + ImageExportDirectory->AddressOfNameOrdinals);
for (Index = 0; Index < ImageExportDirectory->NumberOfNames; Index++) {
CurrentExportName = (CONST CHAR8 *)((UINTN)CurrentImageAddress + ExportNamePointerTable[Index]);
DEBUG ((
DEBUG_INFO,
" %a %a: Export Name[0x%x] - %a.\n",
_DBGMSGID_,
__func__,
Index,
CurrentExportName
));
if (
AsciiStrnCmp (
PRM_STRING (PRM_MODULE_EXPORT_DESCRIPTOR_NAME),
CurrentExportName,
AsciiStrLen (PRM_STRING (PRM_MODULE_EXPORT_DESCRIPTOR_NAME))
) == 0)
{
PrmModuleExportDescriptorOrdinal = OrdinalTable[Index];
DEBUG ((
DEBUG_INFO,
" %a %a: PRM Module Export Descriptor found. Ordinal = %d.\n",
_DBGMSGID_,
__func__,
PrmModuleExportDescriptorOrdinal
));
if (PrmModuleExportDescriptorOrdinal >= ImageExportDirectory->NumberOfFunctions) {
DEBUG ((DEBUG_ERROR, "%a %a: The PRM Module Export Descriptor ordinal value is invalid.\n", _DBGMSGID_, __func__));
return EFI_NOT_FOUND;
}
TempExportDescriptor = (PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *)((UINTN)CurrentImageAddress + ExportAddressTable[PrmModuleExportDescriptorOrdinal]);
if (TempExportDescriptor->Header.Signature == PRM_MODULE_EXPORT_DESCRIPTOR_SIGNATURE) {
*ExportDescriptor = TempExportDescriptor;
DEBUG ((DEBUG_INFO, " %a %a: PRM Module Export Descriptor found at 0x%x.\n", _DBGMSGID_, __func__, (UINTN)ExportDescriptor));
} else {
DEBUG ((
DEBUG_INFO,
" %a %a: PRM Module Export Descriptor found at 0x%x but signature check failed.\n",
_DBGMSGID_,
__func__,
(UINTN)TempExportDescriptor
));
}
DEBUG ((DEBUG_INFO, " %a %a: Exiting export iteration since export descriptor found.\n", _DBGMSGID_, __func__));
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Gets a pointer to the export directory in a given PE/COFF image.
@param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
and already relocated to the memory base address. RVAs in the image given
should be valid.
@param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
PE/COFF image context for the Image given.
@param[out] ImageExportDirectory A pointer to a pointer to the export directory found in the Image given.
@retval EFI_SUCCESS The export directory was found successfully.
@retval EFI_INVALID_PARAMETER A required parameter is NULL.
@retval EFI_UNSUPPORTED The PE/COFF image given is not supported as a PRM Module.
@retval EFI_NOT_FOUND The image export directory could not be found for this image.
**/
EFI_STATUS
GetExportDirectoryInPeCoffImage (
IN VOID *Image,
IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
OUT EFI_IMAGE_EXPORT_DIRECTORY **ImageExportDirectory
)
{
UINT16 Magic;
UINT32 NumberOfRvaAndSizes;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
EFI_IMAGE_EXPORT_DIRECTORY *ExportDirectory;
if ((Image == NULL) || (PeCoffLoaderImageContext == NULL) || (ImageExportDirectory == NULL)) {
return EFI_INVALID_PARAMETER;
}
DirectoryEntry = NULL;
ExportDirectory = NULL;
//
// NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
// image instead of using the Magic field. Some systems might generate a PE32+
// image with PE32 magic.
//
switch (PeCoffLoaderImageContext->Machine) {
case EFI_IMAGE_MACHINE_IA32:
//
// Assume PE32 image with IA32 Machine field.
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
break;
case EFI_IMAGE_MACHINE_X64:
case EFI_IMAGE_MACHINE_AARCH64:
//
// Assume PE32+ image with X64 Machine field
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
break;
default:
//
// For unknown Machine field, use Magic in optional header
//
DEBUG ((
DEBUG_WARN,
"%a %a: The machine type for this image is not valid for a PRM module.\n",
_DBGMSGID_,
__func__
));
return EFI_UNSUPPORTED;
}
OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(
(UINTN)Image +
PeCoffLoaderImageContext->PeCoffHeaderOffset
);
//
// Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
//
if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __func__));
return EFI_UNSUPPORTED;
}
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use the PE32 offset to get the Export Directory Entry
//
NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32->OptionalHeader.NumberOfRvaAndSizes;
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(OptionalHeaderPtrUnion.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
} else if (OptionalHeaderPtrUnion.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//
// Use the PE32+ offset get the Export Directory Entry
//
NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
} else {
return EFI_UNSUPPORTED;
}
if ((NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_EXPORT) || (DirectoryEntry->VirtualAddress == 0)) {
//
// The export directory is not present
//
return EFI_NOT_FOUND;
} else if (((UINT32)(~0) - DirectoryEntry->VirtualAddress) < DirectoryEntry->Size) {
//
// The directory address overflows
//
DEBUG ((DEBUG_ERROR, "%a %a: The export directory entry in this image results in overflow.\n", _DBGMSGID_, __func__));
return EFI_UNSUPPORTED;
} else {
DEBUG ((DEBUG_INFO, "%a %a: Export Directory Entry found in the image at 0x%x.\n", _DBGMSGID_, __func__, (UINTN)OptionalHeaderPtrUnion.Pe32));
DEBUG ((DEBUG_INFO, " %a %a: Directory Entry Virtual Address = 0x%x.\n", _DBGMSGID_, __func__, DirectoryEntry->VirtualAddress));
ExportDirectory = (EFI_IMAGE_EXPORT_DIRECTORY *)((UINTN)Image + DirectoryEntry->VirtualAddress);
DEBUG ((
DEBUG_INFO,
" %a %a: Export Directory Table found successfully at 0x%x. Name address = 0x%x. Name = %a.\n",
_DBGMSGID_,
__func__,
(UINTN)ExportDirectory,
((UINTN)Image + ExportDirectory->Name),
(CHAR8 *)((UINTN)Image + ExportDirectory->Name)
));
}
*ImageExportDirectory = ExportDirectory;
return EFI_SUCCESS;
}
/**
Returns the image major and image minor version in a given PE/COFF image.
@param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
and already relocated to the memory base address. RVAs in the image given
should be valid.
@param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
PE/COFF image context for the Image given.
@param[out] ImageMajorVersion A pointer to a UINT16 buffer to hold the image major version.
@param[out] ImageMinorVersion A pointer to a UINT16 buffer to hold the image minor version.
@retval EFI_SUCCESS The image version was read successfully.
@retval EFI_INVALID_PARAMETER A required parameter is NULL.
@retval EFI_UNSUPPORTED The PE/COFF image given is not supported.
**/
EFI_STATUS
GetImageVersionInPeCoffImage (
IN VOID *Image,
IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
OUT UINT16 *ImageMajorVersion,
OUT UINT16 *ImageMinorVersion
)
{
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
UINT16 Magic;
DEBUG ((DEBUG_INFO, " %a %a - Entry.\n", _DBGMSGID_, __func__));
if ((Image == NULL) || (PeCoffLoaderImageContext == NULL) || (ImageMajorVersion == NULL) || (ImageMinorVersion == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
// image instead of using the Magic field. Some systems might generate a PE32+
// image with PE32 magic.
//
switch (PeCoffLoaderImageContext->Machine) {
case EFI_IMAGE_MACHINE_IA32:
//
// Assume PE32 image
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
break;
case EFI_IMAGE_MACHINE_X64:
case EFI_IMAGE_MACHINE_AARCH64:
//
// Assume PE32+ image
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
break;
default:
//
// For unknown Machine field, use Magic in optional header
//
DEBUG ((
DEBUG_WARN,
"%a %a: The machine type for this image is not valid for a PRM module.\n",
_DBGMSGID_,
__func__
));
return EFI_UNSUPPORTED;
}
OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(
(UINTN)Image +
PeCoffLoaderImageContext->PeCoffHeaderOffset
);
//
// Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
//
if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __func__));
return EFI_UNSUPPORTED;
}
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use the PE32 offset to get the Export Directory Entry
//
*ImageMajorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MajorImageVersion;
*ImageMinorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MinorImageVersion;
} else {
//
// Use the PE32+ offset to get the Export Directory Entry
//
*ImageMajorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MajorImageVersion;
*ImageMinorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MinorImageVersion;
}
DEBUG ((DEBUG_INFO, " %a %a - Image Major Version: 0x%02x.\n", _DBGMSGID_, __func__, *ImageMajorVersion));
DEBUG ((DEBUG_INFO, " %a %a - Image Minor Version: 0x%02x.\n", _DBGMSGID_, __func__, *ImageMinorVersion));
return EFI_SUCCESS;
}
/**
Gets the address of an entry in an image export table by ASCII name.
@param[in] ExportName A pointer to an ASCII name string of the entry name.
@param[in] ImageBaseAddress The base address of the PE/COFF image.
@param[in] ImageExportDirectory A pointer to the export directory in the image.
@param[out] ExportPhysicalAddress A pointer that will be updated with the address of the address of the
export entry if found.
@retval EFI_SUCCESS The export entry was found successfully.
@retval EFI_INVALID_PARAMETER A required pointer argument is NULL.
@retval EFI_NOT_FOUND An entry with the given ExportName was not found.
**/
EFI_STATUS
GetExportEntryAddress (
IN CONST CHAR8 *ExportName,
IN EFI_PHYSICAL_ADDRESS ImageBaseAddress,
IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
OUT EFI_PHYSICAL_ADDRESS *ExportPhysicalAddress
)
{
UINTN ExportNameIndex;
UINT16 CurrentExportOrdinal;
UINT32 *ExportAddressTable;
UINT32 *ExportNamePointerTable;
UINT16 *OrdinalTable;
CONST CHAR8 *ExportNameTablePointerName;
if ((ExportName == NULL) || (ImageBaseAddress == 0) || (ImageExportDirectory == NULL) || (ExportPhysicalAddress == NULL)) {
return EFI_INVALID_PARAMETER;
}
*ExportPhysicalAddress = 0;
ExportAddressTable = (UINT32 *)((UINTN)ImageBaseAddress + ImageExportDirectory->AddressOfFunctions);
ExportNamePointerTable = (UINT32 *)((UINTN)ImageBaseAddress + ImageExportDirectory->AddressOfNames);
OrdinalTable = (UINT16 *)((UINTN)ImageBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);
for (ExportNameIndex = 0; ExportNameIndex < ImageExportDirectory->NumberOfNames; ExportNameIndex++) {
ExportNameTablePointerName = (CONST CHAR8 *)((UINTN)ImageBaseAddress + ExportNamePointerTable[ExportNameIndex]);
if (AsciiStrnCmp (ExportName, ExportNameTablePointerName, PRM_HANDLER_NAME_MAXIMUM_LENGTH) == 0) {
CurrentExportOrdinal = OrdinalTable[ExportNameIndex];
ASSERT (CurrentExportOrdinal < ImageExportDirectory->NumberOfFunctions);
if (CurrentExportOrdinal >= ImageExportDirectory->NumberOfFunctions) {
DEBUG ((DEBUG_ERROR, " %a %a: The export ordinal value is invalid.\n", _DBGMSGID_, __func__));
break;
}
*ExportPhysicalAddress = (EFI_PHYSICAL_ADDRESS)((UINTN)ImageBaseAddress + ExportAddressTable[CurrentExportOrdinal]);
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}