/** @file
Functions to get info and load PE/COFF image.
Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
Portions Copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
Portions Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include "PeCoffLib.h"
typedef union {
VOID *Header;
EFI_IMAGE_OPTIONAL_HEADER32 *Optional32;
EFI_IMAGE_OPTIONAL_HEADER64 *Optional64;
} EFI_IMAGE_OPTIONAL_HEADER_POINTER;
STATIC
RETURN_STATUS
PeCoffLoaderGetPeHeader (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
OUT EFI_IMAGE_OPTIONAL_HEADER_UNION **PeHdr,
OUT EFI_TE_IMAGE_HEADER **TeHdr
);
STATIC
RETURN_STATUS
PeCoffLoaderCheckImageType (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
IN EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr,
IN EFI_TE_IMAGE_HEADER *TeHdr
);
STATIC
VOID *
PeCoffLoaderImageAddress (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
IN UINTN Address
);
RETURN_STATUS
PeCoffLoaderRelocateIa32Image (
IN UINT16 *Reloc,
IN OUT CHAR8 *Fixup,
IN OUT CHAR8 **FixupData,
IN UINT64 Adjust
);
RETURN_STATUS
PeCoffLoaderRelocateArmImage (
IN UINT16 **Reloc,
IN OUT CHAR8 *Fixup,
IN OUT CHAR8 **FixupData,
IN UINT64 Adjust
);
RETURN_STATUS
PeCoffLoaderRelocateRiscVImage (
IN UINT16 *Reloc,
IN OUT CHAR8 *Fixup,
IN OUT CHAR8 **FixupData,
IN UINT64 Adjust
);
RETURN_STATUS
PeCoffLoaderRelocateLoongArch64Image (
IN UINT16 *Reloc,
IN OUT CHAR8 *Fixup,
IN OUT CHAR8 **FixupData,
IN UINT64 Adjust
);
/**
Retrieves the PE or TE Header from a PE/COFF or TE image
@param ImageContext The context of the image being loaded
@param PeHdr The buffer in which to return the PE header
@param TeHdr The buffer in which to return the TE header
@return RETURN_SUCCESS if the PE or TE Header is read,
Otherwise, the error status from reading the PE/COFF or TE image using the ImageRead function.
**/
STATIC
RETURN_STATUS
PeCoffLoaderGetPeHeader (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
OUT EFI_IMAGE_OPTIONAL_HEADER_UNION **PeHdr,
OUT EFI_TE_IMAGE_HEADER **TeHdr
)
{
RETURN_STATUS Status;
EFI_IMAGE_DOS_HEADER DosHdr;
UINTN Size;
ImageContext->IsTeImage = FALSE;
//
// Read the DOS image headers
//
Size = sizeof (EFI_IMAGE_DOS_HEADER);
Status = ImageContext->ImageRead (
ImageContext->Handle,
0,
&Size,
&DosHdr
);
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
ImageContext->PeCoffHeaderOffset = 0;
if (DosHdr.e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present, so read the PE header after the DOS image header
//
ImageContext->PeCoffHeaderOffset = DosHdr.e_lfanew;
}
//
// Get the PE/COFF Header pointer
//
*PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINTN)ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
if ((*PeHdr)->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
//
// Check the PE/COFF Header Signature. If not, then try to get a TE header
//
*TeHdr = (EFI_TE_IMAGE_HEADER *)*PeHdr;
if ((*TeHdr)->Signature != EFI_TE_IMAGE_HEADER_SIGNATURE) {
return RETURN_UNSUPPORTED;
}
ImageContext->IsTeImage = TRUE;
}
return RETURN_SUCCESS;
}
/**
Checks the PE or TE header of a PE/COFF or TE image to determine if it supported
@param ImageContext The context of the image being loaded
@param PeHdr The buffer in which to return the PE header
@param TeHdr The buffer in which to return the TE header
@retval RETURN_SUCCESS if the PE/COFF or TE image is supported
@retval RETURN_UNSUPPORTED of the PE/COFF or TE image is not supported.
**/
STATIC
RETURN_STATUS
PeCoffLoaderCheckImageType (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
IN EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr,
IN EFI_TE_IMAGE_HEADER *TeHdr
)
{
//
// See if the machine type is supported.
// We support a native machine type (IA-32/Itanium-based)
//
if (ImageContext->IsTeImage == FALSE) {
ImageContext->Machine = PeHdr->Pe32.FileHeader.Machine;
} else {
ImageContext->Machine = TeHdr->Machine;
}
if (ImageContext->Machine != IMAGE_FILE_MACHINE_I386 && \
ImageContext->Machine != IMAGE_FILE_MACHINE_X64 && \
ImageContext->Machine != IMAGE_FILE_MACHINE_ARMTHUMB_MIXED && \
ImageContext->Machine != IMAGE_FILE_MACHINE_EBC && \
ImageContext->Machine != IMAGE_FILE_MACHINE_ARM64 && \
ImageContext->Machine != IMAGE_FILE_MACHINE_RISCV64 && \
ImageContext->Machine != IMAGE_FILE_MACHINE_LOONGARCH64) {
//
// unsupported PeImage machine type
//
return RETURN_UNSUPPORTED;
}
//
// See if the image type is supported. We support EFI Applications,
// EFI Boot Service Drivers, EFI Runtime Drivers and EFI SAL Drivers.
//
if (ImageContext->IsTeImage == FALSE) {
ImageContext->ImageType = PeHdr->Pe32.OptionalHeader.Subsystem;
} else {
ImageContext->ImageType = (UINT16) (TeHdr->Subsystem);
}
if (ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && \
ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER && \
ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER && \
ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER) {
//
// unsupported PeImage subsystem type
//
return RETURN_UNSUPPORTED;
}
return RETURN_SUCCESS;
}
/**
Retrieves information on a PE/COFF image
@param This Calling context
@param ImageContext The context of the image being loaded
@retval RETURN_SUCCESS The information on the PE/COFF image was collected.
@retval RETURN_INVALID_PARAMETER ImageContext is NULL.
@retval RETURN_UNSUPPORTED The PE/COFF image is not supported.
@retval Otherwise The error status from reading the PE/COFF image using the
ImageContext->ImageRead() function
**/
RETURN_STATUS
EFIAPI
PeCoffLoaderGetImageInfo (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
)
{
RETURN_STATUS Status;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr;
EFI_TE_IMAGE_HEADER *TeHdr;
EFI_IMAGE_DATA_DIRECTORY *DebugDirectoryEntry;
UINTN Size;
UINTN Index;
UINTN DebugDirectoryEntryRva;
UINTN DebugDirectoryEntryFileOffset;
UINTN SectionHeaderOffset;
EFI_IMAGE_SECTION_HEADER SectionHeader;
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY DebugEntry;
EFI_IMAGE_OPTIONAL_HEADER_POINTER OptionHeader;
PeHdr = NULL;
TeHdr = NULL;
DebugDirectoryEntry = NULL;
DebugDirectoryEntryRva = 0;
if (NULL == ImageContext) {
return RETURN_INVALID_PARAMETER;
}
//
// Assume success
//
ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
Status = PeCoffLoaderGetPeHeader (ImageContext, &PeHdr, &TeHdr);
if (RETURN_ERROR (Status)) {
return Status;
}
//
// Verify machine type
//
Status = PeCoffLoaderCheckImageType (ImageContext, PeHdr, TeHdr);
if (RETURN_ERROR (Status)) {
return Status;
}
OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
//
// Retrieve the base address of the image
//
if (!(ImageContext->IsTeImage)) {
if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
ImageContext->ImageAddress = (PHYSICAL_ADDRESS) OptionHeader.Optional32->ImageBase;
} else {
ImageContext->ImageAddress = (PHYSICAL_ADDRESS) OptionHeader.Optional64->ImageBase;
}
} else {
ImageContext->ImageAddress = (PHYSICAL_ADDRESS) (TeHdr->ImageBase + TeHdr->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER));
}
//
// Initialize the alternate destination address to 0 indicating that it
// should not be used.
//
ImageContext->DestinationAddress = 0;
//
// Initialize the codeview pointer.
//
ImageContext->CodeView = NULL;
ImageContext->PdbPointer = NULL;
//
// Three cases with regards to relocations:
// - Image has base relocs, RELOCS_STRIPPED==0 => image is relocatable
// - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable
// - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but
// has no base relocs to apply
// Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.
//
// Look at the file header to determine if relocations have been stripped, and
// save this info in the image context for later use.
//
if ((!(ImageContext->IsTeImage)) && ((PeHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) {
ImageContext->RelocationsStripped = TRUE;
} else if ((ImageContext->IsTeImage) && (TeHdr->DataDirectory[0].Size == 0) && (TeHdr->DataDirectory[0].VirtualAddress == 0)) {
ImageContext->RelocationsStripped = TRUE;
} else {
ImageContext->RelocationsStripped = FALSE;
}
if (!(ImageContext->IsTeImage)) {
if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
ImageContext->ImageSize = (UINT64) OptionHeader.Optional32->SizeOfImage;
ImageContext->SectionAlignment = OptionHeader.Optional32->SectionAlignment;
ImageContext->SizeOfHeaders = OptionHeader.Optional32->SizeOfHeaders;
//
// Modify ImageSize to contain .PDB file name if required and initialize
// PdbRVA field...
//
if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
}
} else {
ImageContext->ImageSize = (UINT64) OptionHeader.Optional64->SizeOfImage;
ImageContext->SectionAlignment = OptionHeader.Optional64->SectionAlignment;
ImageContext->SizeOfHeaders = OptionHeader.Optional64->SizeOfHeaders;
//
// Modify ImageSize to contain .PDB file name if required and initialize
// PdbRVA field...
//
if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
}
}
if (DebugDirectoryEntryRva != 0) {
//
// Determine the file offset of the debug directory... This means we walk
// the sections to find which section contains the RVA of the debug
// directory
//
DebugDirectoryEntryFileOffset = 0;
SectionHeaderOffset = (UINTN)(
ImageContext->PeCoffHeaderOffset +
sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER) +
PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
);
for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++) {
//
// Read section header from file
//
Size = sizeof (EFI_IMAGE_SECTION_HEADER);
Status = ImageContext->ImageRead (
ImageContext->Handle,
SectionHeaderOffset,
&Size,
&SectionHeader
);
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
DebugDirectoryEntryFileOffset =
DebugDirectoryEntryRva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData;
break;
}
SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
}
if (DebugDirectoryEntryFileOffset != 0) {
for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) {
//
// Read next debug directory entry
//
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
Status = ImageContext->ImageRead (
ImageContext->Handle,
DebugDirectoryEntryFileOffset + Index,
&Size,
&DebugEntry
);
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index);
if (DebugEntry.RVA == 0 && DebugEntry.FileOffset != 0) {
ImageContext->ImageSize += DebugEntry.SizeOfData;
}
return RETURN_SUCCESS;
}
}
}
}
} else {
ImageContext->ImageSize = 0;
ImageContext->SectionAlignment = 4096;
ImageContext->SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN) TeHdr->BaseOfCode - (UINTN) TeHdr->StrippedSize;
DebugDirectoryEntry = &TeHdr->DataDirectory[1];
DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
SectionHeaderOffset = (UINTN) (sizeof (EFI_TE_IMAGE_HEADER));
DebugDirectoryEntryFileOffset = 0;
for (Index = 0; Index < TeHdr->NumberOfSections;) {
//
// Read section header from file
//
Size = sizeof (EFI_IMAGE_SECTION_HEADER);
Status = ImageContext->ImageRead (
ImageContext->Handle,
SectionHeaderOffset,
&Size,
&SectionHeader
);
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
DebugDirectoryEntryFileOffset = DebugDirectoryEntryRva -
SectionHeader.VirtualAddress +
SectionHeader.PointerToRawData +
sizeof (EFI_TE_IMAGE_HEADER) -
TeHdr->StrippedSize;
//
// File offset of the debug directory was found, if this is not the last
// section, then skip to the last section for calculating the image size.
//
if (Index < (UINTN) TeHdr->NumberOfSections - 1) {
SectionHeaderOffset += (TeHdr->NumberOfSections - 1 - Index) * sizeof (EFI_IMAGE_SECTION_HEADER);
Index = TeHdr->NumberOfSections - 1;
continue;
}
}
//
// In Te image header there is not a field to describe the ImageSize.
// Actually, the ImageSize equals the RVA plus the VirtualSize of
// the last section mapped into memory (Must be rounded up to
// a multiple of Section Alignment). Per the PE/COFF specification, the
// section headers in the Section Table must appear in order of the RVA
// values for the corresponding sections. So the ImageSize can be determined
// by the RVA and the VirtualSize of the last section header in the
// Section Table.
//
if ((++Index) == (UINTN) TeHdr->NumberOfSections) {
ImageContext->ImageSize = (SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize +
ImageContext->SectionAlignment - 1) & ~(ImageContext->SectionAlignment - 1);
}
SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
}
if (DebugDirectoryEntryFileOffset != 0) {
for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) {
//
// Read next debug directory entry
//
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
Status = ImageContext->ImageRead (
ImageContext->Handle,
DebugDirectoryEntryFileOffset,
&Size,
&DebugEntry
);
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index);
return RETURN_SUCCESS;
}
}
}
}
return RETURN_SUCCESS;
}
/**
Converts an image address to the loaded address
@param ImageContext The context of the image being loaded
@param Address The address to be converted to the loaded address
@return NULL if the address can not be converted, otherwise, the converted address
--*/
STATIC
VOID *
PeCoffLoaderImageAddress (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
IN UINTN Address
)
{
if (Address >= ImageContext->ImageSize) {
ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
return NULL;
}
return (UINT8 *) ((UINTN) ImageContext->ImageAddress + Address);
}
/**
Relocates a PE/COFF image in memory
@param This Calling context
@param ImageContext Contains information on the loaded image to relocate
@retval RETURN_SUCCESS if the PE/COFF image was relocated
@retval RETURN_LOAD_ERROR if the image is not a valid PE/COFF image
@retval RETURN_UNSUPPORTED not support
**/
RETURN_STATUS
EFIAPI
PeCoffLoaderRelocateImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
)
{
RETURN_STATUS Status;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr;
EFI_TE_IMAGE_HEADER *TeHdr;
EFI_IMAGE_DATA_DIRECTORY *RelocDir;
UINT64 Adjust;
EFI_IMAGE_BASE_RELOCATION *RelocBase;
EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd;
UINT16 *Reloc;
UINT16 *RelocEnd;
CHAR8 *Fixup;
CHAR8 *FixupBase;
UINT16 *F16;
UINT32 *F32;
UINT64 *F64;
CHAR8 *FixupData;
PHYSICAL_ADDRESS BaseAddress;
UINT16 MachineType;
EFI_IMAGE_OPTIONAL_HEADER_POINTER OptionHeader;
PeHdr = NULL;
TeHdr = NULL;
//
// Assume success
//
ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
//
// If there are no relocation entries, then we are done
//
if (ImageContext->RelocationsStripped) {
return RETURN_SUCCESS;
}
//
// Use DestinationAddress field of ImageContext as the relocation address even if it is 0.
//
BaseAddress = ImageContext->DestinationAddress;
if (!(ImageContext->IsTeImage)) {
PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINTN)ImageContext->ImageAddress +
ImageContext->PeCoffHeaderOffset);
OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
Adjust = (UINT64) BaseAddress - OptionHeader.Optional32->ImageBase;
OptionHeader.Optional32->ImageBase = (UINT32) BaseAddress;
MachineType = ImageContext->Machine;
//
// Find the relocation block
//
// Per the PE/COFF spec, you can't assume that a given data directory
// is present in the image. You have to check the NumberOfRvaAndSizes in
// the optional header to verify a desired directory entry is there.
//
if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
RelocDir = &OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
if ((RelocDir != NULL) && (RelocDir->Size > 0)) {
RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress);
RelocBaseEnd = PeCoffLoaderImageAddress (
ImageContext,
RelocDir->VirtualAddress + RelocDir->Size - 1
);
if (RelocBase == NULL || RelocBaseEnd == NULL || RelocBaseEnd < RelocBase) {
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return RETURN_LOAD_ERROR;
}
} else {
//
// Set base and end to bypass processing below.
//
RelocBase = RelocBaseEnd = 0;
}
} else {
//
// Set base and end to bypass processing below.
//
RelocBase = RelocBaseEnd = 0;
}
} else {
Adjust = (UINT64) BaseAddress - OptionHeader.Optional64->ImageBase;
OptionHeader.Optional64->ImageBase = BaseAddress;
MachineType = ImageContext->Machine;
//
// Find the relocation block
//
// Per the PE/COFF spec, you can't assume that a given data directory
// is present in the image. You have to check the NumberOfRvaAndSizes in
// the optional header to verify a desired directory entry is there.
//
if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
RelocDir = &OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
if ((RelocDir != NULL) && (RelocDir->Size > 0)) {
RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress);
RelocBaseEnd = PeCoffLoaderImageAddress (
ImageContext,
RelocDir->VirtualAddress + RelocDir->Size - 1
);
if (RelocBase == NULL || RelocBaseEnd == NULL || RelocBaseEnd < RelocBase) {
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return RETURN_LOAD_ERROR;
}
} else {
//
// Set base and end to bypass processing below.
//
RelocBase = RelocBaseEnd = 0;
}
} else {
//
// Set base and end to bypass processing below.
//
RelocBase = RelocBaseEnd = 0;
}
}
} else {
TeHdr = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
Adjust = (UINT64) (BaseAddress - TeHdr->ImageBase);
TeHdr->ImageBase = (UINT64) (BaseAddress);
MachineType = TeHdr->Machine;
//
// Find the relocation block
//
RelocDir = &TeHdr->DataDirectory[0];
RelocBase = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(
ImageContext->ImageAddress +
RelocDir->VirtualAddress +
sizeof(EFI_TE_IMAGE_HEADER) -
TeHdr->StrippedSize
);
RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *) ((UINTN) RelocBase + (UINTN) RelocDir->Size - 1);
}
//
// Run the relocation information and apply the fixups
//
FixupData = ImageContext->FixupData;
while (RelocBase < RelocBaseEnd) {
Reloc = (UINT16 *) ((CHAR8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
RelocEnd = (UINT16 *) ((CHAR8 *) RelocBase + RelocBase->SizeOfBlock);
if (!(ImageContext->IsTeImage)) {
FixupBase = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress);
if (FixupBase == NULL) {
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return RETURN_LOAD_ERROR;
}
} else {
FixupBase = (CHAR8 *)(UINTN)(ImageContext->ImageAddress +
RelocBase->VirtualAddress +
sizeof(EFI_TE_IMAGE_HEADER) -
TeHdr->StrippedSize
);
}
if ((CHAR8 *) RelocEnd < (CHAR8 *) ((UINTN) ImageContext->ImageAddress) ||
(CHAR8 *) RelocEnd > (CHAR8 *)((UINTN)ImageContext->ImageAddress +
(UINTN)ImageContext->ImageSize)) {
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return RETURN_LOAD_ERROR;
}
//
// Run this relocation record
//
while (Reloc < RelocEnd) {
Fixup = FixupBase + (*Reloc & 0xFFF);
switch ((*Reloc) >> 12) {
case EFI_IMAGE_REL_BASED_ABSOLUTE:
break;
case EFI_IMAGE_REL_BASED_HIGH:
F16 = (UINT16 *) Fixup;
*F16 = (UINT16) (*F16 + ((UINT16) ((UINT32) Adjust >> 16)));
if (FixupData != NULL) {
*(UINT16 *) FixupData = *F16;
FixupData = FixupData + sizeof (UINT16);
}
break;
case EFI_IMAGE_REL_BASED_LOW:
F16 = (UINT16 *) Fixup;
*F16 = (UINT16) (*F16 + (UINT16) Adjust);
if (FixupData != NULL) {
*(UINT16 *) FixupData = *F16;
FixupData = FixupData + sizeof (UINT16);
}
break;
case EFI_IMAGE_REL_BASED_HIGHLOW:
F32 = (UINT32 *) Fixup;
*F32 = *F32 + (UINT32) Adjust;
if (FixupData != NULL) {
FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32));
*(UINT32 *) FixupData = *F32;
FixupData = FixupData + sizeof (UINT32);
}
break;
case EFI_IMAGE_REL_BASED_DIR64:
F64 = (UINT64 *) Fixup;
*F64 = *F64 + (UINT64) Adjust;
if (FixupData != NULL) {
FixupData = ALIGN_POINTER (FixupData, sizeof (UINT64));
*(UINT64 *) FixupData = *F64;
FixupData = FixupData + sizeof (UINT64);
}
break;
case EFI_IMAGE_REL_BASED_HIGHADJ:
//
// Return the same EFI_UNSUPPORTED return code as
// PeCoffLoaderRelocateImageEx() returns if it does not recognize
// the relocation type.
//
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return RETURN_UNSUPPORTED;
default:
switch (MachineType) {
case IMAGE_FILE_MACHINE_I386:
Status = PeCoffLoaderRelocateIa32Image (Reloc, Fixup, &FixupData, Adjust);
break;
case IMAGE_FILE_MACHINE_ARMTHUMB_MIXED:
Status = PeCoffLoaderRelocateArmImage (&Reloc, Fixup, &FixupData, Adjust);
break;
case IMAGE_FILE_MACHINE_RISCV64:
Status = PeCoffLoaderRelocateRiscVImage (Reloc, Fixup, &FixupData, Adjust);
break;
case IMAGE_FILE_MACHINE_LOONGARCH64:
Status = PeCoffLoaderRelocateLoongArch64Image (Reloc, Fixup, &FixupData, Adjust);
break;
default:
Status = RETURN_UNSUPPORTED;
break;
}
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
return Status;
}
}
//
// Next relocation record
//
Reloc += 1;
}
//
// Next reloc block
//
RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
}
return RETURN_SUCCESS;
}
/**
Loads a PE/COFF image into memory
@param This Calling context
@param ImageContext Contains information on image to load into memory
@retval RETURN_SUCCESS if the PE/COFF image was loaded
@retval RETURN_BUFFER_TOO_SMALL if the caller did not provide a large enough buffer
@retval RETURN_LOAD_ERROR if the image is a runtime driver with no relocations
@retval RETURN_INVALID_PARAMETER if the image address is invalid
**/
RETURN_STATUS
EFIAPI
PeCoffLoaderLoadImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
)
{
RETURN_STATUS Status;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr;
EFI_TE_IMAGE_HEADER *TeHdr;
PE_COFF_LOADER_IMAGE_CONTEXT CheckContext;
EFI_IMAGE_SECTION_HEADER *FirstSection;
EFI_IMAGE_SECTION_HEADER *Section;
UINTN NumberOfSections;
UINTN Index;
CHAR8 *Base;
CHAR8 *End;
CHAR8 *MaxEnd;
EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
UINTN Size;
UINT32 TempDebugEntryRva;
EFI_IMAGE_OPTIONAL_HEADER_POINTER OptionHeader;
PeHdr = NULL;
TeHdr = NULL;
OptionHeader.Header = NULL;
//
// Assume success
//
ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
//
// Copy the provided context info into our local version, get what we
// can from the original image, and then use that to make sure everything
// is legit.
//
CopyMem (&CheckContext, ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
Status = PeCoffLoaderGetImageInfo (&CheckContext);
if (RETURN_ERROR (Status)) {
return Status;
}
//
// Make sure there is enough allocated space for the image being loaded
//
if (ImageContext->ImageSize < CheckContext.ImageSize) {
ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_SIZE;
return RETURN_BUFFER_TOO_SMALL;
}
//
// If there's no relocations, then make sure it's not a runtime driver,
// and that it's being loaded at the linked address.
//
if (CheckContext.RelocationsStripped) {
//
// If the image does not contain relocations and it is a runtime driver
// then return an error.
//
if (CheckContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) {
ImageContext->ImageError = IMAGE_ERROR_INVALID_SUBSYSTEM;
return RETURN_LOAD_ERROR;
}
//
// If the image does not contain relocations, and the requested load address
// is not the linked address, then return an error.
//
if (CheckContext.ImageAddress != ImageContext->ImageAddress) {
ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
return RETURN_INVALID_PARAMETER;
}
}
//
// Make sure the allocated space has the proper section alignment
//
if (!(ImageContext->IsTeImage)) {
if ((ImageContext->ImageAddress & (CheckContext.SectionAlignment - 1)) != 0) {
ImageContext->ImageError = IMAGE_ERROR_INVALID_SECTION_ALIGNMENT;
return RETURN_INVALID_PARAMETER;
}
}
//
// Read the entire PE/COFF or TE header into memory
//
if (!(ImageContext->IsTeImage)) {
Status = ImageContext->ImageRead (
ImageContext->Handle,
0,
&ImageContext->SizeOfHeaders,
(VOID *) (UINTN) ImageContext->ImageAddress
);
PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)
((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset);
OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
(UINTN)ImageContext->ImageAddress +
ImageContext->PeCoffHeaderOffset +
sizeof(UINT32) +
sizeof(EFI_IMAGE_FILE_HEADER) +
PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
);
NumberOfSections = (UINTN) (PeHdr->Pe32.FileHeader.NumberOfSections);
} else {
Status = ImageContext->ImageRead (
ImageContext->Handle,
0,
&ImageContext->SizeOfHeaders,
(VOID *) (UINTN) ImageContext->ImageAddress
);
TeHdr = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
(UINTN)ImageContext->ImageAddress +
sizeof(EFI_TE_IMAGE_HEADER)
);
NumberOfSections = (UINTN) (TeHdr->NumberOfSections);
}
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return RETURN_LOAD_ERROR;
}
//
// Load each section of the image
//
Section = FirstSection;
for (Index = 0, MaxEnd = NULL; Index < NumberOfSections; Index++) {
//
// Compute sections address
//
Base = PeCoffLoaderImageAddress (ImageContext, Section->VirtualAddress);
End = PeCoffLoaderImageAddress (
ImageContext,
Section->VirtualAddress + Section->Misc.VirtualSize - 1
);
//
// If the base start or end address resolved to 0, then fail.
//
if ((Base == NULL) || (End == NULL)) {
ImageContext->ImageError = IMAGE_ERROR_SECTION_NOT_LOADED;
return RETURN_LOAD_ERROR;
}
if (ImageContext->IsTeImage) {
Base = (CHAR8 *) ((UINTN) Base + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
End = (CHAR8 *) ((UINTN) End + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
}
if (End > MaxEnd) {
MaxEnd = End;
}
//
// Read the section
//
Size = (UINTN) Section->Misc.VirtualSize;
if ((Size == 0) || (Size > Section->SizeOfRawData)) {
Size = (UINTN) Section->SizeOfRawData;
}
if (Section->SizeOfRawData) {
if (!(ImageContext->IsTeImage)) {
Status = ImageContext->ImageRead (
ImageContext->Handle,
Section->PointerToRawData,
&Size,
Base
);
} else {
Status = ImageContext->ImageRead (
ImageContext->Handle,
Section->PointerToRawData + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize,
&Size,
Base
);
}
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return Status;
}
}
//
// If raw size is less then virt size, zero fill the remaining
//
if (Size < Section->Misc.VirtualSize) {
ZeroMem (Base + Size, Section->Misc.VirtualSize - Size);
}
//
// Next Section
//
Section += 1;
}
//
// Get image's entry point
//
if (!(ImageContext->IsTeImage)) {
ImageContext->EntryPoint = (PHYSICAL_ADDRESS) (UINTN) PeCoffLoaderImageAddress (
ImageContext,
PeHdr->Pe32.OptionalHeader.AddressOfEntryPoint
);
} else {
ImageContext->EntryPoint = (UINTN)ImageContext->ImageAddress +
(UINTN)TeHdr->AddressOfEntryPoint +
(UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
(UINTN) TeHdr->StrippedSize;
}
//
// Determine the size of the fixup data
//
// Per the PE/COFF spec, you can't assume that a given data directory
// is present in the image. You have to check the NumberOfRvaAndSizes in
// the optional header to verify a desired directory entry is there.
//
if (!(ImageContext->IsTeImage)) {
if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)
&OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
} else {
ImageContext->FixupDataSize = 0;
}
} else {
if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)
&OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
} else {
ImageContext->FixupDataSize = 0;
}
}
} else {
DirectoryEntry = &TeHdr->DataDirectory[0];
ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
}
//
// Consumer must allocate a buffer for the relocation fixup log.
// Only used for runtime drivers.
//
ImageContext->FixupData = NULL;
//
// Load the Codeview info if present
//
if (ImageContext->DebugDirectoryEntryRva != 0) {
if (!(ImageContext->IsTeImage)) {
DebugEntry = PeCoffLoaderImageAddress (
ImageContext,
ImageContext->DebugDirectoryEntryRva
);
} else {
DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)(UINTN)(
ImageContext->ImageAddress +
ImageContext->DebugDirectoryEntryRva +
sizeof(EFI_TE_IMAGE_HEADER) -
TeHdr->StrippedSize
);
}
if (DebugEntry != NULL) {
TempDebugEntryRva = DebugEntry->RVA;
if (DebugEntry->RVA == 0 && DebugEntry->FileOffset != 0) {
Section--;
if ((UINTN) Section->SizeOfRawData < Section->Misc.VirtualSize) {
TempDebugEntryRva = Section->VirtualAddress + Section->Misc.VirtualSize;
} else {
TempDebugEntryRva = Section->VirtualAddress + Section->SizeOfRawData;
}
}
if (TempDebugEntryRva != 0) {
if (!(ImageContext->IsTeImage)) {
ImageContext->CodeView = PeCoffLoaderImageAddress (ImageContext, TempDebugEntryRva);
} else {
ImageContext->CodeView = (VOID *)(
(UINTN)ImageContext->ImageAddress +
(UINTN)TempDebugEntryRva +
(UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
(UINTN) TeHdr->StrippedSize
);
}
if (ImageContext->CodeView == NULL) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return RETURN_LOAD_ERROR;
}
if (DebugEntry->RVA == 0) {
Size = DebugEntry->SizeOfData;
if (!(ImageContext->IsTeImage)) {
Status = ImageContext->ImageRead (
ImageContext->Handle,
DebugEntry->FileOffset,
&Size,
ImageContext->CodeView
);
} else {
Status = ImageContext->ImageRead (
ImageContext->Handle,
DebugEntry->FileOffset + sizeof (EFI_TE_IMAGE_HEADER) - TeHdr->StrippedSize,
&Size,
ImageContext->CodeView
);
//
// Should we apply fix up to this field according to the size difference between PE and TE?
// Because now we maintain TE header fields unfixed, this field will also remain as they are
// in original PE image.
//
}
if (RETURN_ERROR (Status)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
return RETURN_LOAD_ERROR;
}
DebugEntry->RVA = TempDebugEntryRva;
}
switch (*(UINT32 *) ImageContext->CodeView) {
case CODEVIEW_SIGNATURE_NB10:
ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY);
break;
case CODEVIEW_SIGNATURE_RSDS:
ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY);
break;
case CODEVIEW_SIGNATURE_MTOC:
ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY);
default:
break;
}
}
}
}
return Status;
}
/**
Returns a pointer to the PDB file name for a raw PE/COFF image that is not
loaded into system memory with the PE/COFF Loader Library functions.
Returns the PDB file name for the PE/COFF image specified by Pe32Data. If
the PE/COFF image specified by Pe32Data is not a valid, then NULL is
returned. If the PE/COFF image specified by Pe32Data does not contain a
debug directory entry, then NULL is returned. If the debug directory entry
in the PE/COFF image specified by Pe32Data does not contain a PDB file name,
then NULL is returned.
If Pe32Data is NULL, then return NULL.
@param Pe32Data Pointer to the PE/COFF image that is loaded in system
memory.
@return The PDB file name for the PE/COFF image specified by Pe32Data or NULL
if it cannot be retrieved.
**/
VOID *
EFIAPI
PeCoffLoaderGetPdbPointer (
IN VOID *Pe32Data
)
{
EFI_IMAGE_DOS_HEADER *DosHdr;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
UINTN DirCount;
VOID *CodeViewEntryPointer;
INTN TEImageAdjust;
UINT32 NumberOfRvaAndSizes;
UINT16 Magic;
EFI_IMAGE_SECTION_HEADER *SectionHeader;
UINT32 Index, Index1;
if (Pe32Data == NULL) {
return NULL;
}
TEImageAdjust = 0;
DirectoryEntry = NULL;
DebugEntry = NULL;
NumberOfRvaAndSizes = 0;
Index = 0;
Index1 = 0;
SectionHeader = NULL;
DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
if (EFI_IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
//
// DOS image header is present, so read the PE header after the DOS image header.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
} else {
//
// DOS image header is not present, so PE header is at the image base.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
}
if (EFI_TE_IMAGE_HEADER_SIGNATURE == Hdr.Te->Signature) {
if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {
DirectoryEntry = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];
TEImageAdjust = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;
//
// Get the DebugEntry offset in the raw data image.
//
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (Hdr.Te + 1);
Index = Hdr.Te->NumberOfSections;
for (Index1 = 0; Index1 < Index; Index1 ++) {
if ((DirectoryEntry->VirtualAddress >= SectionHeader[Index1].VirtualAddress) &&
(DirectoryEntry->VirtualAddress < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +
DirectoryEntry->VirtualAddress -
SectionHeader [Index1].VirtualAddress +
SectionHeader [Index1].PointerToRawData +
TEImageAdjust);
break;
}
}
}
} else if (EFI_IMAGE_NT_SIGNATURE == Hdr.Pe32->Signature) {
//
// NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.
// It is due to backward-compatibility, for some system might
// generate PE32+ image with PE32 Magic.
//
switch (Hdr.Pe32->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386:
case IMAGE_FILE_MACHINE_ARMTHUMB_MIXED:
//
// Assume PE32 image with IA32 Machine field.
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
break;
case IMAGE_FILE_MACHINE_X64:
//
// 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
//
Magic = Hdr.Pe32->OptionalHeader.Magic;
}
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
(UINT8 *) Hdr.Pe32 +
sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER) +
Hdr.Pe32->FileHeader.SizeOfOptionalHeader
);
Index = Hdr.Pe32->FileHeader.NumberOfSections;
if (EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC == Magic) {
//
// Use PE32 offset get Debug Directory Entry
//
NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
} else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//
// Use PE32+ offset get Debug Directory Entry
//
NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
}
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG || DirectoryEntry->VirtualAddress == 0) {
DirectoryEntry = NULL;
DebugEntry = NULL;
} else {
//
// Get the DebugEntry offset in the raw data image.
//
for (Index1 = 0; Index1 < Index; Index1 ++) {
if ((DirectoryEntry->VirtualAddress >= SectionHeader[Index1].VirtualAddress) &&
(DirectoryEntry->VirtualAddress < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (
(UINTN) Pe32Data +
DirectoryEntry->VirtualAddress -
SectionHeader[Index1].VirtualAddress +
SectionHeader[Index1].PointerToRawData);
break;
}
}
}
} else {
return NULL;
}
if (NULL == DebugEntry || NULL == DirectoryEntry) {
return NULL;
}
//
// Scan the directory to find the debug entry.
//
for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {
if (EFI_IMAGE_DEBUG_TYPE_CODEVIEW == DebugEntry->Type) {
if (DebugEntry->SizeOfData > 0) {
//
// Get the DebugEntry offset in the raw data image.
//
CodeViewEntryPointer = NULL;
for (Index1 = 0; Index1 < Index; Index1 ++) {
if ((DebugEntry->RVA >= SectionHeader[Index1].VirtualAddress) &&
(DebugEntry->RVA < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
CodeViewEntryPointer = (VOID *) (
((UINTN)Pe32Data) +
(UINTN) DebugEntry->RVA -
SectionHeader[Index1].VirtualAddress +
SectionHeader[Index1].PointerToRawData +
(UINTN)TEImageAdjust);
break;
}
}
if (Index1 >= Index) {
//
// Can't find CodeViewEntryPointer in raw PE/COFF image.
//
continue;
}
switch (* (UINT32 *) CodeViewEntryPointer) {
case CODEVIEW_SIGNATURE_NB10:
return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
case CODEVIEW_SIGNATURE_RSDS:
return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
case CODEVIEW_SIGNATURE_MTOC:
return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
default:
break;
}
}
}
}
return NULL;
}
RETURN_STATUS
EFIAPI
PeCoffLoaderGetEntryPoint (
IN VOID *Pe32Data,
OUT VOID **EntryPoint,
OUT VOID **BaseOfImage
)
{
EFI_IMAGE_DOS_HEADER *DosHdr;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present, so read the PE header after the DOS image header.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
} else {
//
// DOS image header is not present, so PE header is at the image base.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
}
//
// Calculate the entry point relative to the start of the image.
// AddressOfEntryPoint is common for PE32 & PE32+
//
if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
*BaseOfImage = (VOID *)(UINTN)(Hdr.Te->ImageBase + Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER));
*EntryPoint = (VOID *)((UINTN)*BaseOfImage + (Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);
return RETURN_SUCCESS;
} else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
*EntryPoint = (VOID *)(UINTN)Hdr.Pe32->OptionalHeader.AddressOfEntryPoint;
if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
*BaseOfImage = (VOID *)(UINTN)Hdr.Pe32->OptionalHeader.ImageBase;
} else {
*BaseOfImage = (VOID *)(UINTN)Hdr.Pe32Plus->OptionalHeader.ImageBase;
}
*EntryPoint = (VOID *)(UINTN)((UINTN)*EntryPoint + (UINTN)*BaseOfImage);
return RETURN_SUCCESS;
}
return RETURN_UNSUPPORTED;
}