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