mirror of https://github.com/acidanthera/audk.git
418 lines
17 KiB
C
418 lines
17 KiB
C
/** @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.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <IndustryStandard/PeImage.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PeCoffLib.h>
|
|
|
|
#include <PrmExportDescriptor.h>
|
|
#include <PrmModuleImageContext.h>
|
|
|
|
#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;
|
|
}
|