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