1 /*++ @file
2 
3 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
4 Portions copyright (c) 2008 - 2011, Apple Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "PiPei.h"
10 #include <Library/PeCoffGetEntryPointLib.h>
11 #include <Library/PeiServicesLib.h>
12 #include <IndustryStandard/PeImage.h>
13 #include <Library/DebugLib.h>
14 
15 #include <Ppi/EmuThunk.h>
16 #include <Protocol/EmuThunk.h>
17 
18 
19 
20 /**
21   Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded
22   into system memory with the PE/COFF Loader Library functions.
23 
24   Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry
25   point in EntryPoint.  If the entry point could not be retrieved from the PE/COFF image, then
26   return RETURN_INVALID_PARAMETER.  Otherwise return RETURN_SUCCESS.
27   If Pe32Data is NULL, then ASSERT().
28   If EntryPoint is NULL, then ASSERT().
29 
30   @param  Pe32Data                  The pointer to the PE/COFF image that is loaded in system memory.
31   @param  EntryPoint                The pointer to entry point to the PE/COFF image to return.
32 
33   @retval RETURN_SUCCESS            EntryPoint was returned.
34   @retval RETURN_INVALID_PARAMETER  The entry point could not be found in the PE/COFF image.
35 
36 **/
37 RETURN_STATUS
38 EFIAPI
PeCoffLoaderGetEntryPoint(IN VOID * Pe32Data,IN OUT VOID ** EntryPoint)39 PeCoffLoaderGetEntryPoint (
40   IN     VOID  *Pe32Data,
41   IN OUT VOID  **EntryPoint
42   )
43 {
44   EMU_THUNK_PPI           *ThunkPpi;
45   EFI_STATUS              Status;
46   EMU_THUNK_PROTOCOL      *Thunk;
47 
48   //
49   // Locate EmuThunkPpi for retrieving standard output handle
50   //
51   Status = PeiServicesLocatePpi (
52               &gEmuThunkPpiGuid,
53               0,
54               NULL,
55               (VOID **) &ThunkPpi
56              );
57   ASSERT_EFI_ERROR (Status);
58 
59   Thunk  = (EMU_THUNK_PROTOCOL *)ThunkPpi->Thunk ();
60 
61   return Thunk->PeCoffGetEntryPoint (Pe32Data, EntryPoint);
62 }
63 
64 /**
65   Returns the machine type of PE/COFF image.
66   This is copied from MDE BasePeCoffGetEntryPointLib, the code should be sync with it.
67   The reason is Emu package needs to load the image to memory to support source
68   level debug.
69 
70 
71   @param  Pe32Data   Pointer to a PE/COFF header
72 
73   @return            Machine type or zero if not a valid iamge
74 
75 **/
76 UINT16
77 EFIAPI
PeCoffLoaderGetMachineType(IN VOID * Pe32Data)78 PeCoffLoaderGetMachineType (
79   IN  VOID  *Pe32Data
80   )
81 {
82   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
83   EFI_IMAGE_DOS_HEADER                 *DosHdr;
84 
85   ASSERT (Pe32Data   != NULL);
86 
87   DosHdr = (EFI_IMAGE_DOS_HEADER  *)Pe32Data;
88   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
89     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
90 
91   } else {
92     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data);
93   }
94 
95   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
96     return Hdr.Te->Machine;
97   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE)  {
98     return Hdr.Pe32->FileHeader.Machine;
99   }
100 
101   return 0x0000;
102 }
103 
104 /**
105   Returns a pointer to the PDB file name for a PE/COFF image that has been
106   loaded into system memory with the PE/COFF Loader Library functions.
107 
108   Returns the PDB file name for the PE/COFF image specified by Pe32Data.  If
109   the PE/COFF image specified by Pe32Data is not a valid, then NULL is
110   returned.  If the PE/COFF image specified by Pe32Data does not contain a
111   debug directory entry, then NULL is returned.  If the debug directory entry
112   in the PE/COFF image specified by Pe32Data does not contain a PDB file name,
113   then NULL is returned.
114   If Pe32Data is NULL, then ASSERT().
115 
116   @param  Pe32Data   Pointer to the PE/COFF image that is loaded in system
117                      memory.
118 
119   @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL
120           if it cannot be retrieved.
121 
122 **/
123 VOID *
124 EFIAPI
PeCoffLoaderGetPdbPointer(IN VOID * Pe32Data)125 PeCoffLoaderGetPdbPointer (
126   IN VOID  *Pe32Data
127   )
128 {
129   EFI_IMAGE_DOS_HEADER                  *DosHdr;
130   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
131   EFI_IMAGE_DATA_DIRECTORY              *DirectoryEntry;
132   EFI_IMAGE_DEBUG_DIRECTORY_ENTRY       *DebugEntry;
133   UINTN                                 DirCount;
134   VOID                                  *CodeViewEntryPointer;
135   INTN                                  TEImageAdjust;
136   UINT32                                NumberOfRvaAndSizes;
137   UINT16                                Magic;
138 
139   ASSERT (Pe32Data   != NULL);
140 
141   TEImageAdjust       = 0;
142   DirectoryEntry      = NULL;
143   DebugEntry          = NULL;
144   NumberOfRvaAndSizes = 0;
145 
146   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
147   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
148     //
149     // DOS image header is present, so read the PE header after the DOS image header.
150     //
151     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
152   } else {
153     //
154     // DOS image header is not present, so PE header is at the image base.
155     //
156     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
157   }
158 
159   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
160     if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {
161       DirectoryEntry  = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];
162       TEImageAdjust   = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;
163       DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +
164                     Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress +
165                     TEImageAdjust);
166     }
167   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
168     //
169     // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.
170     //       It is due to backward-compatibility, for some system might
171     //       generate PE32+ image with PE32 Magic.
172     //
173     switch (Hdr.Pe32->FileHeader.Machine) {
174     case EFI_IMAGE_MACHINE_IA32:
175       //
176       // Assume PE32 image with IA32 Machine field.
177       //
178       Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
179       break;
180     case EFI_IMAGE_MACHINE_X64:
181     case EFI_IMAGE_MACHINE_IA64:
182       //
183       // Assume PE32+ image with X64 or IA64 Machine field
184       //
185       Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
186       break;
187     default:
188       //
189       // For unknow Machine field, use Magic in optional Header
190       //
191       Magic = Hdr.Pe32->OptionalHeader.Magic;
192     }
193 
194     if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
195       //
196       // Use PE32 offset get Debug Directory Entry
197       //
198       NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
199       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
200       DebugEntry     = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
201     } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
202       //
203       // Use PE32+ offset get Debug Directory Entry
204       //
205       NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
206       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
207       DebugEntry     = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);
208     }
209 
210     if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
211       DirectoryEntry = NULL;
212       DebugEntry = NULL;
213     }
214   } else {
215     return NULL;
216   }
217 
218   if (DebugEntry == NULL || DirectoryEntry == NULL) {
219     return NULL;
220   }
221 
222   for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {
223     if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
224       if (DebugEntry->SizeOfData > 0) {
225         CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust);
226         switch (* (UINT32 *) CodeViewEntryPointer) {
227         case CODEVIEW_SIGNATURE_NB10:
228           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
229         case CODEVIEW_SIGNATURE_RSDS:
230           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
231         case CODEVIEW_SIGNATURE_MTOC:
232           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
233         default:
234           break;
235         }
236       }
237     }
238   }
239 
240   return NULL;
241 }
242 
243 
244 /**
245   Returns the size of the PE/COFF headers
246 
247   Returns the size of the PE/COFF header specified by Pe32Data.
248   If Pe32Data is NULL, then ASSERT().
249 
250   @param  Pe32Data   Pointer to the PE/COFF image that is loaded in system
251                      memory.
252 
253   @return Size of PE/COFF header in bytes or zero if not a valid image.
254 
255 **/
256 UINT32
257 EFIAPI
PeCoffGetSizeOfHeaders(IN VOID * Pe32Data)258 PeCoffGetSizeOfHeaders (
259   IN VOID     *Pe32Data
260   )
261 {
262   EFI_IMAGE_DOS_HEADER                  *DosHdr;
263   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
264   UINTN                                 SizeOfHeaders;
265 
266   ASSERT (Pe32Data   != NULL);
267 
268   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
269   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
270     //
271     // DOS image header is present, so read the PE header after the DOS image header.
272     //
273     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
274   } else {
275     //
276     // DOS image header is not present, so PE header is at the image base.
277     //
278     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
279   }
280 
281   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
282     SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;
283   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
284     SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
285   } else {
286     SizeOfHeaders = 0;
287   }
288 
289   return (UINT32) SizeOfHeaders;
290 }
291 
292