https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
		
			
				
	
	
		
			383 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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 (
 | |
|     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;
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   return Pe32Data;
 | |
| }
 |