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