[LDR] In case of unrecoverable failure, always exit
[reactos.git] / reactos / 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 LONG LdrpLoaderLockAcquisitonCount;
19 BOOLEAN LdrpShowRecursiveLoads;
20 UNICODE_STRING LdrApiDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
21
22 /* FUNCTIONS *****************************************************************/
23
24 /*
25 * @implemented
26 */
27 NTSTATUS
28 NTAPI
29 LdrUnlockLoaderLock(IN ULONG Flags,
30 IN ULONG Cookie OPTIONAL)
31 {
32 NTSTATUS Status = STATUS_SUCCESS;
33
34 DPRINT("LdrUnlockLoaderLock(%x %x)\n", Flags, Cookie);
35
36 /* Check for valid flags */
37 if (Flags & ~1)
38 {
39 /* Flags are invalid, check how to fail */
40 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
41 {
42 /* The caller wants us to raise status */
43 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
44 }
45 else
46 {
47 /* A normal failure */
48 return STATUS_INVALID_PARAMETER_1;
49 }
50 }
51
52 /* If we don't have a cookie, just return */
53 if (!Cookie) return STATUS_SUCCESS;
54
55 /* Validate the cookie */
56 if ((Cookie & 0xF0000000) ||
57 ((Cookie >> 16) ^ ((ULONG)(NtCurrentTeb()->RealClientId.UniqueThread) & 0xFFF)))
58 {
59 DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
60
61 /* Invalid cookie, check how to fail */
62 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
63 {
64 /* The caller wants us to raise status */
65 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
66 }
67 else
68 {
69 /* A normal failure */
70 return STATUS_INVALID_PARAMETER_2;
71 }
72 }
73
74 /* Ready to release the lock */
75 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
76 {
77 /* Do a direct leave */
78 RtlLeaveCriticalSection(&LdrpLoaderLock);
79 }
80 else
81 {
82 /* Wrap this in SEH, since we're not supposed to raise */
83 _SEH2_TRY
84 {
85 /* Leave the lock */
86 RtlLeaveCriticalSection(&LdrpLoaderLock);
87 }
88 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
89 {
90 /* We should use the LDR Filter instead */
91 Status = _SEH2_GetExceptionCode();
92 }
93 _SEH2_END;
94 }
95
96 /* All done */
97 return Status;
98 }
99
100 /*
101 * @implemented
102 */
103 NTSTATUS
104 NTAPI
105 LdrLockLoaderLock(IN ULONG Flags,
106 OUT PULONG Result OPTIONAL,
107 OUT PULONG Cookie OPTIONAL)
108 {
109 LONG OldCount;
110 NTSTATUS Status = STATUS_SUCCESS;
111 BOOLEAN InInit = LdrpInLdrInit;
112
113 DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags, Result, Cookie);
114
115 /* Zero out the outputs */
116 if (Result) *Result = 0;
117 if (Cookie) *Cookie = 0;
118
119 /* Validate the flags */
120 if (Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS |
121 LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY))
122 {
123 /* Flags are invalid, check how to fail */
124 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
125 {
126 /* The caller wants us to raise status */
127 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
128 }
129
130 /* A normal failure */
131 return STATUS_INVALID_PARAMETER_1;
132 }
133
134 /* Make sure we got a cookie */
135 if (!Cookie)
136 {
137 /* No cookie 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_3);
142 }
143
144 /* A normal failure */
145 return STATUS_INVALID_PARAMETER_3;
146 }
147
148 /* If the flag is set, make sure we have a valid pointer to use */
149 if ((Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) && !(Result))
150 {
151 /* No pointer to return the data to */
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_2);
156 }
157
158 /* Fail */
159 return STATUS_INVALID_PARAMETER_2;
160 }
161
162 /* Return now if we are in the init phase */
163 if (InInit) return STATUS_SUCCESS;
164
165 /* Check what locking semantic to use */
166 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
167 {
168 /* Check if we should enter or simply try */
169 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
170 {
171 /* Do a try */
172 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
173 {
174 /* It's locked */
175 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
176 goto Quickie;
177 }
178 else
179 {
180 /* It worked */
181 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
182 }
183 }
184 else
185 {
186 /* Do a enter */
187 RtlEnterCriticalSection(&LdrpLoaderLock);
188
189 /* See if result was requested */
190 if (Result) *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
191 }
192
193 /* Increase the acquisition count */
194 OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
195
196 /* Generate a cookie */
197 *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
198 }
199 else
200 {
201 /* Wrap this in SEH, since we're not supposed to raise */
202 _SEH2_TRY
203 {
204 /* Check if we should enter or simply try */
205 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
206 {
207 /* Do a try */
208 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
209 {
210 /* It's locked */
211 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
212 _SEH2_YIELD(return STATUS_SUCCESS);
213 }
214 else
215 {
216 /* It worked */
217 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
218 }
219 }
220 else
221 {
222 /* Do an enter */
223 RtlEnterCriticalSection(&LdrpLoaderLock);
224
225 /* See if result was requested */
226 if (Result) *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
227 }
228
229 /* Increase the acquisition count */
230 OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
231
232 /* Generate a cookie */
233 *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
234 }
235 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
236 {
237 /* We should use the LDR Filter instead */
238 Status = _SEH2_GetExceptionCode();
239 }
240 _SEH2_END;
241 }
242
243 Quickie:
244 return Status;
245 }
246
247 /*
248 * @implemented
249 */
250 NTSTATUS
251 NTAPI
252 LdrLoadDll(IN PWSTR SearchPath OPTIONAL,
253 IN PULONG DllCharacteristics OPTIONAL,
254 IN PUNICODE_STRING DllName,
255 OUT PVOID *BaseAddress)
256 {
257 WCHAR StringBuffer[MAX_PATH];
258 UNICODE_STRING DllString1, DllString2;
259 BOOLEAN RedirectedDll = FALSE;
260 NTSTATUS Status;
261 ULONG Cookie;
262 PUNICODE_STRING OldTldDll;
263 PTEB Teb = NtCurrentTeb();
264
265 /* Initialize the strings */
266 RtlInitUnicodeString(&DllString2, NULL);
267 DllString1.Buffer = StringBuffer;
268 DllString1.Length = 0;
269 DllString1.MaximumLength = sizeof(StringBuffer);
270
271 /* Check if the SxS Assemblies specify another file */
272 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
273 DllName,
274 &LdrApiDefaultExtension,
275 &DllString1,
276 &DllString2,
277 &DllName,
278 NULL,
279 NULL,
280 NULL);
281
282 /* Check success */
283 if (NT_SUCCESS(Status))
284 {
285 /* Let Ldrp know */
286 RedirectedDll = TRUE;
287 }
288 else if (Status != STATUS_SXS_KEY_NOT_FOUND)
289 {
290 /* Unrecoverable SxS failure; did we get a string? */
291 if (DllString2.Buffer)
292 {
293 /* Free the string */
294 RtlFreeUnicodeString(&DllString2);
295 }
296 return Status;
297 }
298
299 /* Lock the loader lock */
300 LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &Cookie);
301
302 /* Check if there's a TLD DLL being loaded */
303 if ((OldTldDll = LdrpTopLevelDllBeingLoaded))
304 {
305 /* This is a recursive load, do something about it? */
306 if (ShowSnaps || LdrpShowRecursiveLoads)
307 {
308 /* Print out debug messages */
309 DPRINT1("[%lx, %lx] LDR: Recursive DLL Load\n",
310 Teb->RealClientId.UniqueProcess,
311 Teb->RealClientId.UniqueThread);
312 DPRINT1("[%lx, %lx] Previous DLL being loaded \"%wZ\"\n",
313 Teb->RealClientId.UniqueProcess,
314 Teb->RealClientId.UniqueThread,
315 OldTldDll);
316 DPRINT1("[%lx, %lx] DLL being requested \"%wZ\"\n",
317 Teb->RealClientId.UniqueProcess,
318 Teb->RealClientId.UniqueThread,
319 DllName);
320
321 /* Was it initializing too? */
322 if (!LdrpCurrentDllInitializer)
323 {
324 DPRINT1("[%lx, %lx] LDR: No DLL Initializer was running\n",
325 Teb->RealClientId.UniqueProcess,
326 Teb->RealClientId.UniqueThread);
327 }
328 else
329 {
330 DPRINT1("[%lx, %lx] DLL whose initializer was currently running \"%wZ\"\n",
331 Teb->ClientId.UniqueProcess,
332 Teb->ClientId.UniqueThread,
333 &LdrpCurrentDllInitializer->BaseDllName);
334 }
335 }
336 }
337
338 /* Set this one as the TLD DLL being loaded*/
339 LdrpTopLevelDllBeingLoaded = DllName;
340
341 /* Load the DLL */
342 Status = LdrpLoadDll(RedirectedDll,
343 SearchPath,
344 DllCharacteristics,
345 DllName,
346 BaseAddress,
347 TRUE);
348
349 /* Restore the old TLD DLL */
350 LdrpTopLevelDllBeingLoaded = OldTldDll;
351
352 /* Release the lock */
353 LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
354
355 /* Do we have a redirect string? */
356 if (DllString2.Buffer) RtlFreeUnicodeString(&DllString2);
357
358 /* Return */
359 return Status;
360 }
361
362 /*
363 * @implemented
364 */
365 NTSTATUS
366 NTAPI
367 LdrFindEntryForAddress(PVOID Address,
368 PLDR_DATA_TABLE_ENTRY *Module)
369 {
370 PLIST_ENTRY ListHead, NextEntry;
371 PLDR_DATA_TABLE_ENTRY LdrEntry;
372 PIMAGE_NT_HEADERS NtHeader;
373 PPEB_LDR_DATA Ldr = NtCurrentPeb()->Ldr;
374 ULONG_PTR DllBase, DllEnd;
375
376 DPRINT("LdrFindEntryForAddress(Address %p)\n", Address);
377
378 /* Nothing to do */
379 if (!Ldr) return STATUS_NO_MORE_ENTRIES;
380
381 /* Loop the module list */
382 ListHead = &Ldr->InMemoryOrderModuleList;
383 NextEntry = ListHead->Flink;
384 while (NextEntry != ListHead)
385 {
386 /* Get the entry and NT Headers */
387 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList);
388 if ((NtHeader = RtlImageNtHeader(LdrEntry->DllBase)))
389 {
390 /* Get the Image Base */
391 DllBase = (ULONG_PTR)LdrEntry->DllBase;
392 DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
393
394 /* Check if they match */
395 if (((ULONG_PTR)Address >= DllBase) &&
396 ((ULONG_PTR)Address < DllEnd))
397 {
398 /* Return it */
399 *Module = LdrEntry;
400 return STATUS_SUCCESS;
401 }
402
403 /* Next Entry */
404 NextEntry = NextEntry->Flink;
405 }
406 }
407
408 /* Nothing found */
409 return STATUS_NO_MORE_ENTRIES;
410 }
411
412 /*
413 * @implemented
414 */
415 NTSTATUS
416 NTAPI
417 LdrGetDllHandleEx(IN ULONG Flags,
418 IN PWSTR DllPath OPTIONAL,
419 IN PULONG DllCharacteristics OPTIONAL,
420 IN PUNICODE_STRING DllName,
421 OUT PVOID *DllHandle OPTIONAL)
422 {
423 NTSTATUS Status = STATUS_DLL_NOT_FOUND;
424 PLDR_DATA_TABLE_ENTRY LdrEntry;
425 UNICODE_STRING RedirectName, DllString1;
426 UNICODE_STRING RawDllName;
427 PUNICODE_STRING pRedirectName = &RedirectName;
428 PUNICODE_STRING CompareName;
429 PWCHAR p1, p2, p3;
430 BOOLEAN Locked = FALSE;
431 BOOLEAN RedirectedDll = FALSE;
432 ULONG Cookie;
433 ULONG LoadFlag;
434
435 /* Initialize the strings */
436 RtlInitUnicodeString(&DllString1, NULL);
437 RtlInitUnicodeString(&RawDllName, NULL);
438 RedirectName = *DllName;
439
440 /* Clear the handle */
441 if (DllHandle) *DllHandle = NULL;
442
443 /* Check for a valid flag */
444 if ((Flags & ~3) || (!DllHandle && !(Flags & 2)))
445 {
446 DPRINT1("Flags are invalid or no DllHandle given\n");
447 return STATUS_INVALID_PARAMETER;
448 }
449
450 /* If not initializing */
451 if (!LdrpInLdrInit)
452 {
453 /* Acquire the lock */
454 Status = LdrLockLoaderLock(0, NULL, &Cookie);
455 Locked = TRUE;
456 }
457
458 /* Check if the SxS Assemblies specify another file */
459 Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
460 pRedirectName,
461 &LdrApiDefaultExtension,
462 NULL,
463 &DllString1,
464 &pRedirectName,
465 NULL,
466 NULL,
467 NULL);
468
469 /* Check success */
470 if (NT_SUCCESS(Status))
471 {
472 /* Let Ldrp know */
473 RedirectedDll = TRUE;
474 }
475 else if (Status != STATUS_SXS_KEY_NOT_FOUND)
476 {
477 /* Unrecoverable SxS failure; */
478 goto Quickie;
479 }
480
481 /* Use the cache if we can */
482 if (LdrpGetModuleHandleCache)
483 {
484 /* Check if we were redirected */
485 if (RedirectedDll)
486 {
487 /* Check the flag */
488 if (LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED)
489 {
490 /* Use the right name */
491 CompareName = &LdrpGetModuleHandleCache->FullDllName;
492 }
493 else
494 {
495 goto DontCompare;
496 }
497 }
498 else
499 {
500 /* Check the flag */
501 if (!(LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED))
502 {
503 /* Use the right name */
504 CompareName = &LdrpGetModuleHandleCache->BaseDllName;
505 }
506 else
507 {
508 goto DontCompare;
509 }
510 }
511
512 /* Check if the name matches */
513 if (RtlEqualUnicodeString(pRedirectName,
514 CompareName,
515 TRUE))
516 {
517 /* Skip the rest */
518 LdrEntry = LdrpGetModuleHandleCache;
519
520 /* Return success */
521 Status = STATUS_SUCCESS;
522
523 goto FoundEntry;
524 }
525 }
526
527 DontCompare:
528 /* Find the name without the extension */
529 p1 = pRedirectName->Buffer;
530 p3 = &p1[pRedirectName->Length / sizeof(WCHAR)];
531 StartLoop:
532 p2 = NULL;
533 while (p1 != p3)
534 {
535 if (*p1++ == L'.')
536 {
537 p2 = p1;
538 }
539 else if (*p1 == L'\\')
540 {
541 goto StartLoop;
542 }
543 }
544
545 /* Check if no extension was found or if we got a slash */
546 if (!p2 || *p2 == L'\\' || *p2 == L'/')
547 {
548 /* Check that we have space to add one */
549 if (pRedirectName->Length + LdrApiDefaultExtension.Length >= MAXLONG)
550 {
551 /* No space to add the extension */
552 return STATUS_NAME_TOO_LONG;
553 }
554
555 /* Setup the string */
556 RawDllName.MaximumLength = pRedirectName->Length + LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL);
557 RawDllName.Length = RawDllName.MaximumLength - sizeof(UNICODE_NULL);
558 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
559 0,
560 RawDllName.MaximumLength);
561
562 /* Copy the buffer */
563 RtlMoveMemory(RawDllName.Buffer,
564 pRedirectName->Buffer,
565 pRedirectName->Length);
566
567 /* Add extension */
568 RtlMoveMemory((PVOID)((ULONG_PTR)RawDllName.Buffer + pRedirectName->Length),
569 LdrApiDefaultExtension.Buffer,
570 LdrApiDefaultExtension.Length);
571
572 /* Null terminate */
573 RawDllName.Buffer[RawDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
574 }
575 else
576 {
577 /* Check if there's something in the name */
578 if (pRedirectName->Length)
579 {
580 /* Check and remove trailing period */
581 if (pRedirectName->Buffer[(pRedirectName->Length - 2) /
582 sizeof(WCHAR)] == '.')
583 {
584 /* Decrease the size */
585 pRedirectName->Length -= sizeof(WCHAR);
586 }
587 }
588
589 /* Setup the string */
590 RawDllName.MaximumLength = pRedirectName->Length + sizeof(WCHAR);
591 RawDllName.Length = pRedirectName->Length;
592 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
593 0,
594 RawDllName.MaximumLength);
595
596 /* Copy the buffer */
597 RtlMoveMemory(RawDllName.Buffer,
598 pRedirectName->Buffer,
599 pRedirectName->Length);
600
601 /* Null terminate */
602 RawDllName.Buffer[RawDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
603 }
604
605 /* Display debug string */
606 if (ShowSnaps)
607 {
608 DPRINT1("LDR: LdrGetDllHandle, searching for %wZ from %ws\n",
609 &RawDllName,
610 DllPath ? ((ULONG_PTR)DllPath == 1 ? L"" : DllPath) : L"");
611 }
612
613 /* Do the lookup */
614 if (LdrpCheckForLoadedDll(DllPath,
615 &RawDllName,
616 ((ULONG_PTR)DllPath == 1) ? TRUE : FALSE,
617 RedirectedDll,
618 &LdrEntry))
619 {
620 /* Update cached entry */
621 LdrpGetModuleHandleCache = LdrEntry;
622
623 /* Return success */
624 Status = STATUS_SUCCESS;
625 }
626 else
627 {
628 /* Make sure to NULL this */
629 LdrEntry = NULL;
630 }
631 FoundEntry:
632 DPRINT("Got LdrEntry->BaseDllName %wZ\n", LdrEntry ? &LdrEntry->BaseDllName : NULL);
633
634 /* Check if we got an entry */
635 if (LdrEntry)
636 {
637 /* Check for success */
638 if (NT_SUCCESS(Status))
639 {
640 /* Check if the DLL is locked */
641 if (LdrEntry->LoadCount != -1)
642 {
643 /* Check what flag we got */
644 if (!(Flags & 1))
645 {
646 /* Check what to do with the load count */
647 if (Flags & 2)
648 {
649 /* Pin it */
650 LdrEntry->LoadCount = -1;
651 LoadFlag = LDRP_UPDATE_PIN;
652 }
653 else
654 {
655 /* Increase the load count */
656 LdrEntry->LoadCount++;
657 LoadFlag = LDRP_UPDATE_REFCOUNT;
658 }
659
660 /* Update the load count now */
661 LdrpUpdateLoadCount2(LdrEntry, LoadFlag);
662 LdrpClearLoadInProgress();
663 }
664 }
665
666 /* Check if the caller is requesting the handle */
667 if (DllHandle) *DllHandle = LdrEntry->DllBase;
668 }
669 }
670 Quickie:
671 /* Free string if needed */
672 if (DllString1.Buffer) RtlFreeUnicodeString(&DllString1);
673
674 /* Free the raw DLL Name if needed */
675 if (RawDllName.Buffer)
676 {
677 /* Free the heap-allocated buffer */
678 RtlFreeHeap(RtlGetProcessHeap(), 0, RawDllName.Buffer);
679 RawDllName.Buffer = NULL;
680 }
681
682 /* Release lock */
683 if (Locked) LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
684
685 /* Return */
686 return Status;
687 }
688
689 /*
690 * @implemented
691 */
692 NTSTATUS
693 NTAPI
694 LdrGetDllHandle(IN PWSTR DllPath OPTIONAL,
695 IN PULONG DllCharacteristics OPTIONAL,
696 IN PUNICODE_STRING DllName,
697 OUT PVOID *DllHandle)
698 {
699 /* Call the newer API */
700 return LdrGetDllHandleEx(TRUE,
701 DllPath,
702 DllCharacteristics,
703 DllName,
704 DllHandle);
705 }
706
707 /*
708 * @implemented
709 */
710 NTSTATUS
711 NTAPI
712 LdrGetProcedureAddress(IN PVOID BaseAddress,
713 IN PANSI_STRING Name,
714 IN ULONG Ordinal,
715 OUT PVOID *ProcedureAddress)
716 {
717 /* Call the internal routine and tell it to execute DllInit */
718 return LdrpGetProcedureAddress(BaseAddress, Name, Ordinal, ProcedureAddress, TRUE);
719 }
720
721 /*
722 * @implemented
723 */
724 NTSTATUS
725 NTAPI
726 LdrVerifyImageMatchesChecksum(IN HANDLE FileHandle,
727 IN PLDR_CALLBACK Callback,
728 IN PVOID CallbackContext,
729 OUT PUSHORT ImageCharacteristics)
730 {
731 FILE_STANDARD_INFORMATION FileStandardInfo;
732 PIMAGE_IMPORT_DESCRIPTOR ImportData;
733 PIMAGE_SECTION_HEADER LastSection;
734 IO_STATUS_BLOCK IoStatusBlock;
735 PIMAGE_NT_HEADERS NtHeader;
736 HANDLE SectionHandle;
737 SIZE_T ViewSize = 0;
738 PVOID ViewBase = NULL;
739 BOOLEAN Result;
740 NTSTATUS Status;
741 PVOID ImportName;
742 ULONG Size;
743
744 DPRINT("LdrVerifyImageMatchesChecksum() called\n");
745
746 /* Create the section */
747 Status = NtCreateSection(&SectionHandle,
748 SECTION_MAP_EXECUTE,
749 NULL,
750 NULL,
751 PAGE_EXECUTE,
752 SEC_COMMIT,
753 FileHandle);
754 if (!NT_SUCCESS(Status))
755 {
756 DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status);
757 return Status;
758 }
759
760 /* Map the section */
761 Status = NtMapViewOfSection(SectionHandle,
762 NtCurrentProcess(),
763 &ViewBase,
764 0,
765 0,
766 NULL,
767 &ViewSize,
768 ViewShare,
769 0,
770 PAGE_EXECUTE);
771 if (!NT_SUCCESS(Status))
772 {
773 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
774 NtClose(SectionHandle);
775 return Status;
776 }
777
778 /* Get the file information */
779 Status = NtQueryInformationFile(FileHandle,
780 &IoStatusBlock,
781 &FileStandardInfo,
782 sizeof(FILE_STANDARD_INFORMATION),
783 FileStandardInformation);
784 if (!NT_SUCCESS(Status))
785 {
786 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
787 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
788 NtClose(SectionHandle);
789 return Status;
790 }
791
792 /* Protect with SEH */
793 _SEH2_TRY
794 {
795 /* Verify the checksum */
796 Result = LdrVerifyMappedImageMatchesChecksum(ViewBase,
797 ViewSize,
798 FileStandardInfo.EndOfFile.LowPart);
799
800 /* Check if a callback was supplied */
801 if (Result && Callback)
802 {
803 /* Get the NT Header */
804 NtHeader = RtlImageNtHeader(ViewBase);
805
806 /* Check if caller requested this back */
807 if (ImageCharacteristics)
808 {
809 /* Return to caller */
810 *ImageCharacteristics = NtHeader->FileHeader.Characteristics;
811 }
812
813 /* Get the Import Directory Data */
814 ImportData = RtlImageDirectoryEntryToData(ViewBase,
815 FALSE,
816 IMAGE_DIRECTORY_ENTRY_IMPORT,
817 &Size);
818
819 /* Make sure there is one */
820 if (ImportData)
821 {
822 /* Loop the imports */
823 while (ImportData->Name)
824 {
825 /* Get the name */
826 ImportName = RtlImageRvaToVa(NtHeader,
827 ViewBase,
828 ImportData->Name,
829 &LastSection);
830
831 /* Notify the callback */
832 Callback(CallbackContext, ImportName);
833 ImportData++;
834 }
835 }
836 }
837 }
838 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
839 {
840 /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
841 Result = FALSE;
842 }
843 _SEH2_END;
844
845 /* Unmap file and close handle */
846 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
847 NtClose(SectionHandle);
848
849 /* Return status */
850 return !Result ? STATUS_IMAGE_CHECKSUM_MISMATCH : Status;
851 }
852
853 NTSTATUS
854 NTAPI
855 LdrQueryProcessModuleInformationEx(IN ULONG ProcessId,
856 IN ULONG Reserved,
857 IN PRTL_PROCESS_MODULES ModuleInformation,
858 IN ULONG Size,
859 OUT PULONG ReturnedSize OPTIONAL)
860 {
861 PLIST_ENTRY ModuleListHead, InitListHead;
862 PLIST_ENTRY Entry, InitEntry;
863 PLDR_DATA_TABLE_ENTRY Module, InitModule;
864 PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL;
865 NTSTATUS Status = STATUS_SUCCESS;
866 ULONG UsedSize = sizeof(ULONG);
867 ANSI_STRING AnsiString;
868 PCHAR p;
869
870 DPRINT("LdrQueryProcessModuleInformation() called\n");
871
872 /* Acquire loader lock */
873 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
874
875 /* Check if we were given enough space */
876 if (Size < UsedSize)
877 {
878 Status = STATUS_INFO_LENGTH_MISMATCH;
879 }
880 else
881 {
882 ModuleInformation->NumberOfModules = 0;
883 ModulePtr = &ModuleInformation->Modules[0];
884 Status = STATUS_SUCCESS;
885 }
886
887 /* Traverse the list of modules */
888 _SEH2_TRY
889 {
890 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
891 Entry = ModuleListHead->Flink;
892
893 while (Entry != ModuleListHead)
894 {
895 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
896
897 DPRINT(" Module %wZ\n", &Module->FullDllName);
898
899 /* Increase the used size */
900 UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION);
901
902 if (UsedSize > Size)
903 {
904 Status = STATUS_INFO_LENGTH_MISMATCH;
905 }
906 else
907 {
908 ModulePtr->ImageBase = Module->DllBase;
909 ModulePtr->ImageSize = Module->SizeOfImage;
910 ModulePtr->Flags = Module->Flags;
911 ModulePtr->LoadCount = Module->LoadCount;
912 ModulePtr->MappedBase = NULL;
913 ModulePtr->InitOrderIndex = 0;
914 ModulePtr->LoadOrderIndex = ModuleInformation->NumberOfModules;
915
916 /* Now get init order index by traversing init list */
917 InitListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
918 InitEntry = InitListHead->Flink;
919
920 while (InitEntry != InitListHead)
921 {
922 InitModule = CONTAINING_RECORD(InitEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
923
924 /* Increase the index */
925 ModulePtr->InitOrderIndex++;
926
927 /* Quit the loop if our module is found */
928 if (InitModule == Module) break;
929
930 /* Advance to the next entry */
931 InitEntry = InitEntry->Flink;
932 }
933
934 /* Prepare ANSI string with the module's name */
935 AnsiString.Length = 0;
936 AnsiString.MaximumLength = sizeof(ModulePtr->FullPathName);
937 AnsiString.Buffer = ModulePtr->FullPathName;
938 RtlUnicodeStringToAnsiString(&AnsiString,
939 &Module->FullDllName,
940 FALSE);
941
942 /* Calculate OffsetToFileName field */
943 p = strrchr(ModulePtr->FullPathName, '\\');
944 if (p != NULL)
945 ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1;
946 else
947 ModulePtr->OffsetToFileName = 0;
948
949 /* Advance to the next module in the output list */
950 ModulePtr++;
951
952 /* Increase number of modules */
953 if (ModuleInformation)
954 ModuleInformation->NumberOfModules++;
955 }
956
957 /* Go to the next entry in the modules list */
958 Entry = Entry->Flink;
959 }
960
961 /* Set returned size if it was provided */
962 if (ReturnedSize)
963 *ReturnedSize = UsedSize;
964 }
965 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
966 {
967 /* Ignoring the exception */
968 } _SEH2_END;
969
970 /* Release the lock */
971 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
972
973 DPRINT("LdrQueryProcessModuleInformation() done\n");
974
975 return Status;
976 }
977
978 /*
979 * @implemented
980 */
981 NTSTATUS
982 NTAPI
983 LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation,
984 IN ULONG Size,
985 OUT PULONG ReturnedSize OPTIONAL)
986 {
987 /* Call Ex version of the API */
988 return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation, Size, ReturnedSize);
989 }
990
991 NTSTATUS
992 NTAPI
993 LdrEnumerateLoadedModules(BOOLEAN ReservedFlag, PLDR_ENUM_CALLBACK EnumProc, PVOID Context)
994 {
995 PLIST_ENTRY ListHead, ListEntry;
996 PLDR_DATA_TABLE_ENTRY LdrEntry;
997 NTSTATUS Status;
998 ULONG Cookie;
999 BOOLEAN Stop = FALSE;
1000
1001 /* Check parameters */
1002 if (ReservedFlag || !EnumProc) return STATUS_INVALID_PARAMETER;
1003
1004 /* Acquire the loader lock */
1005 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1006 if (!NT_SUCCESS(Status)) return Status;
1007
1008 /* Loop all the modules and call enum proc */
1009 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1010 ListEntry = ListHead->Flink;
1011 while (ListHead != ListEntry)
1012 {
1013 /* Get the entry */
1014 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1015
1016 /* Call the enumeration proc inside SEH */
1017 _SEH2_TRY
1018 {
1019 EnumProc(LdrEntry, Context, &Stop);
1020 }
1021 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1022 {
1023 /* Ignoring the exception */
1024 } _SEH2_END;
1025
1026 /* Break if we were asked to stop enumeration */
1027 if (Stop)
1028 {
1029 /* Release loader lock */
1030 Status = LdrUnlockLoaderLock(0, Cookie);
1031
1032 /* Reset any successful status to STATUS_SUCCESS, but leave
1033 failure to the caller */
1034 if (NT_SUCCESS(Status))
1035 Status = STATUS_SUCCESS;
1036
1037 /* Return any possible failure status */
1038 return Status;
1039 }
1040
1041 /* Advance to the next module */
1042 ListEntry = ListEntry->Flink;
1043 }
1044
1045 /* Release loader lock, it must succeed this time */
1046 Status = LdrUnlockLoaderLock(0, Cookie);
1047 ASSERT(NT_SUCCESS(Status));
1048
1049 /* Return success */
1050 return STATUS_SUCCESS;
1051 }
1052
1053 /* EOF */