/** @file Provides the services to get the entry point to a PE/COFF image that has either been loaded into memory or is executing at it's linked address. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include <Base.h> #include <Library/PeCoffGetEntryPointLib.h> #include <Library/DebugLib.h> #include <IndustryStandard/PeImage.h> #define PE_COFF_IMAGE_ALIGN_SIZE 4 /** Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded into system memory with the PE/COFF Loader Library functions. Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS. If Pe32Data is NULL, then ASSERT(). If EntryPoint is NULL, then ASSERT(). @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. @param EntryPoint The pointer to entry point to the PE/COFF image to return. @retval RETURN_SUCCESS EntryPoint was returned. @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image. **/ RETURN_STATUS EFIAPI PeCoffLoaderGetEntryPoint ( IN VOID *Pe32Data, OUT VOID **EntryPoint ) { EFI_IMAGE_DOS_HEADER *DosHdr; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; ASSERT (Pe32Data != NULL); ASSERT (EntryPoint != NULL); 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) { *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(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)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff)); return RETURN_SUCCESS; } return RETURN_UNSUPPORTED; } /** Returns the machine type of a PE/COFF image. Returns the machine type from the PE/COFF image specified by Pe32Data. If Pe32Data is NULL, then ASSERT(). @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. @return Machine type or zero if not a valid image. **/ UINT16 EFIAPI PeCoffLoaderGetMachineType ( IN VOID *Pe32Data ) { EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; EFI_IMAGE_DOS_HEADER *DosHdr; ASSERT (Pe32Data != NULL); 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; } if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { return Hdr.Te->Machine; } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { return Hdr.Pe32->FileHeader.Machine; } return 0x0000; } /** Returns a pointer to the PDB file name for a PE/COFF image that has been 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 ASSERT(). @param Pe32Data The 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; ASSERT (Pe32Data != NULL); TEImageAdjust = 0; DirectoryEntry = NULL; DebugEntry = NULL; NumberOfRvaAndSizes = 0; 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; } if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_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; DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN)Hdr.Te + Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress + TEImageAdjust); } } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_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: // // Assume PE32 image with IA32 Machine field. // Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC; break; case IMAGE_FILE_MACHINE_X64: case IMAGE_FILE_MACHINE_IA64: // // Assume PE32+ image with x64 or IA64 Machine field // Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; break; default: // // For unknow Machine field, use Magic in optional Header // Magic = Hdr.Pe32->OptionalHeader.Magic; } if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_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]); DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN)Pe32Data + DirectoryEntry->VirtualAddress); } 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]); DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN)Pe32Data + DirectoryEntry->VirtualAddress); } if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) { DirectoryEntry = NULL; DebugEntry = NULL; } } else { return NULL; } if ((DebugEntry == NULL) || (DirectoryEntry == NULL)) { 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 (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { if (DebugEntry->SizeOfData > 0) { CodeViewEntryPointer = (VOID *)((UINTN)DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust); 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; } /** Returns the size of the PE/COFF headers Returns the size of the PE/COFF header specified by Pe32Data. If Pe32Data is NULL, then ASSERT(). @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. @return Size of PE/COFF header in bytes or zero if not a valid image. **/ UINT32 EFIAPI PeCoffGetSizeOfHeaders ( IN VOID *Pe32Data ) { EFI_IMAGE_DOS_HEADER *DosHdr; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; UINTN SizeOfHeaders; ASSERT (Pe32Data != NULL); 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; } if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize; } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders; } else { SizeOfHeaders = 0; } return (UINT32)SizeOfHeaders; } /** Returns PE/COFF image base is loaded in system memory where the input address is in. On DEBUG build, searches the PE/COFF image base forward the input address and returns it. @param Address Address located in one PE/COFF image. @retval 0 RELEASE build or cannot find the PE/COFF image base. @retval others PE/COFF image base found. **/ UINTN EFIAPI PeCoffSearchImageBase ( IN UINTN Address ) { UINTN Pe32Data; Pe32Data = 0; DEBUG_CODE_BEGIN (); EFI_IMAGE_DOS_HEADER *DosHdr; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; // // Find Image Base // Pe32Data = Address & ~(PE_COFF_IMAGE_ALIGN_SIZE - 1); while (Pe32Data != 0) { 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 *)(Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff)); // // Make sure PE header address does not overflow and is less than the initial address. // if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < Address)) { if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { break; } } } else { // // DOS image header is not present, TE header is at the image base. // Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; if ((Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) && ((Hdr.Te->Machine == IMAGE_FILE_MACHINE_I386) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_IA64) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_EBC) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_X64) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARM64) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARMTHUMB_MIXED)) ) { break; } } // // Not found the image base, check the previous aligned address // Pe32Data -= PE_COFF_IMAGE_ALIGN_SIZE; } DEBUG_CODE_END (); return Pe32Data; }