]> git.proxmox.com Git - mirror_edk2.git/blob - Nt32Pkg/Library/DxeNt32PeCoffExtraActionLib/DxeNt32PeCoffExtraActionLib.c
Make PeCoffExtraActionLib more robust, in case it is called before its constructor...
[mirror_edk2.git] / Nt32Pkg / Library / DxeNt32PeCoffExtraActionLib / DxeNt32PeCoffExtraActionLib.c
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
71 DxeNt32PeCoffLibExtraActionConstructor (
72 IN EFI_HANDLE ImageHandle,
73 IN EFI_SYSTEM_TABLE *SystemTable
74 )
75 {
76 EFI_HOB_GUID_TYPE *GuidHob;
77
78 //
79 // Retrieve WinNtThunkProtocol from GUID'ed HOB
80 //
81 GuidHob = GetFirstGuidHob (&gEfiWinNtThunkProtocolGuid);
82 ASSERT (GuidHob != NULL);
83 mWinNt = (EFI_WIN_NT_THUNK_PROTOCOL *)(*(UINTN *)(GET_GUID_HOB_DATA (GuidHob)));
84 ASSERT (mWinNt != NULL);
85
86
87 return EFI_SUCCESS;
88 }
89
90 /**
91 Convert the passed in Ascii string to Unicode.
92
93 This function Convert the passed in Ascii string to Unicode.Optionally return
94 the length of the strings..
95
96 @param AsciiString Pointer to an AscII string
97 @param StrLen Length of string
98
99 @return Pointer to malloc'ed Unicode version of Ascii
100
101 **/
102 CHAR16 *
103 AsciiToUnicode (
104 IN CHAR8 *Ascii,
105 IN UINTN *StrLen OPTIONAL
106 )
107 {
108 UINTN Index;
109 CHAR16 *Unicode;
110
111 //
112 // Allocate a buffer for unicode string
113 //
114 for (Index = 0; Ascii[Index] != '\0'; Index++)
115 ;
116 Unicode = mWinNt->HeapAlloc ( mWinNt->GetProcessHeap (),
117 HEAP_ZERO_MEMORY,
118 ((Index + 1) * sizeof (CHAR16))
119 );
120 if (Unicode == NULL) {
121 return NULL;
122 }
123
124 for (Index = 0; Ascii[Index] != '\0'; Index++) {
125 Unicode[Index] = (CHAR16) Ascii[Index];
126 }
127
128 Unicode[Index] = '\0';
129
130 if (StrLen != NULL) {
131 *StrLen = Index;
132 }
133
134 return Unicode;
135 }
136 /**
137 Store the ModHandle in an array indexed by the Pdb File name.
138 The ModHandle is needed to unload the image.
139
140
141 @param ImageContext - Input data returned from PE Laoder Library. Used to find the
142 .PDB file name of the PE Image.
143 @param ModHandle - Returned from LoadLibraryEx() and stored for call to
144 FreeLibrary().
145
146 @return return EFI_SUCCESS when ModHandle was stored.
147
148 --*/
149 EFI_STATUS
150 AddModHandle (
151 IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
152 IN VOID *ModHandle
153 )
154
155 {
156 UINTN Index;
157 PDB_NAME_TO_MOD_HANDLE *Array;
158 UINTN PreviousSize;
159 PDB_NAME_TO_MOD_HANDLE *TempArray;
160 HANDLE Handle;
161
162 //
163 // Return EFI_ALREADY_STARTED if this DLL has already been loaded
164 //
165 Array = mPdbNameModHandleArray;
166 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
167 if (Array->PdbPointer != NULL && Array->ModHandle == ModHandle) {
168 return EFI_ALREADY_STARTED;
169 }
170 }
171
172 Array = mPdbNameModHandleArray;
173 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
174 if (Array->PdbPointer == NULL) {
175 //
176 // Make a copy of the stirng and store the ModHandle
177 //
178 Handle = mWinNt->GetProcessHeap ();
179 Array->PdbPointer = mWinNt->HeapAlloc ( Handle,
180 HEAP_ZERO_MEMORY,
181 AsciiStrLen (ImageContext->PdbPointer) + 1
182 );
183
184 ASSERT (Array->PdbPointer != NULL);
185
186 AsciiStrCpy (Array->PdbPointer, ImageContext->PdbPointer);
187 Array->ModHandle = ModHandle;
188 return EFI_SUCCESS;
189 }
190 }
191
192 //
193 // No free space in mPdbNameModHandleArray so grow it by
194 // MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE entires.
195 //
196 PreviousSize = mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE);
197 mPdbNameModHandleArraySize += MAX_PDB_NAME_TO_MOD_HANDLE_ARRAY_SIZE;
198 //
199 // re-allocate a new buffer and copy the old values to the new locaiton.
200 //
201 TempArray = mWinNt->HeapAlloc ( mWinNt->GetProcessHeap (),
202 HEAP_ZERO_MEMORY,
203 mPdbNameModHandleArraySize * sizeof (PDB_NAME_TO_MOD_HANDLE)
204 );
205
206 CopyMem ((VOID *) (UINTN) TempArray, (VOID *) (UINTN)mPdbNameModHandleArray, PreviousSize);
207
208 mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, mPdbNameModHandleArray);
209
210 mPdbNameModHandleArray = TempArray;
211 if (mPdbNameModHandleArray == NULL) {
212 ASSERT (FALSE);
213 return EFI_OUT_OF_RESOURCES;
214 }
215
216
217 return AddModHandle (ImageContext, ModHandle);
218 }
219 /**
220 Return the ModHandle and delete the entry in the array.
221
222
223 @param ImageContext - Input data returned from PE Laoder Library. Used to find the
224 .PDB file name of the PE Image.
225
226 @return
227 ModHandle - ModHandle assoicated with ImageContext is returned
228 NULL - No ModHandle associated with ImageContext
229
230 **/
231 VOID *
232 RemoveModeHandle (
233 IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
234 )
235 {
236 UINTN Index;
237 PDB_NAME_TO_MOD_HANDLE *Array;
238
239 if (ImageContext->PdbPointer == NULL) {
240 //
241 // If no PDB pointer there is no ModHandle so return NULL
242 //
243 return NULL;
244 }
245
246 Array = mPdbNameModHandleArray;
247 for (Index = 0; Index < mPdbNameModHandleArraySize; Index++, Array++) {
248 if ((Array->PdbPointer != NULL) && (AsciiStrCmp(Array->PdbPointer, ImageContext->PdbPointer) == 0)) {
249 //
250 // If you find a match return it and delete the entry
251 //
252 mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, Array->PdbPointer);
253 Array->PdbPointer = NULL;
254 return Array->ModHandle;
255 }
256 }
257
258 return NULL;
259 }
260
261 /**
262 Performs additional actions after a PE/COFF image has been loaded and relocated.
263
264 For NT32, this function load symbols to support source level debugging.
265
266 If ImageContext is NULL, then ASSERT().
267
268 @param ImageContext Pointer to the image context structure that describes the
269 PE/COFF image that has already been loaded and relocated.
270
271 **/
272 VOID
273 EFIAPI
274 PeCoffLoaderRelocateImageExtraAction (
275 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
276 )
277 {
278 EFI_STATUS Status;
279 VOID *DllEntryPoint;
280 CHAR16 *DllFileName;
281 HMODULE Library;
282 UINTN Index;
283
284 ASSERT (ImageContext != NULL);
285
286 if (mWinNt == NULL) {
287 return;
288 }
289
290 //
291 // If we load our own PE COFF images the Windows debugger can not source
292 // level debug our code. If a valid PDB pointer exists usw it to load
293 // the *.dll file as a library using Windows* APIs. This allows
294 // source level debug. The image is still loaded and reloaced
295 // in the Framework memory space like on a real system (by the code above),
296 // but the entry point points into the DLL loaded by the code bellow.
297 //
298
299 DllEntryPoint = NULL;
300
301 //
302 // Load the DLL if it's not an EBC image.
303 //
304 if ((ImageContext->PdbPointer != NULL) &&
305 (ImageContext->Machine != EFI_IMAGE_MACHINE_EBC)) {
306 //
307 // Convert filename from ASCII to Unicode
308 //
309 DllFileName = AsciiToUnicode (ImageContext->PdbPointer, &Index);
310
311 //
312 // Check that we have a valid filename
313 //
314 if (Index < 5 || DllFileName[Index - 4] != '.') {
315 mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, DllFileName);
316
317 //
318 // Never return an error if PeCoffLoaderRelocateImage() succeeded.
319 // The image will run, but we just can't source level debug. If we
320 // return an error the image will not run.
321 //
322 return;
323 }
324 //
325 // Replace .PDB with .DLL on the filename
326 //
327 DllFileName[Index - 3] = 'D';
328 DllFileName[Index - 2] = 'L';
329 DllFileName[Index - 1] = 'L';
330
331 //
332 // Load the .DLL file into the user process's address space for source
333 // level debug
334 //
335 Library = mWinNt->LoadLibraryEx (DllFileName, NULL, DONT_RESOLVE_DLL_REFERENCES);
336 if (Library != NULL) {
337 //
338 // InitializeDriver is the entry point we put in all our EFI DLL's. The
339 // DONT_RESOLVE_DLL_REFERENCES argument to LoadLIbraryEx() supresses the
340 // normal DLL entry point of DllMain, and prevents other modules that are
341 // referenced in side the DllFileName from being loaded. There is no error
342 // checking as the we can point to the PE32 image loaded by Tiano. This
343 // step is only needed for source level debuging
344 //
345 DllEntryPoint = (VOID *) (UINTN) mWinNt->GetProcAddress (Library, "InitializeDriver");
346
347 }
348
349 if ((Library != NULL) && (DllEntryPoint != NULL)) {
350 Status = AddModHandle (ImageContext, Library);
351 if (Status == EFI_ALREADY_STARTED) {
352 //
353 // If the DLL has already been loaded before, then this instance of the DLL can not be debugged.
354 //
355 ImageContext->PdbPointer = NULL;
356 DEBUG ((EFI_D_ERROR, "WARNING: DLL already loaded. No source level debug %s. \n", DllFileName));
357 } else {
358 //
359 // This DLL is not already loaded, so source level debugging is suported.
360 //
361 ImageContext->EntryPoint = (EFI_PHYSICAL_ADDRESS) (UINTN) DllEntryPoint;
362 DEBUG ((EFI_D_INFO, "LoadLibraryEx (%s,\n NULL, DONT_RESOLVE_DLL_REFERENCES)\n", DllFileName));
363 }
364 } else {
365 //
366 // This DLL does not support source level debugging at all.
367 //
368 DEBUG ((EFI_D_ERROR, "WARNING: No source level debug %s. \n", DllFileName));
369 }
370
371 mWinNt->HeapFree (mWinNt->GetProcessHeap (), 0, DllFileName);
372 }
373
374 //
375 // Never return an error if PeCoffLoaderRelocateImage() succeeded.
376 // The image will run, but we just can't source level debug. If we
377 // return an error the image will not run.
378 //
379 return;
380 }
381
382 /**
383 Performs additional actions just before a PE/COFF image is unloaded. Any resources
384 that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed.
385
386 For NT32, this function unloads symbols for source level debugging.
387
388 If ImageContext is NULL, then ASSERT().
389
390 @param ImageContext Pointer to the image context structure that describes the
391 PE/COFF image that is being unloaded.
392
393 **/
394 VOID
395 EFIAPI
396 PeCoffLoaderUnloadImageExtraAction (
397 IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
398 )
399 {
400 VOID *ModHandle;
401
402 ASSERT (ImageContext != NULL);
403 if (mWinNt == NULL) {
404 return;
405 }
406
407 ModHandle = RemoveModeHandle (ImageContext);
408 if (ModHandle != NULL) {
409 mWinNt->FreeLibrary (ModHandle);
410 }
411 return;
412 }