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