/** @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; }