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