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 - 2018, Intel Corporation. All rights reserved.<BR>
6   Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
7   SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 **/
10 
11 
12 #include <Base.h>
13 
14 #include <Library/PeCoffGetEntryPointLib.h>
15 #include <Library/DebugLib.h>
16 
17 #include <IndustryStandard/PeImage.h>
18 
19 #define PE_COFF_IMAGE_ALIGN_SIZE        4
20 
21 /**
22   Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded
23   into system memory with the PE/COFF Loader Library functions.
24 
25   Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry
26   point in EntryPoint.  If the entry point could not be retrieved from the PE/COFF image, then
27   return RETURN_INVALID_PARAMETER.  Otherwise return RETURN_SUCCESS.
28   If Pe32Data is NULL, then ASSERT().
29   If EntryPoint is NULL, then ASSERT().
30 
31   @param  Pe32Data                  The pointer to the PE/COFF image that is loaded in system memory.
32   @param  EntryPoint                The pointer to entry point to the PE/COFF image to return.
33 
34   @retval RETURN_SUCCESS            EntryPoint was returned.
35   @retval RETURN_INVALID_PARAMETER  The entry point could not be found in the PE/COFF image.
36 
37 **/
38 RETURN_STATUS
39 EFIAPI
PeCoffLoaderGetEntryPoint(IN VOID * Pe32Data,OUT VOID ** EntryPoint)40 PeCoffLoaderGetEntryPoint (
41   IN  VOID  *Pe32Data,
42   OUT VOID  **EntryPoint
43   )
44 {
45   EFI_IMAGE_DOS_HEADER                  *DosHdr;
46   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
47 
48   ASSERT (Pe32Data   != NULL);
49   ASSERT (EntryPoint != NULL);
50 
51   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
52   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
53     //
54     // DOS image header is present, so read the PE header after the DOS image header.
55     //
56     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
57   } else {
58     //
59     // DOS image header is not present, so PE header is at the image base.
60     //
61     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
62   }
63 
64   //
65   // Calculate the entry point relative to the start of the image.
66   // AddressOfEntryPoint is common for PE32 & PE32+
67   //
68   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
69     *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);
70     return RETURN_SUCCESS;
71   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
72     *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff));
73     return RETURN_SUCCESS;
74   }
75 
76   return RETURN_UNSUPPORTED;
77 }
78 
79 
80 /**
81   Returns the machine type of a PE/COFF image.
82 
83   Returns the machine type from the PE/COFF image specified by Pe32Data.
84   If Pe32Data is NULL, then ASSERT().
85 
86   @param  Pe32Data   The pointer to the PE/COFF image that is loaded in system
87                      memory.
88 
89   @return Machine type or zero if not a valid image.
90 
91 **/
92 UINT16
93 EFIAPI
PeCoffLoaderGetMachineType(IN VOID * Pe32Data)94 PeCoffLoaderGetMachineType (
95   IN VOID  *Pe32Data
96   )
97 {
98   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
99   EFI_IMAGE_DOS_HEADER                 *DosHdr;
100 
101   ASSERT (Pe32Data != NULL);
102 
103   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
104   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
105     //
106     // DOS image header is present, so read the PE header after the DOS image header.
107     //
108     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
109   } else {
110     //
111     // DOS image header is not present, so PE header is at the image base.
112     //
113     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
114   }
115 
116   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
117     return Hdr.Te->Machine;
118   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE)  {
119     return Hdr.Pe32->FileHeader.Machine;
120   }
121 
122   return 0x0000;
123 }
124 
125 /**
126   Returns a pointer to the PDB file name for a PE/COFF image that has been
127   loaded into system memory with the PE/COFF Loader Library functions.
128 
129   Returns the PDB file name for the PE/COFF image specified by Pe32Data.  If
130   the PE/COFF image specified by Pe32Data is not a valid, then NULL is
131   returned.  If the PE/COFF image specified by Pe32Data does not contain a
132   debug directory entry, then NULL is returned.  If the debug directory entry
133   in the PE/COFF image specified by Pe32Data does not contain a PDB file name,
134   then NULL is returned.
135   If Pe32Data is NULL, then ASSERT().
136 
137   @param  Pe32Data   The pointer to the PE/COFF image that is loaded in system
138                      memory.
139 
140   @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL
141           if it cannot be retrieved.
142 
143 **/
144 VOID *
145 EFIAPI
PeCoffLoaderGetPdbPointer(IN VOID * Pe32Data)146 PeCoffLoaderGetPdbPointer (
147   IN VOID  *Pe32Data
148   )
149 {
150   EFI_IMAGE_DOS_HEADER                  *DosHdr;
151   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
152   EFI_IMAGE_DATA_DIRECTORY              *DirectoryEntry;
153   EFI_IMAGE_DEBUG_DIRECTORY_ENTRY       *DebugEntry;
154   UINTN                                 DirCount;
155   VOID                                  *CodeViewEntryPointer;
156   INTN                                  TEImageAdjust;
157   UINT32                                NumberOfRvaAndSizes;
158   UINT16                                Magic;
159 
160   ASSERT (Pe32Data   != NULL);
161 
162   TEImageAdjust       = 0;
163   DirectoryEntry      = NULL;
164   DebugEntry          = NULL;
165   NumberOfRvaAndSizes = 0;
166 
167   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
168   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
169     //
170     // DOS image header is present, so read the PE header after the DOS image header.
171     //
172     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
173   } else {
174     //
175     // DOS image header is not present, so PE header is at the image base.
176     //
177     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
178   }
179 
180   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
181     if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {
182       DirectoryEntry  = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];
183       TEImageAdjust   = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;
184       DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +
185                     Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress +
186                     TEImageAdjust);
187     }
188   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
189     //
190     // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.
191     //       It is due to backward-compatibility, for some system might
192     //       generate PE32+ image with PE32 Magic.
193     //
194     switch (Hdr.Pe32->FileHeader.Machine) {
195     case IMAGE_FILE_MACHINE_I386:
196       //
197       // Assume PE32 image with IA32 Machine field.
198       //
199       Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
200       break;
201     case IMAGE_FILE_MACHINE_X64:
202     case IMAGE_FILE_MACHINE_IA64:
203       //
204       // Assume PE32+ image with x64 or IA64 Machine field
205       //
206       Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
207       break;
208     default:
209       //
210       // For unknow Machine field, use Magic in optional Header
211       //
212       Magic = Hdr.Pe32->OptionalHeader.Magic;
213     }
214 
215     if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
216       //
217       // Use PE32 offset get Debug Directory Entry
218       //
219       NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
220       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
221       DebugEntry     = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
222     } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
223       //
224       // Use PE32+ offset get Debug Directory Entry
225       //
226       NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
227       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
228       DebugEntry     = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
229     }
230 
231     if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
232       DirectoryEntry = NULL;
233       DebugEntry = NULL;
234     }
235   } else {
236     return NULL;
237   }
238 
239   if (DebugEntry == NULL || DirectoryEntry == NULL) {
240     return NULL;
241   }
242 
243   //
244   // Scan the directory to find the debug entry.
245   //
246   for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {
247     if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
248       if (DebugEntry->SizeOfData > 0) {
249         CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust);
250         switch (* (UINT32 *) CodeViewEntryPointer) {
251         case CODEVIEW_SIGNATURE_NB10:
252           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
253         case CODEVIEW_SIGNATURE_RSDS:
254           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
255         case CODEVIEW_SIGNATURE_MTOC:
256           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
257         default:
258           break;
259         }
260       }
261     }
262   }
263 
264   return NULL;
265 }
266 
267 /**
268   Returns the size of the PE/COFF headers
269 
270   Returns the size of the PE/COFF header specified by Pe32Data.
271   If Pe32Data is NULL, then ASSERT().
272 
273   @param  Pe32Data   The pointer to the PE/COFF image that is loaded in system
274                      memory.
275 
276   @return Size of PE/COFF header in bytes or zero if not a valid image.
277 
278 **/
279 UINT32
280 EFIAPI
PeCoffGetSizeOfHeaders(IN VOID * Pe32Data)281 PeCoffGetSizeOfHeaders (
282   IN VOID     *Pe32Data
283   )
284 {
285   EFI_IMAGE_DOS_HEADER                  *DosHdr;
286   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
287   UINTN                                 SizeOfHeaders;
288 
289   ASSERT (Pe32Data   != NULL);
290 
291   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
292   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
293     //
294     // DOS image header is present, so read the PE header after the DOS image header.
295     //
296     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
297   } else {
298     //
299     // DOS image header is not present, so PE header is at the image base.
300     //
301     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
302   }
303 
304   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
305     SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;
306   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
307     SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
308   } else {
309     SizeOfHeaders = 0;
310   }
311 
312   return (UINT32) SizeOfHeaders;
313 }
314 
315 /**
316   Returns PE/COFF image base is loaded in system memory where the input address is in.
317 
318   On DEBUG build, searches the PE/COFF image base forward the input address and
319   returns it.
320 
321   @param  Address    Address located in one PE/COFF image.
322 
323   @retval 0          RELEASE build or cannot find the PE/COFF image base.
324   @retval others     PE/COFF image base found.
325 
326 **/
327 UINTN
328 EFIAPI
PeCoffSearchImageBase(IN UINTN Address)329 PeCoffSearchImageBase (
330   IN UINTN    Address
331   )
332 {
333   UINTN                                Pe32Data;
334 
335   Pe32Data = 0;
336 
337   DEBUG_CODE (
338     EFI_IMAGE_DOS_HEADER                 *DosHdr;
339     EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
340 
341     //
342     // Find Image Base
343     //
344     Pe32Data = Address & ~(PE_COFF_IMAGE_ALIGN_SIZE - 1);
345     while (Pe32Data != 0) {
346       DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data;
347       if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
348         //
349         // DOS image header is present, so read the PE header after the DOS image header.
350         //
351         Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
352         //
353         // Make sure PE header address does not overflow and is less than the initial address.
354         //
355         if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < Address)) {
356           if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
357             break;
358           }
359         }
360       } else {
361         //
362         // DOS image header is not present, TE header is at the image base.
363         //
364         Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
365         if ((Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) &&
366             ((Hdr.Te->Machine == IMAGE_FILE_MACHINE_I386)  || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_IA64) ||
367              (Hdr.Te->Machine == IMAGE_FILE_MACHINE_EBC)   || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_X64)  ||
368              (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARM64) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARMTHUMB_MIXED))
369              ) {
370           break;
371         }
372       }
373 
374       //
375       // Not found the image base, check the previous aligned address
376       //
377       Pe32Data -= PE_COFF_IMAGE_ALIGN_SIZE;
378     }
379   );
380 
381   return Pe32Data;
382 }
383