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 **/
42 RETURN_STATUS
43 EFIAPI
PeCoffLoaderGetEntryPoint(IN VOID * Pe32Data,OUT VOID ** EntryPoint)44 PeCoffLoaderGetEntryPoint (
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 **/
96 UINT16
97 EFIAPI
PeCoffLoaderGetMachineType(IN VOID * Pe32Data)98 PeCoffLoaderGetMachineType (
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 **/
148 VOID *
149 EFIAPI
PeCoffLoaderGetPdbPointer(IN VOID * Pe32Data)150 PeCoffLoaderGetPdbPointer (
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 **/
283 UINT32
284 EFIAPI
PeCoffGetSizeOfHeaders(IN VOID * Pe32Data)285 PeCoffGetSizeOfHeaders (
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