* Sync to trunk HEAD (r53298).
[reactos.git] / dll / ntdll / ldr / ldrapi.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User Mode Library
4 * FILE: dll/ntdll/ldr/ldrapi.c
5 * PURPOSE: PE Loader Public APIs
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Aleksey Bragin (aleksey@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 LIST_ENTRY LdrpUnloadHead;
19 LONG LdrpLoaderLockAcquisitonCount;
20 BOOLEAN LdrpShowRecursiveLoads, LdrpBreakOnRecursiveDllLoads;
21 UNICODE_STRING LdrApiDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
22
23 /* FUNCTIONS *****************************************************************/
24
25 ULONG_PTR
26 FORCEINLINE
27 LdrpMakeCookie(VOID)
28 {
29 /* Generate a cookie */
30 return (((ULONG_PTR)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) |
31 _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
32 }
33
34 /*
35 * @implemented
36 */
37 NTSTATUS
38 NTAPI
39 LdrUnlockLoaderLock(IN ULONG Flags,
40 IN ULONG Cookie OPTIONAL)
41 {
42 NTSTATUS Status = STATUS_SUCCESS;
43
44 DPRINT("LdrUnlockLoaderLock(%x %x)\n", Flags, Cookie);
45
46 /* Check for valid flags */
47 if (Flags & ~LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
48 {
49 /* Flags are invalid, check how to fail */
50 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
51 {
52 /* The caller wants us to raise status */
53 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
54 }
55
56 /* A normal failure */
57 return STATUS_INVALID_PARAMETER_1;
58 }
59
60 /* If we don't have a cookie, just return */
61 if (!Cookie) return STATUS_SUCCESS;
62
63 /* Validate the cookie */
64 if ((Cookie & 0xF0000000) ||
65 ((Cookie >> 16) ^ ((ULONG)(NtCurrentTeb()->RealClientId.UniqueThread) & 0xFFF)))
66 {
67 DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
68
69 /* Invalid cookie, check how to fail */
70 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
71 {
72 /* The caller wants us to raise status */
73 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
74 }
75
76 /* A normal failure */
77 return STATUS_INVALID_PARAMETER_2;
78 }
79
80 /* Ready to release the lock */
81 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
82 {
83 /* Do a direct leave */
84 RtlLeaveCriticalSection(&LdrpLoaderLock);
85 }
86 else
87 {
88 /* Wrap this in SEH, since we're not supposed to raise */
89 _SEH2_TRY
90 {
91 /* Leave the lock */
92 RtlLeaveCriticalSection(&LdrpLoaderLock);
93 }
94 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
95 {
96 /* We should use the LDR Filter instead */
97 Status = _SEH2_GetExceptionCode();
98 }
99 _SEH2_END;
100 }
101
102 /* All done */
103 return Status;
104 }
105
106 /*
107 * @implemented
108 */
109 NTSTATUS
110 NTAPI
111 LdrLockLoaderLock(IN ULONG Flags,
112 OUT PULONG Disposition OPTIONAL,
113 OUT PULONG_PTR Cookie OPTIONAL)
114 {
115 NTSTATUS Status = STATUS_SUCCESS;
116 BOOLEAN InInit = LdrpInLdrInit;
117
118 DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags, Disposition, Cookie);
119
120 /* Zero out the outputs */
121 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_INVALID;
122 if (Cookie) *Cookie = 0;
123
124 /* Validate the flags */
125 if (Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS |
126 LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY))
127 {
128 /* Flags are invalid, check how to fail */
129 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
130 {
131 /* The caller wants us to raise status */
132 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
133 }
134
135 /* A normal failure */
136 return STATUS_INVALID_PARAMETER_1;
137 }
138
139 /* Make sure we got a cookie */
140 if (!Cookie)
141 {
142 /* No cookie check how to fail */
143 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
144 {
145 /* The caller wants us to raise status */
146 RtlRaiseStatus(STATUS_INVALID_PARAMETER_3);
147 }
148
149 /* A normal failure */
150 return STATUS_INVALID_PARAMETER_3;
151 }
152
153 /* Do or Do Not. There is no Try */
154 ASSERT((Disposition != NULL) || !(Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY));
155
156 /* If the flag is set, make sure we have a valid pointer to use */
157 if ((Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) && !(Disposition))
158 {
159 /* No pointer to return the data to */
160 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
161 {
162 /* The caller wants us to raise status */
163 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
164 }
165
166 /* Fail */
167 return STATUS_INVALID_PARAMETER_2;
168 }
169
170 /* Return now if we are in the init phase */
171 if (InInit) return STATUS_SUCCESS;
172
173 /* Check what locking semantic to use */
174 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
175 {
176 /* Check if we should enter or simply try */
177 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
178 {
179 /* Do a try */
180 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
181 {
182 /* It's locked */
183 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
184 }
185 else
186 {
187 /* It worked */
188 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
189 *Cookie = LdrpMakeCookie();
190 }
191 }
192 else
193 {
194 /* Do a enter */
195 RtlEnterCriticalSection(&LdrpLoaderLock);
196
197 /* See if result was requested */
198 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
199 *Cookie = LdrpMakeCookie();
200 }
201 }
202 else
203 {
204 /* Wrap this in SEH, since we're not supposed to raise */
205 _SEH2_TRY
206 {
207 /* Check if we should enter or simply try */
208 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
209 {
210 /* Do a try */
211 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
212 {
213 /* It's locked */
214 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
215 }
216 else
217 {
218 /* It worked */
219 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
220 *Cookie = LdrpMakeCookie();
221 }
222 }
223 else
224 {
225 /* Do an enter */
226 RtlEnterCriticalSection(&LdrpLoaderLock);
227
228 /* See if result was requested */
229 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
230 *Cookie = LdrpMakeCookie();
231 }
232 }
233 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
234 {
235 /* We should use the LDR Filter instead */
236 Status = _SEH2_GetExceptionCode();
237 }
238 _SEH2_END;
239 }
240
241 /* Return status */
242 return Status;
243 }
244
245 /*
246 * @implemented
247 */
248 NTSTATUS
249 NTAPI
250 LdrLoadDll(IN PWSTR SearchPath OPTIONAL,
251 IN PULONG DllCharacteristics OPTIONAL,
252 IN PUNICODE_STRING DllName,
253 OUT PVOID *BaseAddress)
254 {
255 WCHAR StringBuffer[MAX_PATH];
256 UNICODE_STRING DllString1, DllString2;
257 BOOLEAN RedirectedDll = FALSE;
258 NTSTATUS Status;
259 ULONG Cookie;
260 PUNICODE_STRING OldTldDll;
261 PTEB Teb = NtCurrentTeb();
262
263 /* Initialize the strings */
264 RtlInitEmptyUnicodeString(&DllString2, NULL, 0);
265 DllString1.Buffer = StringBuffer;
266 DllString1.Length = 0;
267 DllString1.MaximumLength = sizeof(StringBuffer);
268
269 /* Check if the SxS Assemblies specify another file */
270 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
271 DllName,
272 &LdrApiDefaultExtension,
273 &DllString1,
274 &DllString2,
275 &DllName,
276 NULL,
277 NULL,
278 NULL);
279
280 /* Check success */
281 if (NT_SUCCESS(Status))
282 {
283 /* Let Ldrp know */
284 RedirectedDll = TRUE;
285 }
286 else if (Status != STATUS_SXS_KEY_NOT_FOUND)
287 {
288 /* Unrecoverable SxS failure; did we get a string? */
289 if (DllString2.Buffer) RtlFreeUnicodeString(&DllString2);
290 return Status;
291 }
292
293 /* Lock the loader lock */
294 LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &Cookie);
295
296 /* Check if there's a TLD DLL being loaded */
297 OldTldDll = LdrpTopLevelDllBeingLoaded;
298 if (OldTldDll)
299 {
300 /* This is a recursive load, do something about it? */
301 if ((ShowSnaps) || (LdrpShowRecursiveLoads) || (LdrpBreakOnRecursiveDllLoads))
302 {
303 /* Print out debug messages */
304 DPRINT1("[%lx, %lx] LDR: Recursive DLL Load\n",
305 Teb->RealClientId.UniqueProcess,
306 Teb->RealClientId.UniqueThread);
307 DPRINT1("[%lx, %lx] Previous DLL being loaded \"%wZ\"\n",
308 Teb->RealClientId.UniqueProcess,
309 Teb->RealClientId.UniqueThread,
310 OldTldDll);
311 DPRINT1("[%lx, %lx] DLL being requested \"%wZ\"\n",
312 Teb->RealClientId.UniqueProcess,
313 Teb->RealClientId.UniqueThread,
314 DllName);
315
316 /* Was it initializing too? */
317 if (!LdrpCurrentDllInitializer)
318 {
319 DPRINT1("[%lx, %lx] LDR: No DLL Initializer was running\n",
320 Teb->RealClientId.UniqueProcess,
321 Teb->RealClientId.UniqueThread);
322 }
323 else
324 {
325 DPRINT1("[%lx, %lx] DLL whose initializer was currently running \"%wZ\"\n",
326 Teb->ClientId.UniqueProcess,
327 Teb->ClientId.UniqueThread,
328 &LdrpCurrentDllInitializer->BaseDllName);
329 }
330 }
331 }
332
333 /* Set this one as the TLD DLL being loaded*/
334 LdrpTopLevelDllBeingLoaded = DllName;
335
336 /* Load the DLL */
337 Status = LdrpLoadDll(RedirectedDll,
338 SearchPath,
339 DllCharacteristics,
340 DllName,
341 BaseAddress,
342 TRUE);
343 if (NT_SUCCESS(Status))
344 {
345 Status = STATUS_SUCCESS;
346 }
347 else if ((Status != STATUS_NO_SUCH_FILE) &&
348 (Status != STATUS_DLL_NOT_FOUND) &&
349 (Status != STATUS_OBJECT_NAME_NOT_FOUND) &&
350 (Status != STATUS_DLL_INIT_FAILED))
351 {
352 // 85 == DPFLTR_LDR_ID;
353 DbgPrintEx(85,
354 DPFLTR_WARNING_LEVEL,
355 "LDR: %s - failing because LdrpLoadDll(%wZ) returned status %x\n",
356 __FUNCTION__,
357 DllName,
358 Status);
359 }
360
361 /* Restore the old TLD DLL */
362 LdrpTopLevelDllBeingLoaded = OldTldDll;
363
364 /* Release the lock */
365 LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
366
367 /* Do we have a redirect string? */
368 if (DllString2.Buffer) RtlFreeUnicodeString(&DllString2);
369
370 /* Return */
371 return Status;
372 }
373
374 /*
375 * @implemented
376 */
377 NTSTATUS
378 NTAPI
379 LdrFindEntryForAddress(PVOID Address,
380 PLDR_DATA_TABLE_ENTRY *Module)
381 {
382 PLIST_ENTRY ListHead, NextEntry;
383 PLDR_DATA_TABLE_ENTRY LdrEntry;
384 PIMAGE_NT_HEADERS NtHeader;
385 PPEB_LDR_DATA Ldr = NtCurrentPeb()->Ldr;
386 ULONG_PTR DllBase, DllEnd;
387
388 DPRINT("LdrFindEntryForAddress(Address %p)\n", Address);
389
390 /* Nothing to do */
391 if (!Ldr) return STATUS_NO_MORE_ENTRIES;
392
393 /* Get the current entry */
394 LdrEntry = Ldr->EntryInProgress;
395 if (LdrEntry)
396 {
397 /* Get the NT Headers */
398 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
399 if (NtHeader)
400 {
401 /* Get the Image Base */
402 DllBase = (ULONG_PTR)LdrEntry->DllBase;
403 DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
404
405 /* Check if they match */
406 if (((ULONG_PTR)Address >= DllBase) &&
407 ((ULONG_PTR)Address < DllEnd))
408 {
409 /* Return it */
410 *Module = LdrEntry;
411 return STATUS_SUCCESS;
412 }
413 }
414 }
415
416 /* Loop the module list */
417 ListHead = &Ldr->InMemoryOrderModuleList;
418 NextEntry = ListHead->Flink;
419 while (NextEntry != ListHead)
420 {
421 /* Get the entry and NT Headers */
422 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList);
423 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
424 if (NtHeader)
425 {
426 /* Get the Image Base */
427 DllBase = (ULONG_PTR)LdrEntry->DllBase;
428 DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
429
430 /* Check if they match */
431 if (((ULONG_PTR)Address >= DllBase) &&
432 ((ULONG_PTR)Address < DllEnd))
433 {
434 /* Return it */
435 *Module = LdrEntry;
436 return STATUS_SUCCESS;
437 }
438
439 /* Next Entry */
440 NextEntry = NextEntry->Flink;
441 }
442 }
443
444 /* Nothing found */
445 // 85 == DPFLTR_LDR_ID;
446 DbgPrintEx(85, DPFLTR_WARNING_LEVEL, "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, STATUS_NO_MORE_ENTRIES);
447 return STATUS_NO_MORE_ENTRIES;
448 }
449
450 /*
451 * @implemented
452 */
453 NTSTATUS
454 NTAPI
455 LdrGetDllHandleEx(IN ULONG Flags,
456 IN PWSTR DllPath OPTIONAL,
457 IN PULONG DllCharacteristics OPTIONAL,
458 IN PUNICODE_STRING DllName,
459 OUT PVOID *DllHandle OPTIONAL)
460 {
461 NTSTATUS Status;
462 PLDR_DATA_TABLE_ENTRY LdrEntry;
463 UNICODE_STRING RedirectName, DllString1, RawDllName;
464 PUNICODE_STRING pRedirectName, CompareName;
465 PWCHAR p1, p2, p3;
466 BOOLEAN Locked, RedirectedDll;
467 ULONG_PTR Cookie;
468 ULONG LoadFlag, Length;
469
470 /* Initialize the strings */
471 RtlInitEmptyUnicodeString(&DllString1, NULL, 0);
472 RtlInitEmptyUnicodeString(&RawDllName, NULL, 0);
473 RedirectName = *DllName;
474 pRedirectName = &RedirectName;
475
476 /* Initialize state */
477 RedirectedDll = Locked = FALSE;
478 LdrEntry = NULL;
479 Cookie = 0;
480
481 /* Clear the handle */
482 if (DllHandle) *DllHandle = NULL;
483
484 /* Check for a valid flag combination */
485 if ((Flags & ~(LDR_GET_DLL_HANDLE_EX_PIN | LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT)) ||
486 (!DllHandle && !(Flags & LDR_GET_DLL_HANDLE_EX_PIN)))
487 {
488 DPRINT1("Flags are invalid or no DllHandle given\n");
489 Status = STATUS_INVALID_PARAMETER;
490 goto Quickie;
491 }
492
493 /* If not initializing */
494 if (!LdrpInLdrInit)
495 {
496 /* Acquire the lock */
497 Status = LdrLockLoaderLock(0, NULL, &Cookie);
498 if (!NT_SUCCESS(Status)) goto Quickie;
499
500 /* Remember we own it */
501 Locked = TRUE;
502 }
503
504 /* Check if the SxS Assemblies specify another file */
505 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
506 pRedirectName,
507 &LdrApiDefaultExtension,
508 NULL,
509 &DllString1,
510 &pRedirectName,
511 NULL,
512 NULL,
513 NULL);
514
515 /* Check success */
516 if (NT_SUCCESS(Status))
517 {
518 /* Let Ldrp know */
519 RedirectedDll = TRUE;
520 }
521 else if (Status != STATUS_SXS_KEY_NOT_FOUND)
522 {
523 /* Unrecoverable SxS failure; */
524 goto Quickie;
525 }
526 else
527 {
528 ASSERT(pRedirectName == &RedirectName);
529 }
530
531 /* Set default failure code */
532 Status = STATUS_DLL_NOT_FOUND;
533
534 /* Use the cache if we can */
535 if (LdrpGetModuleHandleCache)
536 {
537 /* Check if we were redirected */
538 if (RedirectedDll)
539 {
540 /* Check the flag */
541 if (!(LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED))
542 {
543 goto DontCompare;
544 }
545
546 /* Use the right name */
547 CompareName = &LdrpGetModuleHandleCache->FullDllName;
548 }
549 else
550 {
551 /* Check the flag */
552 if (LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED)
553 {
554 goto DontCompare;
555 }
556
557 /* Use the right name */
558 CompareName = &LdrpGetModuleHandleCache->BaseDllName;
559 }
560
561 /* Check if the name matches */
562 if (RtlEqualUnicodeString(pRedirectName,
563 CompareName,
564 TRUE))
565 {
566 /* Skip the rest */
567 LdrEntry = LdrpGetModuleHandleCache;
568
569 /* Return success */
570 Status = STATUS_SUCCESS;
571 goto Quickie;
572 }
573 }
574
575 DontCompare:
576 /* Find the name without the extension */
577 p1 = pRedirectName->Buffer;
578 p2 = NULL;
579 p3 = &p1[pRedirectName->Length / sizeof(WCHAR)];
580 while (p1 != p3)
581 {
582 if (*p1++ == L'.')
583 {
584 p2 = p1;
585 }
586 else if (*p1 == L'\\')
587 {
588 p2 = NULL;
589 }
590 }
591
592 /* Check if no extension was found or if we got a slash */
593 if (!(p2) || (*p2 == L'\\') || (*p2 == L'/'))
594 {
595 /* Check that we have space to add one */
596 Length = pRedirectName->Length +
597 LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL);
598 if (Length >= UNICODE_STRING_MAX_BYTES)
599 {
600 /* No space to add the extension */
601 Status = STATUS_NAME_TOO_LONG;
602 goto Quickie;
603 }
604
605 /* Setup the string */
606 RawDllName.MaximumLength = Length;
607 ASSERT(Length >= sizeof(UNICODE_NULL));
608 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
609 0,
610 RawDllName.MaximumLength);
611 if (!RawDllName.Buffer)
612 {
613 Status = STATUS_NO_MEMORY;
614 goto Quickie;
615 }
616
617 /* Copy the string and add extension */
618 RtlCopyUnicodeString(&RawDllName, pRedirectName);
619 RtlAppendUnicodeStringToString(&RawDllName, &LdrApiDefaultExtension);
620 }
621 else
622 {
623 /* Check if there's something in the name */
624 Length = pRedirectName->Length;
625 if (Length)
626 {
627 /* Check and remove trailing period */
628 if (pRedirectName->Buffer[Length / sizeof(WCHAR) - sizeof(ANSI_NULL)] == '.')
629 {
630 /* Decrease the size */
631 pRedirectName->Length -= sizeof(WCHAR);
632 }
633 }
634
635 /* Setup the string */
636 RawDllName.MaximumLength = pRedirectName->Length + sizeof(WCHAR);
637 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
638 0,
639 RawDllName.MaximumLength);
640 if (!RawDllName.Buffer)
641 {
642 Status = STATUS_NO_MEMORY;
643 goto Quickie;
644 }
645
646 /* Copy the string */
647 RtlCopyUnicodeString(&RawDllName, pRedirectName);
648 }
649
650 /* Display debug string */
651 if (ShowSnaps)
652 {
653 DPRINT1("LDR: LdrGetDllHandleEx, searching for %wZ from %ws\n",
654 &RawDllName,
655 DllPath ? ((ULONG_PTR)DllPath == 1 ? L"" : DllPath) : L"");
656 }
657
658 /* Do the lookup */
659 if (LdrpCheckForLoadedDll(DllPath,
660 &RawDllName,
661 ((ULONG_PTR)DllPath == 1) ? TRUE : FALSE,
662 RedirectedDll,
663 &LdrEntry))
664 {
665 /* Update cached entry */
666 LdrpGetModuleHandleCache = LdrEntry;
667
668 /* Return success */
669 Status = STATUS_SUCCESS;
670 }
671 else
672 {
673 /* Make sure to NULL this */
674 LdrEntry = NULL;
675 }
676 Quickie:
677 /* The success path must have a valid loader entry */
678 ASSERT((LdrEntry != NULL) == NT_SUCCESS(Status));
679
680 /* Check if we got an entry and success */
681 DPRINT("Got LdrEntry->BaseDllName %wZ\n", LdrEntry ? &LdrEntry->BaseDllName : NULL);
682 if ((LdrEntry) && (NT_SUCCESS(Status)))
683 {
684 /* Check if the DLL is locked */
685 if ((LdrEntry->LoadCount != 0xFFFF) &&
686 !(Flags & LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT))
687 {
688 /* Check what to do with the load count */
689 if (Flags & LDR_GET_DLL_HANDLE_EX_PIN)
690 {
691 /* Pin it */
692 LdrEntry->LoadCount = 0xFFFF;
693 LoadFlag = LDRP_UPDATE_PIN;
694 }
695 else
696 {
697 /* Increase the load count */
698 LdrEntry->LoadCount++;
699 LoadFlag = LDRP_UPDATE_REFCOUNT;
700 }
701
702 /* Update the load count now */
703 LdrpUpdateLoadCount2(LdrEntry, LoadFlag);
704 LdrpClearLoadInProgress();
705 }
706
707 /* Check if the caller is requesting the handle */
708 if (DllHandle) *DllHandle = LdrEntry->DllBase;
709 }
710
711 /* Free string if needed */
712 if (DllString1.Buffer) RtlFreeUnicodeString(&DllString1);
713
714 /* Free the raw DLL Name if needed */
715 if (RawDllName.Buffer)
716 {
717 /* Free the heap-allocated buffer */
718 RtlFreeHeap(RtlGetProcessHeap(), 0, RawDllName.Buffer);
719 RawDllName.Buffer = NULL;
720 }
721
722 /* Release lock */
723 if (Locked)
724 {
725 LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS,
726 Cookie);
727 }
728
729 /* Return */
730 return Status;
731 }
732
733 /*
734 * @implemented
735 */
736 NTSTATUS
737 NTAPI
738 LdrGetDllHandle(IN PWSTR DllPath OPTIONAL,
739 IN PULONG DllCharacteristics OPTIONAL,
740 IN PUNICODE_STRING DllName,
741 OUT PVOID *DllHandle)
742 {
743 /* Call the newer API */
744 return LdrGetDllHandleEx(LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT,
745 DllPath,
746 DllCharacteristics,
747 DllName,
748 DllHandle);
749 }
750
751 /*
752 * @implemented
753 */
754 NTSTATUS
755 NTAPI
756 LdrGetProcedureAddress(IN PVOID BaseAddress,
757 IN PANSI_STRING Name,
758 IN ULONG Ordinal,
759 OUT PVOID *ProcedureAddress)
760 {
761 /* Call the internal routine and tell it to execute DllInit */
762 return LdrpGetProcedureAddress(BaseAddress, Name, Ordinal, ProcedureAddress, TRUE);
763 }
764
765 /*
766 * @implemented
767 */
768 NTSTATUS
769 NTAPI
770 LdrVerifyImageMatchesChecksum(IN HANDLE FileHandle,
771 IN PLDR_CALLBACK Callback,
772 IN PVOID CallbackContext,
773 OUT PUSHORT ImageCharacteristics)
774 {
775 FILE_STANDARD_INFORMATION FileStandardInfo;
776 PIMAGE_IMPORT_DESCRIPTOR ImportData;
777 PIMAGE_SECTION_HEADER LastSection;
778 IO_STATUS_BLOCK IoStatusBlock;
779 PIMAGE_NT_HEADERS NtHeader;
780 HANDLE SectionHandle;
781 SIZE_T ViewSize;
782 PVOID ViewBase;
783 BOOLEAN Result, NoActualCheck;
784 NTSTATUS Status;
785 PVOID ImportName;
786 ULONG Size;
787 DPRINT("LdrVerifyImageMatchesChecksum() called\n");
788
789 /* If the handle has the magic KnownDll flag, skip actual checksums */
790 NoActualCheck = ((ULONG_PTR)FileHandle & 1);
791
792 /* Create the section */
793 Status = NtCreateSection(&SectionHandle,
794 SECTION_MAP_EXECUTE,
795 NULL,
796 NULL,
797 PAGE_EXECUTE,
798 SEC_COMMIT,
799 FileHandle);
800 if (!NT_SUCCESS(Status))
801 {
802 DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status);
803 return Status;
804 }
805
806 /* Map the section */
807 ViewSize = 0;
808 ViewBase = NULL;
809 Status = NtMapViewOfSection(SectionHandle,
810 NtCurrentProcess(),
811 &ViewBase,
812 0,
813 0,
814 NULL,
815 &ViewSize,
816 ViewShare,
817 0,
818 PAGE_EXECUTE);
819 if (!NT_SUCCESS(Status))
820 {
821 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
822 NtClose(SectionHandle);
823 return Status;
824 }
825
826 /* Get the file information */
827 Status = NtQueryInformationFile(FileHandle,
828 &IoStatusBlock,
829 &FileStandardInfo,
830 sizeof(FILE_STANDARD_INFORMATION),
831 FileStandardInformation);
832 if (!NT_SUCCESS(Status))
833 {
834 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
835 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
836 NtClose(SectionHandle);
837 return Status;
838 }
839
840 /* Protect with SEH */
841 _SEH2_TRY
842 {
843 /* Check if this is the KnownDll hack */
844 if (NoActualCheck)
845 {
846 /* Don't actually do it */
847 Result = TRUE;
848 }
849 else
850 {
851 /* Verify the checksum */
852 Result = LdrVerifyMappedImageMatchesChecksum(ViewBase,
853 FileStandardInfo.EndOfFile.LowPart,
854 FileStandardInfo.EndOfFile.LowPart);
855 }
856
857 /* Check if a callback was supplied */
858 if ((Result) && (Callback))
859 {
860 /* Get the NT Header */
861 NtHeader = RtlImageNtHeader(ViewBase);
862
863 /* Check if caller requested this back */
864 if (ImageCharacteristics)
865 {
866 /* Return to caller */
867 *ImageCharacteristics = NtHeader->FileHeader.Characteristics;
868 }
869
870 /* Get the Import Directory Data */
871 ImportData = RtlImageDirectoryEntryToData(ViewBase,
872 FALSE,
873 IMAGE_DIRECTORY_ENTRY_IMPORT,
874 &Size);
875
876 /* Make sure there is one */
877 if (ImportData)
878 {
879 /* Loop the imports */
880 while (ImportData->Name)
881 {
882 /* Get the name */
883 ImportName = RtlImageRvaToVa(NtHeader,
884 ViewBase,
885 ImportData->Name,
886 &LastSection);
887
888 /* Notify the callback */
889 Callback(CallbackContext, ImportName);
890 ImportData++;
891 }
892 }
893 }
894 }
895 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
896 {
897 /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
898 Result = FALSE;
899 }
900 _SEH2_END;
901
902 /* Unmap file and close handle */
903 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
904 NtClose(SectionHandle);
905
906 /* Return status */
907 return Result ? Status : STATUS_IMAGE_CHECKSUM_MISMATCH;
908 }
909
910 NTSTATUS
911 NTAPI
912 LdrQueryProcessModuleInformationEx(IN ULONG ProcessId,
913 IN ULONG Reserved,
914 IN PRTL_PROCESS_MODULES ModuleInformation,
915 IN ULONG Size,
916 OUT PULONG ReturnedSize OPTIONAL)
917 {
918 PLIST_ENTRY ModuleListHead, InitListHead;
919 PLIST_ENTRY Entry, InitEntry;
920 PLDR_DATA_TABLE_ENTRY Module, InitModule;
921 PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL;
922 NTSTATUS Status = STATUS_SUCCESS;
923 ULONG UsedSize = sizeof(ULONG);
924 ANSI_STRING AnsiString;
925 PCHAR p;
926
927 DPRINT("LdrQueryProcessModuleInformation() called\n");
928
929 /* Acquire loader lock */
930 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
931
932 /* Check if we were given enough space */
933 if (Size < UsedSize)
934 {
935 Status = STATUS_INFO_LENGTH_MISMATCH;
936 }
937 else
938 {
939 ModuleInformation->NumberOfModules = 0;
940 ModulePtr = &ModuleInformation->Modules[0];
941 Status = STATUS_SUCCESS;
942 }
943
944 /* Traverse the list of modules */
945 _SEH2_TRY
946 {
947 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
948 Entry = ModuleListHead->Flink;
949
950 while (Entry != ModuleListHead)
951 {
952 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
953
954 DPRINT(" Module %wZ\n", &Module->FullDllName);
955
956 /* Increase the used size */
957 UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION);
958
959 if (UsedSize > Size)
960 {
961 Status = STATUS_INFO_LENGTH_MISMATCH;
962 }
963 else
964 {
965 ModulePtr->ImageBase = Module->DllBase;
966 ModulePtr->ImageSize = Module->SizeOfImage;
967 ModulePtr->Flags = Module->Flags;
968 ModulePtr->LoadCount = Module->LoadCount;
969 ModulePtr->MappedBase = NULL;
970 ModulePtr->InitOrderIndex = 0;
971 ModulePtr->LoadOrderIndex = ModuleInformation->NumberOfModules;
972
973 /* Now get init order index by traversing init list */
974 InitListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
975 InitEntry = InitListHead->Flink;
976
977 while (InitEntry != InitListHead)
978 {
979 InitModule = CONTAINING_RECORD(InitEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
980
981 /* Increase the index */
982 ModulePtr->InitOrderIndex++;
983
984 /* Quit the loop if our module is found */
985 if (InitModule == Module) break;
986
987 /* Advance to the next entry */
988 InitEntry = InitEntry->Flink;
989 }
990
991 /* Prepare ANSI string with the module's name */
992 AnsiString.Length = 0;
993 AnsiString.MaximumLength = sizeof(ModulePtr->FullPathName);
994 AnsiString.Buffer = ModulePtr->FullPathName;
995 RtlUnicodeStringToAnsiString(&AnsiString,
996 &Module->FullDllName,
997 FALSE);
998
999 /* Calculate OffsetToFileName field */
1000 p = strrchr(ModulePtr->FullPathName, '\\');
1001 if (p != NULL)
1002 ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1;
1003 else
1004 ModulePtr->OffsetToFileName = 0;
1005
1006 /* Advance to the next module in the output list */
1007 ModulePtr++;
1008
1009 /* Increase number of modules */
1010 if (ModuleInformation)
1011 ModuleInformation->NumberOfModules++;
1012 }
1013
1014 /* Go to the next entry in the modules list */
1015 Entry = Entry->Flink;
1016 }
1017
1018 /* Set returned size if it was provided */
1019 if (ReturnedSize)
1020 *ReturnedSize = UsedSize;
1021 }
1022 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1023 {
1024 /* Ignoring the exception */
1025 } _SEH2_END;
1026
1027 /* Release the lock */
1028 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
1029
1030 DPRINT("LdrQueryProcessModuleInformation() done\n");
1031
1032 return Status;
1033 }
1034
1035 /*
1036 * @implemented
1037 */
1038 NTSTATUS
1039 NTAPI
1040 LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation,
1041 IN ULONG Size,
1042 OUT PULONG ReturnedSize OPTIONAL)
1043 {
1044 /* Call Ex version of the API */
1045 return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation, Size, ReturnedSize);
1046 }
1047
1048 /*
1049 * @implemented
1050 */
1051 NTSTATUS
1052 NTAPI
1053 LdrEnumerateLoadedModules(IN BOOLEAN ReservedFlag,
1054 IN PLDR_ENUM_CALLBACK EnumProc,
1055 IN PVOID Context)
1056 {
1057 PLIST_ENTRY ListHead, ListEntry;
1058 PLDR_DATA_TABLE_ENTRY LdrEntry;
1059 NTSTATUS Status;
1060 ULONG Cookie;
1061 BOOLEAN Stop = FALSE;
1062
1063 /* Check parameters */
1064 if ((ReservedFlag) || !(EnumProc)) return STATUS_INVALID_PARAMETER;
1065
1066 /* Acquire the loader lock */
1067 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1068 if (!NT_SUCCESS(Status)) return Status;
1069
1070 /* Loop all the modules and call enum proc */
1071 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1072 ListEntry = ListHead->Flink;
1073 while (ListHead != ListEntry)
1074 {
1075 /* Get the entry */
1076 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1077
1078 /* Call the enumeration proc inside SEH */
1079 _SEH2_TRY
1080 {
1081 EnumProc(LdrEntry, Context, &Stop);
1082 }
1083 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1084 {
1085 /* Ignoring the exception */
1086 } _SEH2_END;
1087
1088 /* Break if we were asked to stop enumeration */
1089 if (Stop)
1090 {
1091 /* Release loader lock */
1092 Status = LdrUnlockLoaderLock(0, Cookie);
1093
1094 /* Reset any successful status to STATUS_SUCCESS, but leave
1095 failure to the caller */
1096 if (NT_SUCCESS(Status))
1097 Status = STATUS_SUCCESS;
1098
1099 /* Return any possible failure status */
1100 return Status;
1101 }
1102
1103 /* Advance to the next module */
1104 ListEntry = ListEntry->Flink;
1105 }
1106
1107 /* Release loader lock, it must succeed this time */
1108 Status = LdrUnlockLoaderLock(0, Cookie);
1109 ASSERT(NT_SUCCESS(Status));
1110
1111 /* Return success */
1112 return STATUS_SUCCESS;
1113 }
1114
1115 /*
1116 * @implemented
1117 */
1118 NTSTATUS
1119 NTAPI
1120 LdrDisableThreadCalloutsForDll(IN PVOID BaseAddress)
1121 {
1122 PLDR_DATA_TABLE_ENTRY LdrEntry;
1123 NTSTATUS Status;
1124 BOOLEAN LockHeld;
1125 ULONG_PTR Cookie;
1126 DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %p)\n", BaseAddress);
1127
1128 /* Don't do it during shutdown */
1129 if (LdrpShutdownInProgress) return STATUS_SUCCESS;
1130
1131 /* Check if we should grab the lock */
1132 LockHeld = FALSE;
1133 if (!LdrpInLdrInit)
1134 {
1135 /* Grab the lock */
1136 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1137 if (!NT_SUCCESS(Status)) return Status;
1138 LockHeld = TRUE;
1139 }
1140
1141 /* Make sure the DLL is valid and get its entry */
1142 Status = STATUS_DLL_NOT_FOUND;
1143 if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1144 {
1145 /* Get if it has a TLS slot */
1146 if (!LdrEntry->TlsIndex)
1147 {
1148 /* It doesn't, so you're allowed to call this */
1149 LdrEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;
1150 Status = STATUS_SUCCESS;
1151 }
1152 }
1153
1154 /* Check if the lock was held */
1155 if (LockHeld)
1156 {
1157 /* Release it */
1158 LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1159 }
1160
1161 /* Return the status */
1162 return Status;
1163 }
1164
1165 /*
1166 * @implemented
1167 */
1168 NTSTATUS
1169 NTAPI
1170 LdrAddRefDll(IN ULONG Flags,
1171 IN PVOID BaseAddress)
1172 {
1173 PLDR_DATA_TABLE_ENTRY LdrEntry;
1174 NTSTATUS Status = STATUS_SUCCESS;
1175 ULONG Cookie;
1176 BOOLEAN Locked = FALSE;
1177
1178 /* Check for invalid flags */
1179 if (Flags & ~(LDR_ADDREF_DLL_PIN))
1180 {
1181 /* Fail with invalid parameter status if so */
1182 Status = STATUS_INVALID_PARAMETER;
1183 goto quickie;
1184 }
1185
1186 /* Acquire the loader lock if not in init phase */
1187 if (!LdrpInLdrInit)
1188 {
1189 /* Acquire the lock */
1190 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1191 if (!NT_SUCCESS(Status)) goto quickie;
1192 Locked = TRUE;
1193 }
1194
1195 /* Get this module's data table entry */
1196 if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1197 {
1198 if (!LdrEntry)
1199 {
1200 /* Shouldn't happen */
1201 Status = STATUS_INTERNAL_ERROR;
1202 goto quickie;
1203 }
1204
1205 /* If this is not a pinned module */
1206 if (LdrEntry->LoadCount != 0xFFFF)
1207 {
1208 /* Update its load count */
1209 if (Flags & LDR_ADDREF_DLL_PIN)
1210 {
1211 /* Pin it by setting load count to -1 */
1212 LdrEntry->LoadCount = 0xFFFF;
1213 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_PIN);
1214 }
1215 else
1216 {
1217 /* Increase its load count by one */
1218 LdrEntry->LoadCount++;
1219 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
1220 }
1221
1222 /* Clear load in progress */
1223 LdrpClearLoadInProgress();
1224 }
1225 }
1226 else
1227 {
1228 /* There was an error getting this module's handle, return invalid param status */
1229 Status = STATUS_INVALID_PARAMETER;
1230 }
1231
1232 quickie:
1233 /* Check for error case */
1234 if (!NT_SUCCESS(Status))
1235 {
1236 /* Print debug information */
1237 if ((ShowSnaps) || ((Status != STATUS_NO_SUCH_FILE) &&
1238 (Status != STATUS_DLL_NOT_FOUND) &&
1239 (Status != STATUS_OBJECT_NAME_NOT_FOUND)))
1240 {
1241 DPRINT1("LDR: LdrAddRefDll(%p) 0x%08lx\n", BaseAddress);
1242 }
1243 }
1244
1245 /* Release the lock if needed */
1246 if (Locked) LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1247 return Status;
1248 }
1249
1250 /*
1251 * @implemented
1252 */
1253 NTSTATUS
1254 NTAPI
1255 LdrUnloadDll(IN PVOID BaseAddress)
1256 {
1257 NTSTATUS Status = STATUS_SUCCESS;
1258 PPEB Peb = NtCurrentPeb();
1259 PLDR_DATA_TABLE_ENTRY LdrEntry, CurrentEntry;
1260 PVOID EntryPoint;
1261 PLIST_ENTRY NextEntry;
1262 LIST_ENTRY UnloadList;
1263 RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
1264 PVOID CorImageData;
1265 ULONG ComSectionSize;
1266
1267 /* Get the LDR Lock */
1268 if (!LdrpInLdrInit) RtlEnterCriticalSection(Peb->LoaderLock);
1269
1270 /* Increase the unload count */
1271 LdrpActiveUnloadCount++;
1272
1273 /* Skip unload */
1274 if (LdrpShutdownInProgress) goto Quickie;
1275
1276 /* Make sure the DLL is valid and get its entry */
1277 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1278 {
1279 Status = STATUS_DLL_NOT_FOUND;
1280 goto Quickie;
1281 }
1282
1283 /* Check the current Load Count */
1284 if (LdrEntry->LoadCount != 0xFFFF)
1285 {
1286 /* Decrease it */
1287 LdrEntry->LoadCount--;
1288
1289 /* If it's a dll */
1290 if (LdrEntry->Flags & LDRP_IMAGE_DLL)
1291 {
1292 /* Set up the Act Ctx */
1293 ActCtx.Size = sizeof(ActCtx);
1294 ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1295 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1296
1297 /* Activate the ActCtx */
1298 RtlActivateActivationContextUnsafeFast(&ActCtx,
1299 LdrEntry->EntryPointActivationContext);
1300
1301 /* Update the load count */
1302 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_DEREFCOUNT);
1303
1304 /* Release the context */
1305 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1306 }
1307 }
1308 else
1309 {
1310 /* The DLL is locked */
1311 goto Quickie;
1312 }
1313
1314 /* Show debug message */
1315 if (ShowSnaps) DPRINT1("LDR: UNINIT LIST\n");
1316
1317 /* Check if this is our only unload and initialize the list if so */
1318 if (LdrpActiveUnloadCount == 1) InitializeListHead(&LdrpUnloadHead);
1319
1320 /* Loop the modules to build the list */
1321 NextEntry = Peb->Ldr->InInitializationOrderModuleList.Blink;
1322 while (NextEntry != &Peb->Ldr->InInitializationOrderModuleList)
1323 {
1324 /* Get the entry */
1325 LdrEntry = CONTAINING_RECORD(NextEntry,
1326 LDR_DATA_TABLE_ENTRY,
1327 InInitializationOrderModuleList);
1328 NextEntry = NextEntry->Blink;
1329
1330 /* Remove flag */
1331 LdrEntry->Flags &= ~LDRP_UNLOAD_IN_PROGRESS;
1332
1333 /* If the load count is now 0 */
1334 if (!LdrEntry->LoadCount)
1335 {
1336 /* Show message */
1337 if (ShowSnaps)
1338 {
1339 DPRINT1("(%d) [%ws] %ws (%lx) deinit %lx\n",
1340 LdrpActiveUnloadCount,
1341 LdrEntry->BaseDllName.Buffer,
1342 LdrEntry->FullDllName.Buffer,
1343 (ULONG)LdrEntry->LoadCount,
1344 LdrEntry->EntryPoint);
1345 }
1346
1347 /* FIXME: Call Shim Engine and notify */
1348
1349 /* Unlink it */
1350 CurrentEntry = LdrEntry;
1351 RemoveEntryList(&CurrentEntry->InInitializationOrderModuleList);
1352 RemoveEntryList(&CurrentEntry->InMemoryOrderModuleList);
1353 RemoveEntryList(&CurrentEntry->HashLinks);
1354
1355 /* If there's more then one active unload */
1356 if (LdrpActiveUnloadCount > 1)
1357 {
1358 /* Flush the cached DLL handle and clear the list */
1359 LdrpLoadedDllHandleCache = NULL;
1360 CurrentEntry->InMemoryOrderModuleList.Flink = NULL;
1361 }
1362
1363 /* Add the entry on the unload list */
1364 InsertTailList(&LdrpUnloadHead, &CurrentEntry->HashLinks);
1365 }
1366 }
1367
1368 /* Only call the entrypoints once */
1369 if (LdrpActiveUnloadCount > 1) goto Quickie;
1370
1371 /* Now loop the unload list and create our own */
1372 InitializeListHead(&UnloadList);
1373 CurrentEntry = NULL;
1374 NextEntry = LdrpUnloadHead.Flink;
1375 while (NextEntry != &LdrpUnloadHead)
1376 {
1377 /* If we have an active entry */
1378 if (CurrentEntry)
1379 {
1380 /* Remove it */
1381 RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
1382 CurrentEntry = NULL;
1383
1384 /* Reset list pointers */
1385 NextEntry = LdrpUnloadHead.Flink;
1386 if (NextEntry == &LdrpUnloadHead) break;
1387 }
1388
1389 /* Get the current entry */
1390 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1391
1392 /* FIXME: Log the Unload Event */
1393 //LdrpRecordUnloadEvent(LdrEntry);
1394
1395 /* Set the entry and clear it from the list */
1396 CurrentEntry = LdrEntry;
1397 LdrpLoadedDllHandleCache = NULL;
1398 CurrentEntry->InMemoryOrderModuleList.Flink = NULL;
1399
1400 /* Move it from the global to the local list */
1401 RemoveEntryList(&CurrentEntry->HashLinks);
1402 InsertTailList(&UnloadList, &CurrentEntry->HashLinks);
1403
1404 /* Get the entrypoint */
1405 EntryPoint = LdrEntry->EntryPoint;
1406
1407 /* Check if we should call it */
1408 if ((EntryPoint) && (LdrEntry->Flags & LDRP_PROCESS_ATTACH_CALLED))
1409 {
1410 /* Show message */
1411 if (ShowSnaps)
1412 {
1413 DPRINT1("LDR: Calling deinit %lx\n", EntryPoint);
1414 }
1415
1416 /* Set up the Act Ctx */
1417 ActCtx.Size = sizeof(ActCtx);
1418 ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1419 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1420
1421 /* Activate the ActCtx */
1422 RtlActivateActivationContextUnsafeFast(&ActCtx,
1423 LdrEntry->EntryPointActivationContext);
1424
1425 /* Call the entrypoint */
1426 LdrpCallInitRoutine(LdrEntry->EntryPoint,
1427 LdrEntry->DllBase,
1428 DLL_PROCESS_DETACH,
1429 NULL);
1430
1431 /* Release the context */
1432 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1433 }
1434
1435 /* Remove it from the list */
1436 RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
1437 CurrentEntry = NULL;
1438 NextEntry = LdrpUnloadHead.Flink;
1439 }
1440
1441 /* Now loop our local list */
1442 NextEntry = UnloadList.Flink;
1443 while (NextEntry != &UnloadList)
1444 {
1445 /* Get the entry */
1446 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1447 NextEntry = NextEntry->Flink;
1448 CurrentEntry = LdrEntry;
1449
1450 /* Notify Application Verifier */
1451 if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK)
1452 {
1453 DPRINT1("We don't support Application Verifier yet\n");
1454 }
1455
1456 /* Show message */
1457 if (ShowSnaps)
1458 {
1459 DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
1460 }
1461
1462 /* Check if this is a .NET executable */
1463 CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1464 TRUE,
1465 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
1466 &ComSectionSize);
1467 if (CorImageData)
1468 {
1469 /* FIXME */
1470 DPRINT1(".NET Images are not supported yet\n");
1471 }
1472
1473 /* Check if we should unmap*/
1474 if (!(CurrentEntry->Flags & LDR_COR_OWNS_UNMAP))
1475 {
1476 /* Unmap the DLL */
1477 Status = NtUnmapViewOfSection(NtCurrentProcess(),
1478 CurrentEntry->DllBase);
1479 ASSERT(NT_SUCCESS(Status));
1480 }
1481
1482 /* Unload the alternate resource module, if any */
1483 LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
1484
1485 /* FIXME: Send shutdown notification */
1486 //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
1487
1488 /* Check if a Hotpatch is active */
1489 if (LdrEntry->PatchInformation)
1490 {
1491 /* FIXME */
1492 DPRINT1("We don't support Hotpatching yet\n");
1493 }
1494
1495 /* Deallocate the Entry */
1496 LdrpFinalizeAndDeallocateDataTableEntry(CurrentEntry);
1497
1498 /* If this is the cached entry, invalidate it */
1499 if (LdrpGetModuleHandleCache == CurrentEntry)
1500 {
1501 LdrpGetModuleHandleCache = NULL;
1502 }
1503 }
1504
1505 Quickie:
1506 /* Decrease unload count */
1507 LdrpActiveUnloadCount--;
1508 if (!LdrpInLdrInit) RtlLeaveCriticalSection(Peb->LoaderLock);
1509
1510 /* Return to caller */
1511 return Status;
1512 }
1513
1514 /*
1515 * @implemented
1516 */
1517 BOOLEAN
1518 NTAPI
1519 RtlDllShutdownInProgress(VOID)
1520 {
1521 /* Return the internal global */
1522 return LdrpShutdownInProgress;
1523 }
1524
1525 /*
1526 * @implemented
1527 */
1528 PIMAGE_BASE_RELOCATION
1529 NTAPI
1530 LdrProcessRelocationBlock(IN ULONG_PTR Address,
1531 IN ULONG Count,
1532 IN PUSHORT TypeOffset,
1533 IN LONG_PTR Delta)
1534 {
1535 return LdrProcessRelocationBlockLongLong(Address, Count, TypeOffset, Delta);
1536 }
1537
1538 /*
1539 * @unimplemented
1540 */
1541 BOOLEAN
1542 NTAPI
1543 LdrUnloadAlternateResourceModule(IN PVOID BaseAddress)
1544 {
1545 static BOOLEAN WarnedOnce = FALSE;
1546 if (WarnedOnce == FALSE) { UNIMPLEMENTED; WarnedOnce = TRUE; }
1547 return FALSE;
1548 }
1549
1550 /* EOF */