[SHELL32] Fix Control_RunDLLW (#5400)
[reactos.git] / dll / ntdll / ldr / ldrapi.c
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 */