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