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