1/** @file 2 Provides the services to get the entry point to a PE/COFF image that has either been 3 loaded into memory or is executing at it's linked address. 4 5 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR> 6 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this distribution. The full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php. 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15**/ 16 17 18#include <Base.h> 19 20#include <Library/PeCoffGetEntryPointLib.h> 21#include <Library/DebugLib.h> 22 23#include <IndustryStandard/PeImage.h> 24 25/** 26 Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded 27 into system memory with the PE/COFF Loader Library functions. 28 29 Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry 30 point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then 31 return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS. 32 If Pe32Data is NULL, then ASSERT(). 33 If EntryPoint is NULL, then ASSERT(). 34 35 @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory. 36 @param EntryPoint The pointer to entry point to the PE/COFF image to return. 37 38 @retval RETURN_SUCCESS EntryPoint was returned. 39 @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image. 40 41**/ 42RETURN_STATUS 43EFIAPI 44PeCoffLoaderGetEntryPoint ( 45 IN VOID *Pe32Data, 46 OUT VOID **EntryPoint 47 ) 48{ 49 EFI_IMAGE_DOS_HEADER *DosHdr; 50 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; 51 52 ASSERT (Pe32Data != NULL); 53 ASSERT (EntryPoint != NULL); 54 55 DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; 56 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { 57 // 58 // DOS image header is present, so read the PE header after the DOS image header. 59 // 60 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); 61 } else { 62 // 63 // DOS image header is not present, so PE header is at the image base. 64 // 65 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; 66 } 67 68 // 69 // Calculate the entry point relative to the start of the image. 70 // AddressOfEntryPoint is common for PE32 & PE32+ 71 // 72 if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { 73 *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize); 74 return RETURN_SUCCESS; 75 } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { 76 *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff)); 77 return RETURN_SUCCESS; 78 } 79 80 return RETURN_UNSUPPORTED; 81} 82 83 84/** 85 Returns the machine type of a PE/COFF image. 86 87 Returns the machine type from the PE/COFF image specified by Pe32Data. 88 If Pe32Data is NULL, then ASSERT(). 89 90 @param Pe32Data The pointer to the PE/COFF image that is loaded in system 91 memory. 92 93 @return Machine type or zero if not a valid image. 94 95**/ 96UINT16 97EFIAPI 98PeCoffLoaderGetMachineType ( 99 IN VOID *Pe32Data 100 ) 101{ 102 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; 103 EFI_IMAGE_DOS_HEADER *DosHdr; 104 105 ASSERT (Pe32Data != NULL); 106 107 DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; 108 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { 109 // 110 // DOS image header is present, so read the PE header after the DOS image header. 111 // 112 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); 113 } else { 114 // 115 // DOS image header is not present, so PE header is at the image base. 116 // 117 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; 118 } 119 120 if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { 121 return Hdr.Te->Machine; 122 } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { 123 return Hdr.Pe32->FileHeader.Machine; 124 } 125 126 return 0x0000; 127} 128 129/** 130 Returns a pointer to the PDB file name for a PE/COFF image that has been 131 loaded into system memory with the PE/COFF Loader Library functions. 132 133 Returns the PDB file name for the PE/COFF image specified by Pe32Data. If 134 the PE/COFF image specified by Pe32Data is not a valid, then NULL is 135 returned. If the PE/COFF image specified by Pe32Data does not contain a 136 debug directory entry, then NULL is returned. If the debug directory entry 137 in the PE/COFF image specified by Pe32Data does not contain a PDB file name, 138 then NULL is returned. 139 If Pe32Data is NULL, then ASSERT(). 140 141 @param Pe32Data The pointer to the PE/COFF image that is loaded in system 142 memory. 143 144 @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL 145 if it cannot be retrieved. 146 147**/ 148VOID * 149EFIAPI 150PeCoffLoaderGetPdbPointer ( 151 IN VOID *Pe32Data 152 ) 153{ 154 EFI_IMAGE_DOS_HEADER *DosHdr; 155 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; 156 EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry; 157 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry; 158 UINTN DirCount; 159 VOID *CodeViewEntryPointer; 160 INTN TEImageAdjust; 161 UINT32 NumberOfRvaAndSizes; 162 UINT16 Magic; 163 164 ASSERT (Pe32Data != NULL); 165 166 TEImageAdjust = 0; 167 DirectoryEntry = NULL; 168 DebugEntry = NULL; 169 NumberOfRvaAndSizes = 0; 170 171 DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; 172 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { 173 // 174 // DOS image header is present, so read the PE header after the DOS image header. 175 // 176 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); 177 } else { 178 // 179 // DOS image header is not present, so PE header is at the image base. 180 // 181 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; 182 } 183 184 if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { 185 if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) { 186 DirectoryEntry = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG]; 187 TEImageAdjust = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize; 188 DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te + 189 Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress + 190 TEImageAdjust); 191 } 192 } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { 193 // 194 // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic. 195 // It is due to backward-compatibility, for some system might 196 // generate PE32+ image with PE32 Magic. 197 // 198 switch (Hdr.Pe32->FileHeader.Machine) { 199 case IMAGE_FILE_MACHINE_I386: 200 // 201 // Assume PE32 image with IA32 Machine field. 202 // 203 Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC; 204 break; 205 case IMAGE_FILE_MACHINE_X64: 206 case IMAGE_FILE_MACHINE_IA64: 207 // 208 // Assume PE32+ image with x64 or IA64 Machine field 209 // 210 Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; 211 break; 212 default: 213 // 214 // For unknow Machine field, use Magic in optional Header 215 // 216 Magic = Hdr.Pe32->OptionalHeader.Magic; 217 } 218 219 if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { 220 // 221 // Use PE32 offset get Debug Directory Entry 222 // 223 NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; 224 DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); 225 DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress); 226 } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 227 // 228 // Use PE32+ offset get Debug Directory Entry 229 // 230 NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; 231 DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); 232 DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress); 233 } 234 235 if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) { 236 DirectoryEntry = NULL; 237 DebugEntry = NULL; 238 } 239 } else { 240 return NULL; 241 } 242 243 if (DebugEntry == NULL || DirectoryEntry == NULL) { 244 return NULL; 245 } 246 247 // 248 // Scan the directory to find the debug entry. 249 // 250 for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) { 251 if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) { 252 if (DebugEntry->SizeOfData > 0) { 253 CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust); 254 switch (* (UINT32 *) CodeViewEntryPointer) { 255 case CODEVIEW_SIGNATURE_NB10: 256 return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY)); 257 case CODEVIEW_SIGNATURE_RSDS: 258 return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY)); 259 case CODEVIEW_SIGNATURE_MTOC: 260 return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY)); 261 default: 262 break; 263 } 264 } 265 } 266 } 267 268 return NULL; 269} 270 271/** 272 Returns the size of the PE/COFF headers 273 274 Returns the size of the PE/COFF header specified by Pe32Data. 275 If Pe32Data is NULL, then ASSERT(). 276 277 @param Pe32Data The pointer to the PE/COFF image that is loaded in system 278 memory. 279 280 @return Size of PE/COFF header in bytes or zero if not a valid image. 281 282**/ 283UINT32 284EFIAPI 285PeCoffGetSizeOfHeaders ( 286 IN VOID *Pe32Data 287 ) 288{ 289 EFI_IMAGE_DOS_HEADER *DosHdr; 290 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; 291 UINTN SizeOfHeaders; 292 293 ASSERT (Pe32Data != NULL); 294 295 DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; 296 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { 297 // 298 // DOS image header is present, so read the PE header after the DOS image header. 299 // 300 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); 301 } else { 302 // 303 // DOS image header is not present, so PE header is at the image base. 304 // 305 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; 306 } 307 308 if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { 309 SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize; 310 } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { 311 SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders; 312 } else { 313 SizeOfHeaders = 0; 314 } 315 316 return (UINT32) SizeOfHeaders; 317} 318 319