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