• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**@file
2 
3 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   PeiNt32PeCoffExtraActionLib.c
15 
16 Abstract:
17 
18   Provides services to perform additional actions to relocate and unload
19   PE/Coff image for NT32 environment specific purpose such as souce level debug.
20   This version only works for DXE phase
21 
22 
23 **/
24 //
25 // The package level header files this module uses
26 //
27 #include <WinNtDxe.h>
28 
29 //
30 // The protocols, PPI and GUID defintions for this module
31 //
32 #include <Protocol/WinNtThunk.h>
33 
34 #include <Library/PeCoffLib.h>
35 
36 #include <Library/BaseLib.h>
37 #include <Library/DebugLib.h>
38 #include <Library/HobLib.h>
39 #include <Library/BaseMemoryLib.h>
40 #include <Library/PeCoffExtraActionLib.h>
41 
42 #define MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE 0x100
43 
44 typedef struct {
45   CHAR8   *PdbPointer;
46   VOID    *ModHandle;
47 } PDB_NAME_TO_MOD_HANDLE;
48 
49 
50 //
51 // Cache of WinNtThunk protocol
52 //
53 EFI_WIN_NT_THUNK_PROTOCOL   *mWinNt = NULL;
54 
55 //
56 // An Array to hold the ModHandle
57 //
58 PDB_NAME_TO_MOD_HANDLE  *mPdbNameModHandleArray = NULL;
59 UINTN                   mPdbNameModHandleArraySize = 0;
60 
61 
62 /**
63   The constructor function gets  the pointer of the WinNT thunk functions
64   It will ASSERT() if NT thunk protocol is not installed.
65 
66   @retval EFI_SUCCESS   WinNT thunk protocol is found and cached.
67 
68 **/
69 EFI_STATUS
70 EFIAPI
Nt32PeCoffGetWinNtThunkStucture(VOID)71 Nt32PeCoffGetWinNtThunkStucture (
72   VOID
73   )
74 {
75   EFI_HOB_GUID_TYPE   *GuidHob;
76 
77   //
78   // Retrieve WinNtThunkProtocol from GUID'ed HOB
79   //
80   GuidHob = GetFirstGuidHob (&gEfiWinNtThunkProtocolGuid);
81   ASSERT (GuidHob != NULL);
82   mWinNt = (EFI_WIN_NT_THUNK_PROTOCOL *)(*(UINTN *)(GET_GUID_HOB_DATA (GuidHob)));
83   ASSERT (mWinNt != NULL);
84 
85 
86   return EFI_SUCCESS;
87 }
88 
89 /**
90   Convert the passed in Ascii string to Unicode.
91 
92   This function  Convert the passed in Ascii string to Unicode.Optionally return
93    the length of the strings..
94 
95   @param  AsciiString    Pointer to an AscII string
96   @param  StrLen         Length of string
97 
98   @return  Pointer to malloc'ed Unicode version of Ascii
99 
100 **/
101 CHAR16 *
AsciiToUnicode(IN CHAR8 * Ascii,IN UINTN * StrLen OPTIONAL)102 AsciiToUnicode (
103   IN  CHAR8   *Ascii,
104   IN  UINTN   *StrLen OPTIONAL
105   )
106 {
107   UINTN   Index;
108   CHAR16  *Unicode;
109 
110   //
111   // Allocate a buffer for unicode string
112   //
113   for (Index = 0; Ascii[Index] != '\0'; Index++)
114     ;
115   Unicode = mWinNt->HeapAlloc ( mWinNt->GetProcessHeap (),
116                                 HEAP_ZERO_MEMORY,
117                                 ((Index + 1) * sizeof (CHAR16))
118                                );
119   if (Unicode == NULL) {
120     return NULL;
121   }
122 
123   for (Index = 0; Ascii[Index] != '\0'; Index++) {
124     Unicode[Index] = (CHAR16) Ascii[Index];
125   }
126 
127   Unicode[Index] = '\0';
128 
129   if (StrLen != NULL) {
130     *StrLen = Index;
131   }
132 
133   return Unicode;
134 }
135 /**
136   Store the ModHandle in an array indexed by the Pdb File name.
137   The ModHandle is needed to unload the image.
138 
139 
140   @param ImageContext - Input data returned from PE Laoder Library. Used to find the
141                  .PDB file name of the PE Image.
142   @param ModHandle    - Returned from LoadLibraryEx() and stored for call to
143                  FreeLibrary().
144 
145   @return   return EFI_SUCCESS when ModHandle was stored.
146 
147 --*/
148 EFI_STATUS
AddModHandle(IN PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext,IN VOID * ModHandle)149 AddModHandle (
150   IN  PE_COFF_LOADER_IMAGE_CONTEXT         *ImageContext,
151   IN  VOID                                 *ModHandle
152   )
153 
154 {
155   UINTN                   Index;
156   PDB_NAME_TO_MOD_HANDLE  *Array;
157   UINTN                   PreviousSize;
158   PDB_NAME_TO_MOD_HANDLE  *TempArray;
159   HANDLE                  Handle;
160 
161   //
162   // Return EFI_ALREADY_STARTED if this DLL has already been loaded
163   //
164   Array = mPdbNameModHandleArray;
165   for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
166     if (Array->PdbPointer != NULL && Array->ModHandle == ModHandle) {
167       return EFI_ALREADY_STARTED;
168     }
169   }
170 
171   Array = mPdbNameModHandleArray;
172   for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
173     if (Array->PdbPointer == NULL) {
174       //
175       // Make a copy of the stirng and store the ModHandle
176       //
177       Handle = mWinNt->GetProcessHeap ();
178       Array->PdbPointer = mWinNt->HeapAlloc ( Handle,
179                                 HEAP_ZERO_MEMORY,
180                                 AsciiStrLen (ImageContext->PdbPointer) + 1
181                                );
182 
183       ASSERT (Array->PdbPointer != NULL);
184 
185       AsciiStrCpy (Array->PdbPointer, ImageContext->PdbPointer);
186       Array->ModHandle = ModHandle;
187       return EFI_SUCCESS;
188     }
189   }
190 
191   //
192   // No free space in mPdbNameModHandleArray so grow it by
193   // MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE entires.
194   //
195   PreviousSize = mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE);
196   mPdbNameModHandleArraySize += MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE;
197   //
198   // re-allocate a new buffer and copy the old values to the new locaiton.
199   //
200   TempArray = mWinNt->HeapAlloc ( mWinNt->GetProcessHeap (),
201                                 HEAP_ZERO_MEMORY,
202                                 mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE)
203                                );
204 
205   CopyMem ((VOID *) (UINTN) TempArray, (VOID *) (UINTN)mPdbNameModHandleArray, PreviousSize);
206 
207   mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, mPdbNameModHandleArray);
208 
209   mPdbNameModHandleArray = TempArray;
210   if (mPdbNameModHandleArray == NULL) {
211     ASSERT (FALSE);
212     return EFI_OUT_OF_RESOURCES;
213   }
214 
215 
216   return AddModHandle (ImageContext, ModHandle);
217 }
218 /**
219   Return the ModHandle and delete the entry in the array.
220 
221 
222    @param  ImageContext - Input data returned from PE Laoder Library. Used to find the
223                  .PDB file name of the PE Image.
224 
225   @return
226     ModHandle - ModHandle assoicated with ImageContext is returned
227     NULL      - No ModHandle associated with ImageContext
228 
229 **/
230 VOID *
RemoveModeHandle(IN PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)231 RemoveModeHandle (
232   IN  PE_COFF_LOADER_IMAGE_CONTEXT         *ImageContext
233   )
234 {
235   UINTN                   Index;
236   PDB_NAME_TO_MOD_HANDLE  *Array;
237 
238   if (ImageContext->PdbPointer == NULL) {
239     //
240     // If no PDB pointer there is no ModHandle so return NULL
241     //
242     return NULL;
243   }
244 
245   Array = mPdbNameModHandleArray;
246   for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
247     if ((Array->PdbPointer != NULL) && (AsciiStrCmp(Array->PdbPointer, ImageContext->PdbPointer) == 0)) {
248       //
249       // If you find a match return it and delete the entry
250       //
251       mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, Array->PdbPointer);
252       Array->PdbPointer = NULL;
253       return Array->ModHandle;
254     }
255   }
256 
257   return NULL;
258 }
259 
260 /**
261   Performs additional actions after a PE/COFF image has been loaded and relocated.
262 
263   For NT32, this function load symbols to support source level debugging.
264 
265   If ImageContext is NULL, then ASSERT().
266 
267   @param  ImageContext  Pointer to the image context structure that describes the
268                         PE/COFF image that has already been loaded and relocated.
269 
270 **/
271 VOID
272 EFIAPI
PeCoffLoaderRelocateImageExtraAction(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)273 PeCoffLoaderRelocateImageExtraAction (
274   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
275   )
276 {
277   EFI_STATUS        Status;
278   VOID              *DllEntryPoint;
279   CHAR16            *DllFileName;
280   HMODULE           Library;
281   UINTN             Index;
282 
283   ASSERT (ImageContext != NULL);
284 
285   if (mWinNt == NULL) {
286     Nt32PeCoffGetWinNtThunkStucture ();
287   }
288 
289   //
290   // If we load our own PE COFF images the Windows debugger can not source
291   //  level debug our code. If a valid PDB pointer exists usw it to load
292   //  the *.dll file as a library using Windows* APIs. This allows
293   //  source level debug. The image is still loaded and relocated
294   //  in the Framework memory space like on a real system (by the code above),
295   //  but the entry point points into the DLL loaded by the code bellow.
296   //
297 
298   DllEntryPoint = NULL;
299 
300   //
301   // Load the DLL if it's not an EBC image.
302   //
303   if ((ImageContext->PdbPointer != NULL) &&
304       (ImageContext->Machine != EFI_IMAGE_MACHINE_EBC)) {
305     //
306     // Convert filename from ASCII to Unicode
307     //
308     DllFileName = AsciiToUnicode (ImageContext->PdbPointer, &Index);
309 
310     //
311     // Check that we have a valid filename
312     //
313     if (Index < 5 || DllFileName[Index - 4] != '.') {
314       mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, DllFileName);
315 
316       //
317       // Never return an error if PeCoffLoaderRelocateImage() succeeded.
318       // The image will run, but we just can't source level debug. If we
319       // return an error the image will not run.
320       //
321       return;
322     }
323     //
324     // Replace .PDB with .DLL on the filename
325     //
326     DllFileName[Index - 3]  = 'D';
327     DllFileName[Index - 2]  = 'L';
328     DllFileName[Index - 1]  = 'L';
329 
330     //
331     // Load the .DLL file into the user process's address space for source
332     // level debug
333     //
334     Library = mWinNt->LoadLibraryEx  (DllFileName, NULL, DONT_RESOLVE_DLL_REFERENCES);
335     if (Library != NULL) {
336       //
337       // InitializeDriver is the entry point we put in all our EFI DLL's. The
338       // DONT_RESOLVE_DLL_REFERENCES argument to LoadLIbraryEx() suppresses the
339       // normal DLL entry point of DllMain, and prevents other modules that are
340       // referenced in side the DllFileName from being loaded. There is no error
341       // checking as the we can point to the PE32 image loaded by Tiano. This
342       // step is only needed for source level debugging
343       //
344       DllEntryPoint = (VOID *) (UINTN) mWinNt->GetProcAddress (Library, "InitializeDriver");
345 
346     }
347 
348     if ((Library != NULL) && (DllEntryPoint != NULL)) {
349       Status = AddModHandle (ImageContext, Library);
350       if (Status == EFI_ALREADY_STARTED) {
351         //
352         // If the DLL has already been loaded before, then this instance of the DLL can not be debugged.
353         //
354         ImageContext->PdbPointer = NULL;
355         DEBUG ((EFI_D_ERROR, "WARNING: DLL already loaded.  No source level debug %s. \n", DllFileName));
356       } else {
357         //
358         // This DLL is not already loaded, so source level debugging is supported.
359         //
360         ImageContext->EntryPoint  = (EFI_PHYSICAL_ADDRESS) (UINTN) DllEntryPoint;
361         DEBUG ((EFI_D_INFO, "LoadLibraryEx (%s,\n               NULL, DONT_RESOLVE_DLL_REFERENCES)\n", DllFileName));
362       }
363     } else {
364       //
365       // This DLL does not support source level debugging at all.
366       //
367       DEBUG ((EFI_D_ERROR, "WARNING: No source level debug %s. \n", DllFileName));
368     }
369 
370     mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, DllFileName);
371   }
372 
373   //
374   // Never return an error if PeCoffLoaderRelocateImage() succeeded.
375   // The image will run, but we just can't source level debug. If we
376   // return an error the image will not run.
377   //
378   return;
379 }
380 
381 /**
382   Performs additional actions just before a PE/COFF image is unloaded.  Any resources
383   that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed.
384 
385   For NT32, this function unloads symbols for source level debugging.
386 
387   If ImageContext is NULL, then ASSERT().
388 
389   @param  ImageContext  Pointer to the image context structure that describes the
390                         PE/COFF image that is being unloaded.
391 
392 **/
393 VOID
394 EFIAPI
PeCoffLoaderUnloadImageExtraAction(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)395 PeCoffLoaderUnloadImageExtraAction (
396   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
397   )
398 {
399   VOID *ModHandle;
400 
401   ASSERT (ImageContext != NULL);
402 
403   ModHandle = RemoveModeHandle (ImageContext);
404   if (ModHandle != NULL) {
405     mWinNt->FreeLibrary (ModHandle);
406   }
407   return;
408 }
409