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