xref: /reactos/dll/ntdll/ldr/ldrapi.c (revision ccef43f3)
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) ^ (HandleToUlong(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_PTR 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     _SEH2_TRY
357     {
358 
359         if (OldTldDll)
360         {
361             /* This is a recursive load, do something about it? */
362             if ((ShowSnaps) || (LdrpShowRecursiveLoads) || (LdrpBreakOnRecursiveDllLoads))
363             {
364                 /* Print out debug messages */
365                 DPRINT1("[%p, %p] LDR: Recursive DLL Load\n",
366                         Teb->RealClientId.UniqueProcess,
367                         Teb->RealClientId.UniqueThread);
368                 DPRINT1("[%p, %p]      Previous DLL being loaded \"%wZ\"\n",
369                         Teb->RealClientId.UniqueProcess,
370                         Teb->RealClientId.UniqueThread,
371                         OldTldDll);
372                 DPRINT1("[%p, %p]      DLL being requested \"%wZ\"\n",
373                         Teb->RealClientId.UniqueProcess,
374                         Teb->RealClientId.UniqueThread,
375                         DllName);
376 
377                 /* Was it initializing too? */
378                 if (!LdrpCurrentDllInitializer)
379                 {
380                     DPRINT1("[%p, %p] LDR: No DLL Initializer was running\n",
381                             Teb->RealClientId.UniqueProcess,
382                             Teb->RealClientId.UniqueThread);
383                 }
384                 else
385                 {
386                     DPRINT1("[%p, %p]      DLL whose initializer was currently running \"%wZ\"\n",
387                             Teb->ClientId.UniqueProcess,
388                             Teb->ClientId.UniqueThread,
389                             &LdrpCurrentDllInitializer->BaseDllName);
390                 }
391             }
392         }
393 
394         /* Set this one as the TLD DLL being loaded*/
395         LdrpTopLevelDllBeingLoaded = DllName;
396 
397         /* Load the DLL */
398         Status = LdrpLoadDll(RedirectedDll,
399                              SearchPath,
400                              DllCharacteristics,
401                              DllName,
402                              BaseAddress,
403                              TRUE);
404         if (NT_SUCCESS(Status))
405         {
406             Status = STATUS_SUCCESS;
407         }
408         else if ((Status != STATUS_NO_SUCH_FILE) &&
409                  (Status != STATUS_DLL_NOT_FOUND) &&
410                  (Status != STATUS_OBJECT_NAME_NOT_FOUND) &&
411                  (Status != STATUS_DLL_INIT_FAILED))
412         {
413             DbgPrintEx(DPFLTR_LDR_ID,
414                        DPFLTR_WARNING_LEVEL,
415                        "LDR: %s - failing because LdrpLoadDll(%wZ) returned status %x\n",
416                        __FUNCTION__,
417                        DllName,
418                        Status);
419         }
420     }
421     _SEH2_FINALLY
422     {
423         /* Restore the old TLD DLL */
424         LdrpTopLevelDllBeingLoaded = OldTldDll;
425 
426         /* Release the lock */
427         LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
428     }
429     _SEH2_END;
430 
431     /* Do we have a redirect string? */
432     if (DllString2.Buffer) RtlFreeUnicodeString(&DllString2);
433 
434     /* Return */
435     return Status;
436 }
437 
438 /*
439  * @implemented
440  */
441 NTSTATUS
442 NTAPI
443 LdrFindEntryForAddress(PVOID Address,
444                        PLDR_DATA_TABLE_ENTRY *Module)
445 {
446     PLIST_ENTRY ListHead, NextEntry;
447     PLDR_DATA_TABLE_ENTRY LdrEntry;
448     PIMAGE_NT_HEADERS NtHeader;
449     PPEB_LDR_DATA Ldr = NtCurrentPeb()->Ldr;
450     ULONG_PTR DllBase, DllEnd;
451 
452     DPRINT("LdrFindEntryForAddress(Address %p)\n", Address);
453 
454     /* Nothing to do */
455     if (!Ldr) return STATUS_NO_MORE_ENTRIES;
456 
457     /* Get the current entry */
458     LdrEntry = Ldr->EntryInProgress;
459     if (LdrEntry)
460     {
461         /* Get the NT Headers */
462         NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
463         if (NtHeader)
464         {
465             /* Get the Image Base */
466             DllBase = (ULONG_PTR)LdrEntry->DllBase;
467             DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
468 
469             /* Check if they match */
470             if (((ULONG_PTR)Address >= DllBase) &&
471                 ((ULONG_PTR)Address < DllEnd))
472             {
473                 /* Return it */
474                 *Module = LdrEntry;
475                 return STATUS_SUCCESS;
476             }
477         }
478     }
479 
480     /* Loop the module list */
481     ListHead = &Ldr->InMemoryOrderModuleList;
482     NextEntry = ListHead->Flink;
483     while (NextEntry != ListHead)
484     {
485         /* Get the entry and NT Headers */
486         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
487         NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
488         if (NtHeader)
489         {
490             /* Get the Image Base */
491             DllBase = (ULONG_PTR)LdrEntry->DllBase;
492             DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
493 
494             /* Check if they match */
495             if (((ULONG_PTR)Address >= DllBase) &&
496                 ((ULONG_PTR)Address < DllEnd))
497             {
498                 /* Return it */
499                 *Module = LdrEntry;
500                 return STATUS_SUCCESS;
501             }
502 
503             /* Next Entry */
504             NextEntry = NextEntry->Flink;
505         }
506     }
507 
508     /* Nothing found */
509     DbgPrintEx(DPFLTR_LDR_ID,
510                DPFLTR_WARNING_LEVEL,
511                "LDR: %s() exiting 0x%08lx\n",
512                __FUNCTION__,
513                STATUS_NO_MORE_ENTRIES);
514     return STATUS_NO_MORE_ENTRIES;
515 }
516 
517 /*
518  * @implemented
519  */
520 NTSTATUS
521 NTAPI
522 LdrGetDllHandleEx(IN ULONG Flags,
523                   IN PWSTR DllPath OPTIONAL,
524                   IN PULONG DllCharacteristics OPTIONAL,
525                   IN PUNICODE_STRING DllName,
526                   OUT PVOID *DllHandle OPTIONAL)
527 {
528     NTSTATUS Status;
529     PLDR_DATA_TABLE_ENTRY LdrEntry;
530     UNICODE_STRING RedirectName, DllString1, RawDllName;
531     PUNICODE_STRING pRedirectName, CompareName;
532     PWCHAR p1, p2, p3;
533     BOOLEAN Locked, RedirectedDll;
534     ULONG_PTR Cookie;
535     ULONG LoadFlag, Length;
536 
537     /* Initialize the strings */
538     RtlInitEmptyUnicodeString(&DllString1, NULL, 0);
539     RtlInitEmptyUnicodeString(&RawDllName, NULL, 0);
540     RedirectName = *DllName;
541     pRedirectName = &RedirectName;
542 
543     /* Initialize state */
544     RedirectedDll = Locked = FALSE;
545     LdrEntry = NULL;
546     Cookie = 0;
547 
548     /* Clear the handle */
549     if (DllHandle) *DllHandle = NULL;
550 
551     /* Check for a valid flag combination */
552     if ((Flags & ~(LDR_GET_DLL_HANDLE_EX_PIN | LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT)) ||
553         (!DllHandle && !(Flags & LDR_GET_DLL_HANDLE_EX_PIN)))
554     {
555         DPRINT1("Flags are invalid or no DllHandle given\n");
556         Status = STATUS_INVALID_PARAMETER;
557         goto Quickie;
558     }
559 
560     /* If not initializing */
561     if (!LdrpInLdrInit)
562     {
563         /* Acquire the lock */
564         Status = LdrLockLoaderLock(0, NULL, &Cookie);
565         if (!NT_SUCCESS(Status)) goto Quickie;
566 
567         /* Remember we own it */
568         Locked = TRUE;
569     }
570 
571     /* Check if the SxS Assemblies specify another file */
572     Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
573                                                       pRedirectName,
574                                                       &LdrApiDefaultExtension,
575                                                       NULL,
576                                                       &DllString1,
577                                                       &pRedirectName,
578                                                       NULL,
579                                                       NULL,
580                                                       NULL);
581 
582     /* Check success */
583     if (NT_SUCCESS(Status))
584     {
585         /* Let Ldrp know */
586         RedirectedDll = TRUE;
587     }
588     else if (Status != STATUS_SXS_KEY_NOT_FOUND)
589     {
590         /* Unrecoverable SxS failure */
591         goto Quickie;
592     }
593     else
594     {
595         ASSERT(pRedirectName == &RedirectName);
596     }
597 
598     /* Set default failure code */
599     Status = STATUS_DLL_NOT_FOUND;
600 
601     /* Use the cache if we can */
602     if (LdrpGetModuleHandleCache)
603     {
604         /* Check if we were redirected */
605         if (RedirectedDll)
606         {
607             /* Check the flag */
608             if (!(LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED))
609             {
610                 goto DontCompare;
611             }
612 
613             /* Use the right name */
614             CompareName = &LdrpGetModuleHandleCache->FullDllName;
615         }
616         else
617         {
618             /* Check the flag */
619             if (LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED)
620             {
621                 goto DontCompare;
622             }
623 
624             /* Use the right name */
625             CompareName = &LdrpGetModuleHandleCache->BaseDllName;
626         }
627 
628         /* Check if the name matches */
629         if (RtlEqualUnicodeString(pRedirectName,
630                                   CompareName,
631                                   TRUE))
632         {
633             /* Skip the rest */
634             LdrEntry = LdrpGetModuleHandleCache;
635 
636             /* Return success */
637             Status = STATUS_SUCCESS;
638             goto Quickie;
639         }
640     }
641 
642 DontCompare:
643     /* Find the name without the extension */
644     p1 = pRedirectName->Buffer;
645     p2 = NULL;
646     p3 = &p1[pRedirectName->Length / sizeof(WCHAR)];
647     while (p1 != p3)
648     {
649         if (*p1++ == L'.')
650         {
651             p2 = p1;
652         }
653         else if (*p1 == L'\\')
654         {
655             p2 = NULL;
656         }
657     }
658 
659     /* Check if no extension was found or if we got a slash */
660     if (!(p2) || (*p2 == L'\\') || (*p2 == L'/'))
661     {
662         /* Check that we have space to add one */
663         Length = pRedirectName->Length +
664                  LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL);
665         if (Length >= UNICODE_STRING_MAX_BYTES)
666         {
667             /* No space to add the extension */
668             Status = STATUS_NAME_TOO_LONG;
669             goto Quickie;
670         }
671 
672         /* Setup the string */
673         RawDllName.MaximumLength = Length;
674         ASSERT(Length >= sizeof(UNICODE_NULL));
675         RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
676                                             0,
677                                             RawDllName.MaximumLength);
678         if (!RawDllName.Buffer)
679         {
680             Status = STATUS_NO_MEMORY;
681             goto Quickie;
682         }
683 
684         /* Copy the string and add extension */
685         RtlCopyUnicodeString(&RawDllName, pRedirectName);
686         RtlAppendUnicodeStringToString(&RawDllName, &LdrApiDefaultExtension);
687     }
688     else
689     {
690         /* Check if there's something in the name */
691         Length = pRedirectName->Length;
692         if (Length)
693         {
694             /* Check and remove trailing period */
695             if (pRedirectName->Buffer[Length / sizeof(WCHAR) - sizeof(ANSI_NULL)] == '.')
696             {
697                 /* Decrease the size */
698                 pRedirectName->Length -= sizeof(WCHAR);
699             }
700         }
701 
702         /* Setup the string */
703         RawDllName.MaximumLength = pRedirectName->Length + sizeof(WCHAR);
704         RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
705                                             0,
706                                             RawDllName.MaximumLength);
707         if (!RawDllName.Buffer)
708         {
709             Status = STATUS_NO_MEMORY;
710             goto Quickie;
711         }
712 
713         /* Copy the string */
714         RtlCopyUnicodeString(&RawDllName, pRedirectName);
715     }
716 
717     /* Display debug string */
718     if (ShowSnaps)
719     {
720         DPRINT1("LDR: LdrGetDllHandleEx, searching for %wZ from %ws\n",
721                 &RawDllName,
722                 DllPath ? ((ULONG_PTR)DllPath == 1 ? L"" : DllPath) : L"");
723     }
724 
725     /* Do the lookup */
726     if (LdrpCheckForLoadedDll(DllPath,
727                               &RawDllName,
728                               ((ULONG_PTR)DllPath == 1) ? TRUE : FALSE,
729                               RedirectedDll,
730                               &LdrEntry))
731     {
732         /* Update cached entry */
733         LdrpGetModuleHandleCache = LdrEntry;
734 
735         /* Return success */
736         Status = STATUS_SUCCESS;
737     }
738     else
739     {
740         /* Make sure to NULL this */
741         LdrEntry = NULL;
742     }
743 Quickie:
744     /* The success path must have a valid loader entry */
745     ASSERT((LdrEntry != NULL) == NT_SUCCESS(Status));
746 
747     /* Check if we got an entry and success */
748     DPRINT("Got LdrEntry->BaseDllName %wZ\n", LdrEntry ? &LdrEntry->BaseDllName : NULL);
749     if ((LdrEntry) && (NT_SUCCESS(Status)))
750     {
751         /* Check if the DLL is locked */
752         if ((LdrEntry->LoadCount != 0xFFFF) &&
753             !(Flags & LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT))
754         {
755             /* Check what to do with the load count */
756             if (Flags & LDR_GET_DLL_HANDLE_EX_PIN)
757             {
758                 /* Pin it */
759                 LdrEntry->LoadCount = 0xFFFF;
760                 LoadFlag = LDRP_UPDATE_PIN;
761             }
762             else
763             {
764                 /* Increase the load count */
765                 LdrEntry->LoadCount++;
766                 LoadFlag = LDRP_UPDATE_REFCOUNT;
767             }
768 
769             /* Update the load count now */
770             LdrpUpdateLoadCount2(LdrEntry, LoadFlag);
771             LdrpClearLoadInProgress();
772         }
773 
774         /* Check if the caller is requesting the handle */
775         if (DllHandle) *DllHandle = LdrEntry->DllBase;
776     }
777 
778     /* Free string if needed */
779     if (DllString1.Buffer) RtlFreeUnicodeString(&DllString1);
780 
781     /* Free the raw DLL Name if needed */
782     if (RawDllName.Buffer)
783     {
784         /* Free the heap-allocated buffer */
785         RtlFreeHeap(RtlGetProcessHeap(), 0, RawDllName.Buffer);
786         RawDllName.Buffer = NULL;
787     }
788 
789     /* Release lock */
790     if (Locked)
791     {
792         LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS,
793                             Cookie);
794     }
795 
796     /* Return */
797     return Status;
798 }
799 
800 /*
801  * @implemented
802  */
803 NTSTATUS
804 NTAPI
805 LdrGetDllHandle(IN PWSTR DllPath OPTIONAL,
806                 IN PULONG DllCharacteristics OPTIONAL,
807                 IN PUNICODE_STRING DllName,
808                 OUT PVOID *DllHandle)
809 {
810     /* Call the newer API */
811     return LdrGetDllHandleEx(LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT,
812                              DllPath,
813                              DllCharacteristics,
814                              DllName,
815                              DllHandle);
816 }
817 
818 /*
819  * @implemented
820  */
821 NTSTATUS
822 NTAPI
823 LdrGetProcedureAddress(IN PVOID BaseAddress,
824                        IN PANSI_STRING Name,
825                        IN ULONG Ordinal,
826                        OUT PVOID *ProcedureAddress)
827 {
828     /* Call the internal routine and tell it to execute DllInit */
829     return LdrpGetProcedureAddress(BaseAddress, Name, Ordinal, ProcedureAddress, TRUE);
830 }
831 
832 /*
833  * @implemented
834  */
835 NTSTATUS
836 NTAPI
837 LdrVerifyImageMatchesChecksum(IN HANDLE FileHandle,
838                               IN PLDR_CALLBACK Callback,
839                               IN PVOID CallbackContext,
840                               OUT PUSHORT ImageCharacteristics)
841 {
842     FILE_STANDARD_INFORMATION FileStandardInfo;
843     PIMAGE_IMPORT_DESCRIPTOR ImportData;
844     PIMAGE_SECTION_HEADER LastSection = NULL;
845     IO_STATUS_BLOCK IoStatusBlock;
846     PIMAGE_NT_HEADERS NtHeader;
847     HANDLE SectionHandle;
848     SIZE_T ViewSize;
849     PVOID ViewBase;
850     BOOLEAN Result, NoActualCheck;
851     NTSTATUS Status;
852     PVOID ImportName;
853     ULONG Size;
854     DPRINT("LdrVerifyImageMatchesChecksum() called\n");
855 
856     /* If the handle has the magic KnownDll flag, skip actual checksums */
857     NoActualCheck = ((ULONG_PTR)FileHandle & 1);
858 
859     /* Create the section */
860     Status = NtCreateSection(&SectionHandle,
861                              SECTION_MAP_EXECUTE,
862                              NULL,
863                              NULL,
864                              PAGE_EXECUTE,
865                              SEC_COMMIT,
866                              FileHandle);
867     if (!NT_SUCCESS(Status))
868     {
869         DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status);
870         return Status;
871     }
872 
873     /* Map the section */
874     ViewSize = 0;
875     ViewBase = NULL;
876     Status = NtMapViewOfSection(SectionHandle,
877                                 NtCurrentProcess(),
878                                 &ViewBase,
879                                 0,
880                                 0,
881                                 NULL,
882                                 &ViewSize,
883                                 ViewShare,
884                                 0,
885                                 PAGE_EXECUTE);
886     if (!NT_SUCCESS(Status))
887     {
888         DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
889         NtClose(SectionHandle);
890         return Status;
891     }
892 
893     /* Get the file information */
894     Status = NtQueryInformationFile(FileHandle,
895                                     &IoStatusBlock,
896                                     &FileStandardInfo,
897                                     sizeof(FILE_STANDARD_INFORMATION),
898                                     FileStandardInformation);
899     if (!NT_SUCCESS(Status))
900     {
901         DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
902         NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
903         NtClose(SectionHandle);
904         return Status;
905     }
906 
907     /* Protect with SEH */
908     _SEH2_TRY
909     {
910         /* Check if this is the KnownDll hack */
911         if (NoActualCheck)
912         {
913             /* Don't actually do it */
914             Result = TRUE;
915         }
916         else
917         {
918             /* Verify the checksum */
919             Result = LdrVerifyMappedImageMatchesChecksum(ViewBase,
920                                                          FileStandardInfo.EndOfFile.LowPart,
921                                                          FileStandardInfo.EndOfFile.LowPart);
922         }
923 
924         /* Check if a callback was supplied */
925         if ((Result) && (Callback))
926         {
927             /* Get the NT Header */
928             NtHeader = RtlImageNtHeader(ViewBase);
929 
930             /* Check if caller requested this back */
931             if (ImageCharacteristics)
932             {
933                 /* Return to caller */
934                 *ImageCharacteristics = NtHeader->FileHeader.Characteristics;
935             }
936 
937             /* Get the Import Directory Data */
938             ImportData = RtlImageDirectoryEntryToData(ViewBase,
939                                                       FALSE,
940                                                       IMAGE_DIRECTORY_ENTRY_IMPORT,
941                                                       &Size);
942 
943             /* Make sure there is one */
944             if (ImportData)
945             {
946                 /* Loop the imports */
947                 while (ImportData->Name)
948                 {
949                     /* Get the name */
950                     ImportName = RtlImageRvaToVa(NtHeader,
951                                                  ViewBase,
952                                                  ImportData->Name,
953                                                  &LastSection);
954 
955                     /* Notify the callback */
956                     Callback(CallbackContext, ImportName);
957                     ImportData++;
958                 }
959             }
960         }
961     }
962     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
963     {
964         /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
965         Result = FALSE;
966     }
967     _SEH2_END;
968 
969     /* Unmap file and close handle */
970     NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
971     NtClose(SectionHandle);
972 
973     /* Return status */
974     return Result ? Status : STATUS_IMAGE_CHECKSUM_MISMATCH;
975 }
976 
977 NTSTATUS
978 NTAPI
979 LdrQueryProcessModuleInformationEx(IN ULONG ProcessId,
980                                    IN ULONG Reserved,
981                                    OUT PRTL_PROCESS_MODULES ModuleInformation,
982                                    IN ULONG Size,
983                                    OUT PULONG ReturnedSize OPTIONAL)
984 {
985     PLIST_ENTRY ModuleListHead, InitListHead;
986     PLIST_ENTRY Entry, InitEntry;
987     PLDR_DATA_TABLE_ENTRY Module, InitModule;
988     PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL;
989     NTSTATUS Status = STATUS_SUCCESS;
990     ULONG UsedSize = sizeof(ULONG);
991     ANSI_STRING AnsiString;
992     PCHAR p;
993 
994     DPRINT("LdrQueryProcessModuleInformation() called\n");
995 
996     /* Acquire loader lock */
997     RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
998 
999     _SEH2_TRY
1000     {
1001         /* Check if we were given enough space */
1002         if (Size < UsedSize)
1003         {
1004             Status = STATUS_INFO_LENGTH_MISMATCH;
1005         }
1006         else
1007         {
1008             ModuleInformation->NumberOfModules = 0;
1009             ModulePtr = &ModuleInformation->Modules[0];
1010             Status = STATUS_SUCCESS;
1011         }
1012 
1013         /* Traverse the list of modules */
1014         ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1015         Entry = ModuleListHead->Flink;
1016 
1017         while (Entry != ModuleListHead)
1018         {
1019             Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1020 
1021             DPRINT("  Module %wZ\n", &Module->FullDllName);
1022 
1023             /* Increase the used size */
1024             UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION);
1025 
1026             if (UsedSize > Size)
1027             {
1028                 Status = STATUS_INFO_LENGTH_MISMATCH;
1029             }
1030             else
1031             {
1032                 ModulePtr->ImageBase = Module->DllBase;
1033                 ModulePtr->ImageSize = Module->SizeOfImage;
1034                 ModulePtr->Flags = Module->Flags;
1035                 ModulePtr->LoadCount = Module->LoadCount;
1036                 ModulePtr->MappedBase = NULL;
1037                 ModulePtr->InitOrderIndex = 0;
1038                 ModulePtr->LoadOrderIndex = ModuleInformation->NumberOfModules;
1039 
1040                 /* Now get init order index by traversing init list */
1041                 InitListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1042                 InitEntry = InitListHead->Flink;
1043 
1044                 while (InitEntry != InitListHead)
1045                 {
1046                     InitModule = CONTAINING_RECORD(InitEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
1047 
1048                     /* Increase the index */
1049                     ModulePtr->InitOrderIndex++;
1050 
1051                     /* Quit the loop if our module is found */
1052                     if (InitModule == Module) break;
1053 
1054                     /* Advance to the next entry */
1055                     InitEntry = InitEntry->Flink;
1056                 }
1057 
1058                 /* Prepare ANSI string with the module's name */
1059                 AnsiString.Length = 0;
1060                 AnsiString.MaximumLength = sizeof(ModulePtr->FullPathName);
1061                 AnsiString.Buffer = ModulePtr->FullPathName;
1062                 RtlUnicodeStringToAnsiString(&AnsiString,
1063                                              &Module->FullDllName,
1064                                              FALSE);
1065 
1066                 /* Calculate OffsetToFileName field */
1067                 p = strrchr(ModulePtr->FullPathName, '\\');
1068                 if (p != NULL)
1069                     ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1;
1070                 else
1071                     ModulePtr->OffsetToFileName = 0;
1072 
1073                 /* Advance to the next module in the output list */
1074                 ModulePtr++;
1075 
1076                 /* Increase number of modules */
1077                 if (ModuleInformation)
1078                     ModuleInformation->NumberOfModules++;
1079             }
1080 
1081             /* Go to the next entry in the modules list */
1082             Entry = Entry->Flink;
1083         }
1084 
1085         /* Set returned size if it was provided */
1086         if (ReturnedSize)
1087             *ReturnedSize = UsedSize;
1088     }
1089     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1090     {
1091         /* Ignoring the exception */
1092     } _SEH2_END;
1093 
1094     /* Release the lock */
1095     RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
1096 
1097     DPRINT("LdrQueryProcessModuleInformation() done\n");
1098 
1099     return Status;
1100 }
1101 
1102 /*
1103  * @implemented
1104  */
1105 NTSTATUS
1106 NTAPI
1107 LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation,
1108                                  IN ULONG Size,
1109                                  OUT PULONG ReturnedSize OPTIONAL)
1110 {
1111     /* Call Ex version of the API */
1112     return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation, Size, ReturnedSize);
1113 }
1114 
1115 /*
1116  * @implemented
1117  */
1118 NTSTATUS
1119 NTAPI
1120 LdrEnumerateLoadedModules(IN BOOLEAN ReservedFlag,
1121                           IN PLDR_ENUM_CALLBACK EnumProc,
1122                           IN PVOID Context)
1123 {
1124     PLIST_ENTRY ListHead, ListEntry;
1125     PLDR_DATA_TABLE_ENTRY LdrEntry;
1126     NTSTATUS Status;
1127     ULONG_PTR Cookie;
1128     BOOLEAN Stop = FALSE;
1129 
1130     /* Check parameters */
1131     if ((ReservedFlag) || !(EnumProc)) return STATUS_INVALID_PARAMETER;
1132 
1133     /* Acquire the loader lock */
1134     Status = LdrLockLoaderLock(0, NULL, &Cookie);
1135     if (!NT_SUCCESS(Status)) return Status;
1136 
1137     /* Loop all the modules and call enum proc */
1138     ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1139     ListEntry = ListHead->Flink;
1140     while (ListHead != ListEntry)
1141     {
1142         /* Get the entry */
1143         LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1144 
1145         /* Call the enumeration proc inside SEH */
1146         _SEH2_TRY
1147         {
1148             EnumProc(LdrEntry, Context, &Stop);
1149         }
1150         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1151         {
1152             /* Ignoring the exception */
1153         } _SEH2_END;
1154 
1155         /* Break if we were asked to stop enumeration */
1156         if (Stop)
1157         {
1158             /* Release loader lock */
1159             Status = LdrUnlockLoaderLock(0, Cookie);
1160 
1161             /* Reset any successful status to STATUS_SUCCESS, but leave
1162                failure to the caller */
1163             if (NT_SUCCESS(Status))
1164                 Status = STATUS_SUCCESS;
1165 
1166             /* Return any possible failure status */
1167             return Status;
1168         }
1169 
1170         /* Advance to the next module */
1171         ListEntry = ListEntry->Flink;
1172     }
1173 
1174     /* Release loader lock, it must succeed this time */
1175     Status = LdrUnlockLoaderLock(0, Cookie);
1176     ASSERT(NT_SUCCESS(Status));
1177 
1178     /* Return success */
1179     return STATUS_SUCCESS;
1180 }
1181 
1182 /*
1183  * @implemented
1184  */
1185 NTSTATUS
1186 NTAPI
1187 LdrDisableThreadCalloutsForDll(IN PVOID BaseAddress)
1188 {
1189     PLDR_DATA_TABLE_ENTRY LdrEntry;
1190     NTSTATUS Status;
1191     BOOLEAN LockHeld;
1192     ULONG_PTR Cookie;
1193     DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %p)\n", BaseAddress);
1194 
1195     /* Don't do it during shutdown */
1196     if (LdrpShutdownInProgress) return STATUS_SUCCESS;
1197 
1198     /* Check if we should grab the lock */
1199     LockHeld = FALSE;
1200     if (!LdrpInLdrInit)
1201     {
1202         /* Grab the lock */
1203         Status = LdrLockLoaderLock(0, NULL, &Cookie);
1204         if (!NT_SUCCESS(Status)) return Status;
1205         LockHeld = TRUE;
1206     }
1207 
1208     /* Make sure the DLL is valid and get its entry */
1209     Status = STATUS_DLL_NOT_FOUND;
1210     if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1211     {
1212         /* Get if it has a TLS slot */
1213         if (!LdrEntry->TlsIndex)
1214         {
1215             /* It doesn't, so you're allowed to call this */
1216             LdrEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;
1217             Status = STATUS_SUCCESS;
1218         }
1219     }
1220 
1221     /* Check if the lock was held */
1222     if (LockHeld)
1223     {
1224         /* Release it */
1225         LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1226     }
1227 
1228     /* Return the status */
1229     return Status;
1230 }
1231 
1232 /*
1233  * @implemented
1234  */
1235 NTSTATUS
1236 NTAPI
1237 LdrAddRefDll(IN ULONG Flags,
1238              IN PVOID BaseAddress)
1239 {
1240     PLDR_DATA_TABLE_ENTRY LdrEntry;
1241     NTSTATUS Status = STATUS_SUCCESS;
1242     ULONG_PTR Cookie;
1243     BOOLEAN Locked = FALSE;
1244 
1245     /* Check for invalid flags */
1246     if (Flags & ~(LDR_ADDREF_DLL_PIN))
1247     {
1248         /* Fail with invalid parameter status if so */
1249         Status = STATUS_INVALID_PARAMETER;
1250         goto quickie;
1251     }
1252 
1253     /* Acquire the loader lock if not in init phase */
1254     if (!LdrpInLdrInit)
1255     {
1256         /* Acquire the lock */
1257         Status = LdrLockLoaderLock(0, NULL, &Cookie);
1258         if (!NT_SUCCESS(Status)) goto quickie;
1259         Locked = TRUE;
1260     }
1261 
1262     /* Get this module's data table entry */
1263     if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1264     {
1265         if (!LdrEntry)
1266         {
1267             /* Shouldn't happen */
1268             Status = STATUS_INTERNAL_ERROR;
1269             goto quickie;
1270         }
1271 
1272         /* If this is not a pinned module */
1273         if (LdrEntry->LoadCount != 0xFFFF)
1274         {
1275             /* Update its load count */
1276             if (Flags & LDR_ADDREF_DLL_PIN)
1277             {
1278                 /* Pin it by setting load count to -1 */
1279                 LdrEntry->LoadCount = 0xFFFF;
1280                 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_PIN);
1281             }
1282             else
1283             {
1284                 /* Increase its load count by one */
1285                 LdrEntry->LoadCount++;
1286                 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
1287             }
1288 
1289             /* Clear load in progress */
1290             LdrpClearLoadInProgress();
1291         }
1292     }
1293     else
1294     {
1295         /* There was an error getting this module's handle, return invalid param status */
1296         Status = STATUS_INVALID_PARAMETER;
1297     }
1298 
1299 quickie:
1300     /* Check for error case */
1301     if (!NT_SUCCESS(Status))
1302     {
1303         /* Print debug information */
1304         if ((ShowSnaps) || ((Status != STATUS_NO_SUCH_FILE) &&
1305                             (Status != STATUS_DLL_NOT_FOUND) &&
1306                             (Status != STATUS_OBJECT_NAME_NOT_FOUND)))
1307         {
1308             DPRINT1("LDR: LdrAddRefDll(%p) 0x%08lx\n", BaseAddress, Status);
1309         }
1310     }
1311 
1312     /* Release the lock if needed */
1313     if (Locked) LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1314     return Status;
1315 }
1316 
1317 /*
1318  * @implemented
1319  */
1320 NTSTATUS
1321 NTAPI
1322 LdrUnloadDll(IN PVOID BaseAddress)
1323 {
1324     NTSTATUS Status = STATUS_SUCCESS;
1325     PPEB Peb = NtCurrentPeb();
1326     PLDR_DATA_TABLE_ENTRY LdrEntry, CurrentEntry;
1327     PVOID EntryPoint;
1328     PLIST_ENTRY NextEntry;
1329     LIST_ENTRY UnloadList;
1330     RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
1331     PVOID CorImageData;
1332     ULONG ComSectionSize;
1333 
1334     /* Get the LDR Lock */
1335     if (!LdrpInLdrInit) RtlEnterCriticalSection(Peb->LoaderLock);
1336 
1337     /* Increase the unload count */
1338     LdrpActiveUnloadCount++;
1339 
1340     /* Skip unload */
1341     if (LdrpShutdownInProgress) goto Quickie;
1342 
1343     /* Make sure the DLL is valid and get its entry */
1344     if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1345     {
1346         Status = STATUS_DLL_NOT_FOUND;
1347         goto Quickie;
1348     }
1349 
1350     /* Check the current Load Count */
1351     if (LdrEntry->LoadCount != 0xFFFF)
1352     {
1353         /* Decrease it */
1354         LdrEntry->LoadCount--;
1355 
1356         /* If it's a dll */
1357         if (LdrEntry->Flags & LDRP_IMAGE_DLL)
1358         {
1359             /* Set up the Act Ctx */
1360             ActCtx.Size = sizeof(ActCtx);
1361             ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1362             RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1363 
1364             /* Activate the ActCtx */
1365             RtlActivateActivationContextUnsafeFast(&ActCtx,
1366                                                    LdrEntry->EntryPointActivationContext);
1367 
1368             /* Update the load count */
1369             LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_DEREFCOUNT);
1370 
1371             /* Release the context */
1372             RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1373         }
1374     }
1375     else
1376     {
1377         /* The DLL is locked */
1378         goto Quickie;
1379     }
1380 
1381     /* Show debug message */
1382     if (ShowSnaps) DPRINT1("LDR: UNINIT LIST\n");
1383 
1384     /* Check if this is our only unload and initialize the list if so */
1385     if (LdrpActiveUnloadCount == 1) InitializeListHead(&LdrpUnloadHead);
1386 
1387     /* Loop the modules to build the list */
1388     NextEntry = Peb->Ldr->InInitializationOrderModuleList.Blink;
1389     while (NextEntry != &Peb->Ldr->InInitializationOrderModuleList)
1390     {
1391         /* Get the entry */
1392         LdrEntry = CONTAINING_RECORD(NextEntry,
1393                                      LDR_DATA_TABLE_ENTRY,
1394                                      InInitializationOrderLinks);
1395         NextEntry = NextEntry->Blink;
1396 
1397         /* Remove flag */
1398         LdrEntry->Flags &= ~LDRP_UNLOAD_IN_PROGRESS;
1399 
1400         /* If the load count is now 0 */
1401         if (!LdrEntry->LoadCount)
1402         {
1403             /* Show message */
1404             if (ShowSnaps)
1405             {
1406                 DPRINT1("(%lu) [%ws] %ws (%lx) deinit %p\n",
1407                         LdrpActiveUnloadCount,
1408                         LdrEntry->BaseDllName.Buffer,
1409                         LdrEntry->FullDllName.Buffer,
1410                         (ULONG)LdrEntry->LoadCount,
1411                         LdrEntry->EntryPoint);
1412             }
1413 
1414             /* Call Shim Engine and notify */
1415             if (g_ShimsEnabled)
1416             {
1417                 VOID (NTAPI* SE_DllUnloaded)(PVOID) = RtlDecodeSystemPointer(g_pfnSE_DllUnloaded);
1418                 SE_DllUnloaded(LdrEntry);
1419             }
1420 
1421             /* Unlink it */
1422             CurrentEntry = LdrEntry;
1423             RemoveEntryList(&CurrentEntry->InInitializationOrderLinks);
1424             RemoveEntryList(&CurrentEntry->InMemoryOrderLinks);
1425             RemoveEntryList(&CurrentEntry->HashLinks);
1426 
1427             /* If there's more then one active unload */
1428             if (LdrpActiveUnloadCount > 1)
1429             {
1430                 /* Flush the cached DLL handle and clear the list */
1431                 LdrpLoadedDllHandleCache = NULL;
1432                 CurrentEntry->InMemoryOrderLinks.Flink = NULL;
1433             }
1434 
1435             /* Add the entry on the unload list */
1436             InsertTailList(&LdrpUnloadHead, &CurrentEntry->HashLinks);
1437         }
1438     }
1439 
1440     /* Only call the entrypoints once */
1441     if (LdrpActiveUnloadCount > 1) goto Quickie;
1442 
1443     /* Now loop the unload list and create our own */
1444     InitializeListHead(&UnloadList);
1445     CurrentEntry = NULL;
1446     NextEntry = LdrpUnloadHead.Flink;
1447     while (NextEntry != &LdrpUnloadHead)
1448     {
1449         /* Get the current entry */
1450         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1451 
1452         LdrpRecordUnloadEvent(LdrEntry);
1453 
1454         /* Set the entry and clear it from the list */
1455         CurrentEntry = LdrEntry;
1456         LdrpLoadedDllHandleCache = NULL;
1457         CurrentEntry->InMemoryOrderLinks.Flink = NULL;
1458 
1459         /* Move it from the global to the local list */
1460         RemoveEntryList(&CurrentEntry->HashLinks);
1461         InsertTailList(&UnloadList, &CurrentEntry->HashLinks);
1462 
1463         /* Get the entrypoint */
1464         EntryPoint = LdrEntry->EntryPoint;
1465 
1466         /* Check if we should call it */
1467         if ((EntryPoint) && (LdrEntry->Flags & LDRP_PROCESS_ATTACH_CALLED))
1468         {
1469             /* Show message */
1470             if (ShowSnaps)
1471             {
1472                 DPRINT1("LDR: Calling deinit %p\n", EntryPoint);
1473             }
1474 
1475             /* Set up the Act Ctx */
1476             ActCtx.Size = sizeof(ActCtx);
1477             ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1478             RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1479 
1480             /* Activate the ActCtx */
1481             RtlActivateActivationContextUnsafeFast(&ActCtx,
1482                                                    LdrEntry->EntryPointActivationContext);
1483 
1484             /* Call the entrypoint */
1485             _SEH2_TRY
1486             {
1487                 LdrpCallInitRoutine(LdrEntry->EntryPoint,
1488                                     LdrEntry->DllBase,
1489                                     DLL_PROCESS_DETACH,
1490                                     NULL);
1491             }
1492             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1493             {
1494                 DPRINT1("WARNING: Exception 0x%x during LdrpCallInitRoutine(DLL_PROCESS_DETACH) for %wZ\n",
1495                         _SEH2_GetExceptionCode(), &LdrEntry->BaseDllName);
1496             }
1497             _SEH2_END;
1498 
1499             /* Release the context */
1500             RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1501         }
1502 
1503         /* Remove it from the list */
1504         RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
1505         CurrentEntry = NULL;
1506         NextEntry = LdrpUnloadHead.Flink;
1507     }
1508 
1509     /* Now loop our local list */
1510     NextEntry = UnloadList.Flink;
1511     while (NextEntry != &UnloadList)
1512     {
1513         /* Get the entry */
1514         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1515         NextEntry = NextEntry->Flink;
1516         CurrentEntry = LdrEntry;
1517 
1518         /* Notify Application Verifier */
1519         if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK)
1520         {
1521             AVrfDllUnloadNotification(LdrEntry);
1522         }
1523 
1524         /* Show message */
1525         if (ShowSnaps)
1526         {
1527             DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
1528         }
1529 
1530         /* Check if this is a .NET executable */
1531         CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1532                                                     TRUE,
1533                                                     IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
1534                                                     &ComSectionSize);
1535         if (CorImageData)
1536         {
1537             /* FIXME */
1538             DPRINT1(".NET Images are not supported yet\n");
1539         }
1540 
1541         /* Check if we should unmap*/
1542         if (!(CurrentEntry->Flags & LDR_COR_OWNS_UNMAP))
1543         {
1544             /* Unmap the DLL */
1545             Status = NtUnmapViewOfSection(NtCurrentProcess(),
1546                                           CurrentEntry->DllBase);
1547             ASSERT(NT_SUCCESS(Status));
1548         }
1549 
1550         /* Unload the alternate resource module, if any */
1551         LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
1552 
1553         /* FIXME: Send shutdown notification */
1554         //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
1555 
1556         /* Check if a Hotpatch is active */
1557         if (LdrEntry->PatchInformation)
1558         {
1559             /* FIXME */
1560             DPRINT1("We don't support Hotpatching yet\n");
1561         }
1562 
1563         /* Deallocate the Entry */
1564         LdrpFinalizeAndDeallocateDataTableEntry(CurrentEntry);
1565 
1566         /* If this is the cached entry, invalidate it */
1567         if (LdrpGetModuleHandleCache == CurrentEntry)
1568         {
1569             LdrpGetModuleHandleCache = NULL;
1570         }
1571     }
1572 
1573 Quickie:
1574     /* Decrease unload count */
1575     LdrpActiveUnloadCount--;
1576     if (!LdrpInLdrInit) RtlLeaveCriticalSection(Peb->LoaderLock);
1577 
1578     /* Return to caller */
1579     return Status;
1580 }
1581 
1582 /*
1583  * @implemented
1584  */
1585 BOOLEAN
1586 NTAPI
1587 RtlDllShutdownInProgress(VOID)
1588 {
1589     /* Return the internal global */
1590     return LdrpShutdownInProgress;
1591 }
1592 
1593 /*
1594  * @implemented
1595  */
1596 PIMAGE_BASE_RELOCATION
1597 NTAPI
1598 LdrProcessRelocationBlock(IN ULONG_PTR Address,
1599                           IN ULONG Count,
1600                           IN PUSHORT TypeOffset,
1601                           IN LONG_PTR Delta)
1602 {
1603     return LdrProcessRelocationBlockLongLong(Address, Count, TypeOffset, Delta);
1604 }
1605 
1606 /* FIXME: Add to ntstatus.mc */
1607 #define STATUS_MUI_FILE_NOT_FOUND        ((NTSTATUS)0xC00B0001L)
1608 
1609 /*
1610  * @implemented
1611  */
1612 NTSTATUS
1613 NTAPI
1614 LdrLoadAlternateResourceModule(IN PVOID Module,
1615                                IN PWSTR Buffer)
1616 {
1617     /* Is MUI Support enabled? */
1618     if (!LdrAlternateResourcesEnabled()) return STATUS_SUCCESS;
1619 
1620     UNIMPLEMENTED;
1621     return STATUS_MUI_FILE_NOT_FOUND;
1622 }
1623 
1624 /*
1625  * @implemented
1626  */
1627 BOOLEAN
1628 NTAPI
1629 LdrUnloadAlternateResourceModule(IN PVOID BaseAddress)
1630 {
1631     ULONG_PTR Cookie;
1632 
1633     /* Acquire the loader lock */
1634     LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &Cookie);
1635 
1636     /* Check if there's any alternate resources loaded */
1637     if (AlternateResourceModuleCount)
1638     {
1639         UNIMPLEMENTED;
1640     }
1641 
1642     /* Release the loader lock */
1643     LdrUnlockLoaderLock(1, Cookie);
1644 
1645     /* All done */
1646     return TRUE;
1647 }
1648 
1649 /*
1650  * @unimplemented
1651  */
1652 BOOLEAN
1653 NTAPI
1654 LdrFlushAlternateResourceModules(VOID)
1655 {
1656     UNIMPLEMENTED;
1657     return FALSE;
1658 }
1659 
1660 /*
1661  * @unimplemented
1662  * See https://www.kernelmode.info/forum/viewtopic.php?t=991
1663  */
1664 NTSTATUS
1665 NTAPI
1666 LdrSetAppCompatDllRedirectionCallback(
1667     _In_ ULONG Flags,
1668     _In_ PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION CallbackFunction,
1669     _In_opt_ PVOID CallbackData)
1670 {
1671     UNIMPLEMENTED;
1672     return STATUS_NOT_IMPLEMENTED;
1673 }
1674 
1675 BOOLEAN
1676 NTAPI
1677 LdrInitShimEngineDynamic(IN PVOID BaseAddress)
1678 {
1679     ULONG_PTR Cookie;
1680     NTSTATUS Status = LdrLockLoaderLock(0, NULL, &Cookie);
1681     if (NT_SUCCESS(Status))
1682     {
1683         if (!g_pShimEngineModule)
1684         {
1685             g_pShimEngineModule = BaseAddress;
1686             LdrpGetShimEngineInterface();
1687         }
1688         LdrUnlockLoaderLock(0, Cookie);
1689         return TRUE;
1690     }
1691     return FALSE;
1692 }
1693 
1694 /* EOF */
1695