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