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