* Sync to recent trunk (r52563).
[reactos.git] / dll / ntdll / ldr / ldrutils.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User-Mode Library
4 * FILE: dll/ntdll/ldr/ldrutils.c
5 * PURPOSE: Internal Loader Utility Functions
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 #define IMAGE_DLLCHARACTERISTICS_WX86_DLL 0x1000
18
19 LIST_ENTRY LdrpUnloadHead;
20 PLDR_DATA_TABLE_ENTRY LdrpLoadedDllHandleCache;
21 PLDR_DATA_TABLE_ENTRY LdrpGetModuleHandleCache;
22
23 #define LDR_GET_HASH_ENTRY(x) (RtlUpcaseUnicodeChar((x)) & (LDR_HASH_TABLE_ENTRIES - 1))
24
25 /* FUNCTIONS *****************************************************************/
26
27 BOOLEAN
28 NTAPI
29 LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint,
30 PVOID BaseAddress,
31 ULONG Reason,
32 PVOID Context)
33 {
34 /* Call the entry */
35 return EntryPoint(BaseAddress, Reason, Context);
36 }
37
38 VOID
39 NTAPI
40 LdrpUpdateLoadCount3(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
41 IN ULONG Flags,
42 OUT PUNICODE_STRING UpdateString)
43 {
44 PIMAGE_BOUND_FORWARDER_REF NewImportForwarder;
45
46
47 PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry;
48 PIMAGE_IMPORT_DESCRIPTOR ImportEntry;
49 PIMAGE_THUNK_DATA FirstThunk;
50 PLDR_DATA_TABLE_ENTRY Entry;
51 PUNICODE_STRING ImportNameUnic;
52 ANSI_STRING ImportNameAnsi;
53 LPSTR ImportName;
54 ULONG ImportSize;
55 NTSTATUS Status;
56 ULONG i;
57
58 /* Check the action we need to perform */
59 if (Flags == LDRP_UPDATE_REFCOUNT)
60 {
61 /* Make sure entry is not being loaded already */
62 if (LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS)
63 return;
64
65 LdrEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
66 }
67 else if (Flags == LDRP_UPDATE_DEREFCOUNT)
68 {
69 /* Make sure the entry is not being unloaded already */
70 if (LdrEntry->Flags & LDRP_UNLOAD_IN_PROGRESS)
71 return;
72
73 LdrEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
74 }
75
76 /* Go through all bound DLLs and dereference them */
77 ImportNameUnic = &NtCurrentTeb()->StaticUnicodeString;
78
79 /* Try to get the new import entry */
80 BoundEntry = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(LdrEntry->DllBase,
81 TRUE,
82 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
83 &ImportSize);
84
85 if (BoundEntry)
86 {
87 /* Set entry flags if refing/derefing */
88 if (Flags == LDRP_UPDATE_REFCOUNT)
89 LdrEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
90 else if (Flags == LDRP_UPDATE_DEREFCOUNT)
91 LdrEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
92
93 while (BoundEntry->OffsetModuleName)
94 {
95 /* Get pointer to the current import name */
96 ImportName = (PCHAR)BoundEntry + BoundEntry->OffsetModuleName;
97
98 RtlInitAnsiString(&ImportNameAnsi, ImportName);
99 Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
100
101 if (NT_SUCCESS(Status))
102 {
103 if (LdrpCheckForLoadedDll(NULL,
104 ImportNameUnic,
105 TRUE,
106 FALSE,
107 &Entry))
108 {
109 if (Entry->LoadCount != -1)
110 {
111 /* Perform the required action */
112 switch (Flags)
113 {
114 case LDRP_UPDATE_REFCOUNT:
115 Entry->LoadCount++;
116 break;
117 case LDRP_UPDATE_DEREFCOUNT:
118 Entry->LoadCount--;
119 break;
120 case LDRP_UPDATE_PIN:
121 Entry->LoadCount = -1;
122 break;
123 }
124
125 /* Show snaps */
126 if (ShowSnaps)
127 {
128 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags, ImportNameUnic, Entry->LoadCount);
129 }
130 }
131
132 /* Recurse into this entry */
133 LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
134 }
135 }
136
137 /* Go through forwarders */
138 NewImportForwarder = (PIMAGE_BOUND_FORWARDER_REF)(BoundEntry + 1);
139 for (i=0; i<BoundEntry->NumberOfModuleForwarderRefs; i++)
140 {
141 ImportName = (PCHAR)BoundEntry + NewImportForwarder->OffsetModuleName;
142
143 RtlInitAnsiString(&ImportNameAnsi, ImportName);
144 Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
145 if (NT_SUCCESS(Status))
146 {
147 if (LdrpCheckForLoadedDll(NULL,
148 ImportNameUnic,
149 TRUE,
150 FALSE,
151 &Entry))
152 {
153 if (Entry->LoadCount != -1)
154 {
155 /* Perform the required action */
156 switch (Flags)
157 {
158 case LDRP_UPDATE_REFCOUNT:
159 Entry->LoadCount++;
160 break;
161 case LDRP_UPDATE_DEREFCOUNT:
162 Entry->LoadCount--;
163 break;
164 case LDRP_UPDATE_PIN:
165 Entry->LoadCount = -1;
166 break;
167 }
168
169 /* Show snaps */
170 if (ShowSnaps)
171 {
172 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags, ImportNameUnic, Entry->LoadCount);
173 }
174 }
175
176 /* Recurse into this entry */
177 LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
178 }
179 }
180
181 NewImportForwarder++;
182 }
183
184 BoundEntry = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)NewImportForwarder;
185 }
186
187 /* We're done */
188 return;
189 }
190
191 /* Check oldstyle import descriptor */
192 ImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(LdrEntry->DllBase,
193 TRUE,
194 IMAGE_DIRECTORY_ENTRY_IMPORT,
195 &ImportSize);
196 if (ImportEntry)
197 {
198 /* There is old one, so go through its entries */
199 while (ImportEntry->Name && ImportEntry->FirstThunk)
200 {
201 FirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrEntry->DllBase + ImportEntry->FirstThunk);
202
203 /* Skip this entry if needed */
204 if (!FirstThunk->u1.Function)
205 {
206 ImportEntry++;
207 continue;
208 }
209
210 ImportName = (PSZ)((ULONG_PTR)LdrEntry->DllBase + ImportEntry->Name);
211
212 RtlInitAnsiString(&ImportNameAnsi, ImportName);
213 Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
214 if (NT_SUCCESS(Status))
215 {
216 if (LdrpCheckForLoadedDll(NULL,
217 ImportNameUnic,
218 TRUE,
219 FALSE,
220 &Entry))
221 {
222 if (Entry->LoadCount != -1)
223 {
224 /* Perform the required action */
225 switch (Flags)
226 {
227 case LDRP_UPDATE_REFCOUNT:
228 Entry->LoadCount++;
229 break;
230 case LDRP_UPDATE_DEREFCOUNT:
231 Entry->LoadCount--;
232 break;
233 case LDRP_UPDATE_PIN:
234 Entry->LoadCount = -1;
235 break;
236 }
237
238 /* Show snaps */
239 if (ShowSnaps)
240 {
241 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags, ImportNameUnic, Entry->LoadCount);
242 }
243 }
244
245 /* Recurse */
246 LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
247 }
248 }
249
250 /* Go to the next entry */
251 ImportEntry++;
252 }
253 }
254 }
255
256 VOID
257 NTAPI
258 LdrpUpdateLoadCount2(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
259 IN ULONG Flags)
260 {
261 WCHAR Buffer[MAX_PATH];
262 UNICODE_STRING UpdateString;
263
264 /* Setup the string */
265 UpdateString.Buffer = Buffer;
266 UpdateString.Length = 0;
267 UpdateString.MaximumLength = sizeof(Buffer);
268
269 /* Call the extended API */
270 LdrpUpdateLoadCount3(LdrEntry, Flags, &UpdateString);
271 }
272
273 VOID
274 NTAPI
275 LdrpTlsCallback(PVOID BaseAddress, ULONG Reason)
276 {
277 PIMAGE_TLS_DIRECTORY TlsDirectory;
278 PIMAGE_TLS_CALLBACK *Array, Callback;
279 ULONG Size;
280
281 /* Get the TLS Directory */
282 TlsDirectory = RtlImageDirectoryEntryToData(BaseAddress,
283 TRUE,
284 IMAGE_DIRECTORY_ENTRY_TLS,
285 &Size);
286
287 /* Protect against invalid pointers */
288 _SEH2_TRY
289 {
290 /* Make sure it's valid and we have an array */
291 if (TlsDirectory && (Array = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks))
292 {
293 /* Display debug */
294 if (ShowSnaps)
295 {
296 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
297 BaseAddress, TlsDirectory, Array);
298 }
299
300 /* Loop the array */
301 while (*Array)
302 {
303 /* Get the TLS Entrypoint */
304 Callback = *Array++;
305
306 /* Display debug */
307 if (ShowSnaps)
308 {
309 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
310 BaseAddress, Callback);
311 }
312
313 /* Call it */
314 LdrpCallDllEntry((PDLLMAIN_FUNC)Callback, BaseAddress, Reason, NULL);
315 }
316 }
317 }
318 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
319 {
320 /* Do nothing */
321 }
322 _SEH2_END;
323 }
324
325 NTSTATUS
326 NTAPI
327 LdrpCodeAuthzCheckDllAllowed(PUNICODE_STRING FullName,
328 HANDLE DllHandle)
329 {
330 return STATUS_SUCCESS;
331 }
332
333 NTSTATUS
334 NTAPI
335 LdrpCreateDllSection(IN PUNICODE_STRING FullName,
336 IN HANDLE DllHandle,
337 IN PULONG DllCharacteristics OPTIONAL,
338 OUT PHANDLE SectionHandle)
339 {
340 HANDLE FileHandle;
341 NTSTATUS Status;
342 OBJECT_ATTRIBUTES ObjectAttributes;
343 IO_STATUS_BLOCK IoStatusBlock;
344 ULONG_PTR HardErrorParameters[1];
345 ULONG Response;
346 SECTION_IMAGE_INFORMATION SectionImageInfo;
347
348 /* Check if we don't already have a handle */
349 if (!DllHandle)
350 {
351 /* Create the object attributes */
352 InitializeObjectAttributes(&ObjectAttributes,
353 FullName,
354 OBJ_CASE_INSENSITIVE,
355 NULL,
356 NULL);
357
358 /* Open the DLL */
359 Status = NtOpenFile(&FileHandle,
360 SYNCHRONIZE | FILE_EXECUTE | FILE_READ_DATA,
361 &ObjectAttributes,
362 &IoStatusBlock,
363 FILE_SHARE_READ | FILE_SHARE_DELETE,
364 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
365
366 /* Check if we failed */
367 if (!NT_SUCCESS(Status))
368 {
369 /* Attempt to open for execute only */
370 Status = NtOpenFile(&FileHandle,
371 SYNCHRONIZE | FILE_EXECUTE,
372 &ObjectAttributes,
373 &IoStatusBlock,
374 FILE_SHARE_READ | FILE_SHARE_DELETE,
375 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
376
377 /* Check if this failed too */
378 if (!NT_SUCCESS(Status))
379 {
380 /* Show debug message */
381 if (ShowSnaps)
382 {
383 DPRINT1("LDR: LdrpCreateDllSection - NtOpenFile failed; status = %x\n",
384 Status);
385 }
386
387 /* Make sure to return an expected status code */
388 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
389 {
390 /* Callers expect this instead */
391 Status = STATUS_DLL_NOT_FOUND;
392 }
393
394 /* Return an empty section handle */
395 *SectionHandle = NULL;
396 return Status;
397 }
398 }
399 }
400 else
401 {
402 /* Use the handle we already have */
403 FileHandle = DllHandle;
404 }
405
406 /* Create a section for the DLL */
407 Status = NtCreateSection(SectionHandle,
408 SECTION_MAP_READ | SECTION_MAP_EXECUTE |
409 SECTION_MAP_WRITE | SECTION_QUERY,
410 NULL,
411 NULL,
412 PAGE_EXECUTE,
413 SEC_IMAGE,
414 FileHandle);
415
416 /* If mapping failed, raise a hard error */
417 if (!NT_SUCCESS(Status))
418 {
419 /* Forget the handle */
420 *SectionHandle = NULL;
421
422 /* Give the DLL name */
423 HardErrorParameters[0] = (ULONG_PTR)FullName;
424
425 /* Raise the error */
426 ZwRaiseHardError(STATUS_INVALID_IMAGE_FORMAT,
427 1,
428 1,
429 HardErrorParameters,
430 OptionOk,
431 &Response);
432
433 /* Increment the error count */
434 if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
435 }
436
437 /* Check for Safer restrictions */
438 if (DllCharacteristics &&
439 !(*DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WX86_DLL))
440 {
441 /* Make sure it's executable */
442 Status = ZwQuerySection(*SectionHandle,
443 SectionImageInformation,
444 &SectionImageInfo,
445 sizeof(SECTION_IMAGE_INFORMATION),
446 NULL);
447 if (NT_SUCCESS(Status))
448 {
449 /* Check if it's executable */
450 if (SectionImageInfo.ImageContainsCode)
451 {
452 /* It is, check safer */
453 Status = LdrpCodeAuthzCheckDllAllowed(FullName, DllHandle);
454 if (!NT_SUCCESS(Status) && (Status != STATUS_NOT_FOUND))
455 {
456 /* Show debug message */
457 if (ShowSnaps)
458 {
459 DPRINT1("LDR: Loading of (%wZ) blocked by Winsafer\n",
460 &FullName);
461 }
462 }
463 else
464 {
465 /* We're fine, return normally */
466 goto Quickie;
467 }
468 }
469 }
470
471 /* Failure case, close section handle */
472 NtClose(*SectionHandle);
473 *SectionHandle = NULL;
474 }
475
476 Quickie:
477 /* Close the file handle, we don't need it */
478 NtClose(FileHandle);
479
480 /* Return status */
481 return Status;
482 }
483
484 BOOLEAN
485 NTAPI
486 LdrpResolveDllName(PWSTR DllPath,
487 PWSTR DllName,
488 PUNICODE_STRING FullDllName,
489 PUNICODE_STRING BaseDllName)
490 {
491 PWCHAR NameBuffer, p1, p2 = 0;
492 ULONG Length;
493 ULONG BufSize = 500;
494
495 /* Allocate space for full DLL name */
496 FullDllName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufSize + sizeof(UNICODE_NULL));
497 if (!FullDllName->Buffer) return FALSE;
498
499 Length = RtlDosSearchPath_U(DllPath ? DllPath : LdrpDefaultPath.Buffer,
500 DllName,
501 NULL,
502 BufSize,
503 FullDllName->Buffer,
504 &BaseDllName->Buffer);
505
506 if (!Length || Length > BufSize)
507 {
508 if (ShowSnaps)
509 {
510 DPRINT1("LDR: LdrResolveDllName - Unable to find ");
511 DPRINT1("%ws from %ws\n", DllName, DllPath ? DllPath : LdrpDefaultPath.Buffer);
512 }
513
514 RtlFreeUnicodeString(FullDllName);
515 return FALSE;
516 }
517
518 /* Construct full DLL name */
519 FullDllName->Length = Length;
520 FullDllName->MaximumLength = FullDllName->Length + sizeof(UNICODE_NULL);
521
522 /* Allocate a new buffer */
523 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullDllName->MaximumLength);
524 if (!NameBuffer)
525 {
526 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName->Buffer);
527 return FALSE;
528 }
529
530 /* Copy over the contents from the previous one and free it */
531 RtlCopyMemory(NameBuffer, FullDllName->Buffer, FullDllName->MaximumLength);
532 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName->Buffer);
533 FullDllName->Buffer = NameBuffer;
534
535 /* Find last backslash */
536 p1 = FullDllName->Buffer;
537 while (*p1)
538 {
539 if (*p1++ == L'\\')
540 {
541 p2 = p1;
542 }
543 }
544
545 /* If found, set p1 to it, otherwise p1 points to the beginning of DllName */
546 if (p2)
547 p1 = p2;
548 else
549 p1 = DllName;
550
551 p2 = p1;
552
553 /* Calculate remaining length */
554 while (*p1) ++p1;
555
556 /* Construct base DLL name */
557 BaseDllName->Length = (ULONG_PTR)p1 - (ULONG_PTR)p2;
558 BaseDllName->MaximumLength = BaseDllName->Length + sizeof(UNICODE_NULL);
559 BaseDllName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BaseDllName->MaximumLength);
560
561 if (!BaseDllName->Buffer)
562 {
563 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
564 return FALSE;
565 }
566
567 /* Copy base dll name to the new buffer */
568 RtlMoveMemory(BaseDllName->Buffer,
569 p2,
570 BaseDllName->Length);
571
572 /* Null-terminate the string */
573 BaseDllName->Buffer[BaseDllName->Length / sizeof(WCHAR)] = 0;
574
575 return TRUE;
576 }
577
578 PVOID
579 NTAPI
580 LdrpFetchAddressOfEntryPoint(PVOID ImageBase)
581 {
582 PIMAGE_NT_HEADERS NtHeaders;
583 ULONG_PTR EntryPoint;
584
585 /* Get entry point offset from NT headers */
586 NtHeaders = RtlImageNtHeader(ImageBase);
587 EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
588
589 /* If it's 0 - return so */
590 if (!EntryPoint) return NULL;
591
592 /* Add image base */
593 EntryPoint += (ULONG_PTR)ImageBase;
594
595 /* Return calculated pointer */
596 return (PVOID)EntryPoint;
597 }
598
599 HANDLE
600 NTAPI
601 LdrpCheckForKnownDll(PWSTR DllName,
602 PUNICODE_STRING FullDllName,
603 PUNICODE_STRING BaseDllName)
604 {
605 OBJECT_ATTRIBUTES ObjectAttributes;
606 HANDLE Section = NULL;
607 UNICODE_STRING DllNameUnic;
608 NTSTATUS Status;
609 PCHAR p1;
610 PWCHAR p2;
611
612 /* Upgrade DllName to a unicode string */
613 RtlInitUnicodeString(&DllNameUnic, DllName);
614
615 /* Get the activation context */
616 Status = RtlFindActivationContextSectionString(0,
617 NULL,
618 ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
619 &DllNameUnic,
620 NULL);
621
622 /* Check if it's a SxS or not */
623 if (Status == STATUS_SXS_SECTION_NOT_FOUND ||
624 Status == STATUS_SXS_KEY_NOT_FOUND)
625 {
626 /* Set up BaseDllName */
627 BaseDllName->Length = DllNameUnic.Length;
628 BaseDllName->MaximumLength = DllNameUnic.MaximumLength;
629 BaseDllName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
630 0,
631 DllNameUnic.MaximumLength);
632 if (!BaseDllName->Buffer) return NULL;
633
634 /* Copy the contents there */
635 RtlMoveMemory(BaseDllName->Buffer, DllNameUnic.Buffer, DllNameUnic.MaximumLength);
636
637 /* Set up FullDllName */
638 FullDllName->Length = LdrpKnownDllPath.Length + BaseDllName->Length + sizeof(WCHAR);
639 FullDllName->MaximumLength = FullDllName->Length + sizeof(UNICODE_NULL);
640 FullDllName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FullDllName->MaximumLength);
641 if (!FullDllName->Buffer)
642 {
643 /* Free base name and fail */
644 RtlFreeHeap(RtlGetProcessHeap(), 0, BaseDllName->Buffer);
645 return NULL;
646 }
647
648 RtlMoveMemory(FullDllName->Buffer, LdrpKnownDllPath.Buffer, LdrpKnownDllPath.Length);
649
650 /* Put a slash there */
651 p1 = (PCHAR)FullDllName->Buffer + LdrpKnownDllPath.Length;
652 p2 = (PWCHAR)p1;
653 *p2++ = (WCHAR)'\\';
654 p1 = (PCHAR)p2;
655
656 /* Set up DllNameUnic for a relative path */
657 DllNameUnic.Buffer = (PWSTR)p1;
658 DllNameUnic.Length = BaseDllName->Length;
659 DllNameUnic.MaximumLength = DllNameUnic.Length + sizeof(UNICODE_NULL);
660
661 /* Copy the contents */
662 RtlMoveMemory(p1, BaseDllName->Buffer, BaseDllName->MaximumLength);
663
664 /* There are all names, init attributes and open the section */
665 InitializeObjectAttributes(&ObjectAttributes,
666 &DllNameUnic,
667 OBJ_CASE_INSENSITIVE,
668 LdrpKnownDllObjectDirectory,
669 NULL);
670
671 Status = NtOpenSection(&Section,
672 SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
673 &ObjectAttributes);
674 if (!NT_SUCCESS(Status))
675 {
676 /* Opening failed, free resources */
677 Section = NULL;
678 RtlFreeHeap(RtlGetProcessHeap(), 0, BaseDllName->Buffer);
679 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName->Buffer);
680 }
681 }
682 else
683 {
684 if (!NT_SUCCESS(Status)) Section = NULL;
685 }
686
687 /* Return section handle */
688 return Section;
689 }
690
691 NTSTATUS
692 NTAPI
693 LdrpSetProtection(PVOID ViewBase,
694 BOOLEAN Restore)
695 {
696 PIMAGE_NT_HEADERS NtHeaders;
697 PIMAGE_SECTION_HEADER Section;
698 NTSTATUS Status;
699 PVOID SectionBase;
700 SIZE_T SectionSize;
701 ULONG NewProtection, OldProtection, i;
702
703 /* Get the NT headers */
704 NtHeaders = RtlImageNtHeader(ViewBase);
705
706 /* Compute address of the first section header */
707 Section = (PIMAGE_SECTION_HEADER)(
708 (ULONG_PTR)NtHeaders + sizeof(ULONG) + sizeof(IMAGE_FILE_HEADER) +
709 NtHeaders->FileHeader.SizeOfOptionalHeader);
710
711 /* Go through all sections */
712 for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
713 {
714 if (Section->SizeOfRawData &&
715 !(Section->Characteristics & IMAGE_SCN_MEM_WRITE))
716 {
717 /* This section is not writable and has some size, so we need to change
718 its protection */
719 if (Restore)
720 {
721 /* Set it to either EXECUTE or READONLY */
722 if (Section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
723 NewProtection = PAGE_EXECUTE;
724 else
725 NewProtection = PAGE_READONLY;
726
727 /* Add PAGE_NOCACHE if needed */
728 if (Section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED)
729 NewProtection |= PAGE_NOCACHE;
730 }
731 else
732 {
733 /* Enable write access */
734 NewProtection = PAGE_READWRITE;
735 }
736
737 SectionBase = (PVOID)((ULONG_PTR)ViewBase + Section->VirtualAddress);
738 SectionSize = Section->SizeOfRawData;
739
740 if (SectionSize)
741 {
742 /* Set protection */
743 Status = ZwProtectVirtualMemory(NtCurrentProcess(),
744 &SectionBase,
745 &SectionSize,
746 NewProtection,
747 &OldProtection);
748
749 if (!NT_SUCCESS(Status)) return Status;
750 }
751 }
752
753 /* Move to the next section */
754 Section++;
755 }
756
757 /* Flush instruction cache if necessary */
758 if (Restore) ZwFlushInstructionCache(NtCurrentProcess(), NULL, 0);
759
760 return STATUS_SUCCESS;
761 }
762
763 NTSTATUS
764 NTAPI
765 LdrpMapDll(IN PWSTR SearchPath OPTIONAL,
766 IN PWSTR DllPath2,
767 IN PWSTR DllName OPTIONAL,
768 IN PULONG DllCharacteristics,
769 IN BOOLEAN Static,
770 IN BOOLEAN Redirect,
771 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
772 {
773 PTEB Teb = NtCurrentTeb();
774 PPEB Peb = NtCurrentPeb();
775 PWCHAR p1 = DllName;
776 WCHAR TempChar;
777 BOOLEAN KnownDll = FALSE;
778 UNICODE_STRING FullDllName, BaseDllName;
779 HANDLE SectionHandle = NULL, DllHandle = 0;
780 UNICODE_STRING NtPathDllName;
781 ULONG_PTR HardErrorParameters[2];
782 UNICODE_STRING HardErrorDllName, HardErrorDllPath;
783 ULONG Response;
784 SIZE_T ViewSize = 0;
785 PVOID ViewBase = NULL;
786 PVOID ArbitraryUserPointer;
787 PIMAGE_NT_HEADERS NtHeaders;
788 NTSTATUS HardErrorStatus, Status;
789 BOOLEAN OverlapDllFound = FALSE;
790 ULONG_PTR ImageBase, ImageEnd;
791 PLIST_ENTRY ListHead, NextEntry;
792 PLDR_DATA_TABLE_ENTRY CandidateEntry, LdrEntry;
793 ULONG_PTR CandidateBase, CandidateEnd;
794 UNICODE_STRING OverlapDll;
795 BOOLEAN RelocatableDll = TRUE;
796 UNICODE_STRING IllegalDll;
797 PVOID RelocData;
798 ULONG RelocDataSize = 0;
799
800 // FIXME: AppCompat stuff is missing
801
802 if (ShowSnaps)
803 {
804 DPRINT1("LDR: LdrpMapDll: Image Name %ws, Search Path %ws\n",
805 DllName,
806 SearchPath ? SearchPath : L"");
807 }
808
809 /* Check if we have a known dll directory */
810 if (LdrpKnownDllObjectDirectory)
811 {
812 /* Check if the path is full */
813 while (*p1)
814 {
815 TempChar = *p1++;
816 if (TempChar == '\\' || TempChar == '/' )
817 {
818 /* Complete path, don't do Known Dll lookup */
819 goto SkipCheck;
820 }
821 }
822
823 /* Try to find a Known DLL */
824 SectionHandle = LdrpCheckForKnownDll(DllName,
825 &FullDllName,
826 &BaseDllName);
827 }
828
829 SkipCheck:
830
831 /* Check if the Known DLL Check returned something */
832 if (!SectionHandle)
833 {
834 /* It didn't, so try to resolve the name now */
835 if (LdrpResolveDllName(SearchPath,
836 DllName,
837 &FullDllName,
838 &BaseDllName))
839 {
840 /* Got a name, display a message */
841 if (ShowSnaps)
842 {
843 DPRINT1("LDR: Loading (%s) %wZ\n",
844 Static ? "STATIC" : "DYNAMIC",
845 &FullDllName);
846 }
847
848 /* Convert to NT Name */
849 if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
850 &NtPathDllName,
851 NULL,
852 NULL))
853 {
854 /* Path was invalid */
855 return STATUS_OBJECT_PATH_SYNTAX_BAD;
856 }
857
858 /* Create a section for this dLL */
859 Status = LdrpCreateDllSection(&NtPathDllName,
860 DllHandle,
861 DllCharacteristics,
862 &SectionHandle);
863
864 /* Free the NT Name */
865 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathDllName.Buffer);
866
867 /* If we failed */
868 if (!NT_SUCCESS(Status))
869 {
870 /* Free the name strings and return */
871 RtlFreeUnicodeString(&FullDllName);
872 RtlFreeUnicodeString(&BaseDllName);
873 return Status;
874 }
875 }
876 else
877 {
878 /* We couldn't resolve the name, is this a static load? */
879 if (Static)
880 {
881 /*
882 * This is BAD! Static loads are CRITICAL. Bugcheck!
883 * Initialize the strings for the error
884 */
885 RtlInitUnicodeString(&HardErrorDllName, DllName);
886 RtlInitUnicodeString(&HardErrorDllPath,
887 DllPath2 ? DllPath2 : LdrpDefaultPath.Buffer);
888
889 /* Set them as error parameters */
890 HardErrorParameters[0] = (ULONG_PTR)&HardErrorDllName;
891 HardErrorParameters[1] = (ULONG_PTR)&HardErrorDllPath;
892
893 /* Raise the hard error */
894 NtRaiseHardError(STATUS_DLL_NOT_FOUND,
895 2,
896 0x00000003,
897 HardErrorParameters,
898 OptionOk,
899 &Response);
900
901 /* We're back, where we initializing? */
902 if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
903 }
904
905 /* Return failure */
906 return STATUS_DLL_NOT_FOUND;
907 }
908 }
909 else
910 {
911 /* We have a section handle, so this is a known dll */
912 KnownDll = TRUE;
913 }
914
915 /* Stuff the image name in the TIB, for the debugger */
916 ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
917 Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
918
919 /* Map the DLL */
920 ViewBase = NULL;
921 ViewSize = 0;
922 Status = NtMapViewOfSection(SectionHandle,
923 NtCurrentProcess(),
924 &ViewBase,
925 0,
926 0,
927 NULL,
928 &ViewSize,
929 ViewShare,
930 0,
931 PAGE_READWRITE);
932
933 /* Restore */
934 Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
935
936 /* Fail if we couldn't map it */
937 if (!NT_SUCCESS(Status))
938 {
939 /* Close and return */
940 NtClose(SectionHandle);
941 return Status;
942 }
943
944 /* Get the NT Header */
945 if (!(NtHeaders = RtlImageNtHeader(ViewBase)))
946 {
947 /* Invalid image, unmap, close handle and fail */
948 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
949 NtClose(SectionHandle);
950 return STATUS_INVALID_IMAGE_FORMAT;
951 }
952
953 // FIXME: .NET support is missing
954
955 /* Allocate an entry */
956 if (!(LdrEntry = LdrpAllocateDataTableEntry(ViewBase)))
957 {
958 /* Invalid image, unmap, close handle and fail */
959 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
960 NtClose(SectionHandle);
961 return STATUS_NO_MEMORY;
962 }
963
964 /* Setup the entry */
965 LdrEntry->Flags = Static ? LDRP_STATIC_LINK : 0;
966 if (Redirect) LdrEntry->Flags |= LDRP_REDIRECTED;
967 LdrEntry->LoadCount = 0;
968 LdrEntry->FullDllName = FullDllName;
969 LdrEntry->BaseDllName = BaseDllName;
970 LdrEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrEntry->DllBase);
971
972 /* Show debug message */
973 if (ShowSnaps)
974 {
975 DPRINT1("LDR: LdrpMapDll: Full Name %wZ, Base Name %wZ\n",
976 &FullDllName,
977 &BaseDllName);
978 }
979
980 /* Insert this entry */
981 LdrpInsertMemoryTableEntry(LdrEntry);
982
983 // LdrpSendDllNotifications(LdrEntry, TRUE, Status == STATUS_IMAGE_NOT_AT_BASE)
984
985 /* Check for invalid CPU Image */
986 if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
987 {
988 /* Load our header */
989 PIMAGE_NT_HEADERS ImageNtHeader = RtlImageNtHeader(Peb->ImageBaseAddress);
990
991 /* Assume defaults if we don't have to run the Hard Error path */
992 HardErrorStatus = STATUS_SUCCESS;
993 Response = ResponseCancel;
994
995 /* Are we an NT 3.0 image? [Do these still exist? LOL -- IAI] */
996 if (ImageNtHeader->OptionalHeader.MajorSubsystemVersion <= 3)
997 {
998 /* Reset the entrypoint, save our Dll Name */
999 LdrEntry->EntryPoint = 0;
1000 HardErrorParameters[0] = (ULONG_PTR)&FullDllName;
1001
1002 /* Raise the error */
1003 HardErrorStatus = ZwRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH,
1004 1,
1005 1,
1006 HardErrorParameters,
1007 OptionOkCancel,
1008 &Response);
1009 }
1010
1011 /* Check if the user pressed cancel */
1012 if (NT_SUCCESS(HardErrorStatus) && Response == ResponseCancel)
1013 {
1014 /* Remove the DLL from the lists */
1015 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
1016 RemoveEntryList(&LdrEntry->InMemoryOrderModuleList);
1017 RemoveEntryList(&LdrEntry->HashLinks);
1018
1019 /* Remove the LDR Entry */
1020 RtlFreeHeap(RtlGetProcessHeap(), 0, LdrEntry );
1021
1022 /* Unmap and close section */
1023 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1024 NtClose(SectionHandle);
1025
1026 /* Did we do a hard error? */
1027 if (ImageNtHeader->OptionalHeader.MajorSubsystemVersion <= 3)
1028 {
1029 /* Yup, so increase fatal error count if we are initializing */
1030 if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
1031 }
1032
1033 /* Return failure */
1034 return STATUS_INVALID_IMAGE_FORMAT;
1035 }
1036 }
1037 else
1038 {
1039 /* The image was valid. Is it a DLL? */
1040 if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL)
1041 {
1042 /* Set the DLL Flag */
1043 LdrEntry->Flags |= LDRP_IMAGE_DLL;
1044 }
1045
1046 /* If we're not a DLL, clear the entrypoint */
1047 if (!(LdrEntry->Flags & LDRP_IMAGE_DLL))
1048 {
1049 LdrEntry->EntryPoint = 0;
1050 }
1051 }
1052
1053 /* Return it for the caller */
1054 *DataTableEntry = LdrEntry;
1055
1056 /* Check if we loaded somewhere else */
1057 if (Status == STATUS_IMAGE_NOT_AT_BASE)
1058 {
1059 /* Write the flag */
1060 LdrEntry->Flags |= LDRP_IMAGE_NOT_AT_BASE;
1061
1062 /* Find our region */
1063 ImageBase = (ULONG_PTR)NtHeaders->OptionalHeader.ImageBase;
1064 ImageEnd = ImageBase + ViewSize;
1065
1066 DPRINT1("LDR: LdrpMapDll Relocating Image Name %ws (%p -> %p)\n", DllName, ImageBase, ViewBase);
1067
1068 /* Scan all the modules */
1069 ListHead = &Peb->Ldr->InLoadOrderModuleList;
1070 NextEntry = ListHead->Flink;
1071 while (NextEntry != ListHead)
1072 {
1073 /* Get the entry */
1074 CandidateEntry = CONTAINING_RECORD(NextEntry,
1075 LDR_DATA_TABLE_ENTRY,
1076 InLoadOrderLinks);
1077 NextEntry = NextEntry->Flink;
1078
1079 /* Get the entry's bounds */
1080 CandidateBase = (ULONG_PTR)CandidateEntry->DllBase;
1081 CandidateEnd = CandidateBase + CandidateEntry->SizeOfImage;
1082
1083 /* Make sure this entry isn't unloading */
1084 if (!CandidateEntry->InMemoryOrderModuleList.Flink) continue;
1085
1086 /* Check if our regions are colliding */
1087 if ((ImageBase >= CandidateBase && ImageBase <= CandidateEnd) ||
1088 (ImageEnd >= CandidateBase && ImageEnd <= CandidateEnd) ||
1089 (CandidateBase >= ImageBase && CandidateBase <= ImageEnd))
1090 {
1091 /* Found who is overlapping */
1092 OverlapDllFound = TRUE;
1093 OverlapDll = CandidateEntry->FullDllName;
1094 break;
1095 }
1096 }
1097
1098 /* Check if we found the DLL overlapping with us */
1099 if (!OverlapDllFound)
1100 {
1101 /* It's not another DLL, it's memory already here */
1102 RtlInitUnicodeString(&OverlapDll, L"Dynamically Allocated Memory");
1103 }
1104
1105 DPRINT1("Overlapping DLL: %wZ\n", &OverlapDll);
1106
1107 /* Are we dealing with a DLL? */
1108 if (LdrEntry->Flags & LDRP_IMAGE_DLL)
1109 {
1110 /* Check if relocs were stripped */
1111 if (!(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED))
1112 {
1113 /* Get the relocation data */
1114 RelocData = RtlImageDirectoryEntryToData(ViewBase,
1115 TRUE,
1116 IMAGE_DIRECTORY_ENTRY_BASERELOC,
1117 &RelocDataSize);
1118
1119 /* Does the DLL not have any? */
1120 if (!RelocData && !RelocDataSize)
1121 {
1122 /* We'll allow this and simply continue */
1123 goto NoRelocNeeded;
1124 }
1125 }
1126
1127 /* See if this is an Illegal DLL - IE: user32 and kernel32 */
1128 RtlInitUnicodeString(&IllegalDll,L"user32.dll");
1129 if (RtlEqualUnicodeString(&BaseDllName, &IllegalDll, TRUE))
1130 {
1131 /* Can't relocate user32 */
1132 RelocatableDll = FALSE;
1133 }
1134 else
1135 {
1136 RtlInitUnicodeString(&IllegalDll, L"kernel32.dll");
1137 if (RtlEqualUnicodeString(&BaseDllName, &IllegalDll, TRUE))
1138 {
1139 /* Can't relocate kernel32 */
1140 RelocatableDll = FALSE;
1141 }
1142 }
1143
1144 /* Check if this was a non-relocatable DLL or a known dll */
1145 if (!RelocatableDll && KnownDll)
1146 {
1147 /* Setup for hard error */
1148 HardErrorParameters[0] = (ULONG_PTR)&IllegalDll;
1149 HardErrorParameters[1] = (ULONG_PTR)&OverlapDll;
1150
1151 /* Raise the error */
1152 ZwRaiseHardError(STATUS_ILLEGAL_DLL_RELOCATION,
1153 2,
1154 3,
1155 HardErrorParameters,
1156 OptionOk,
1157 &Response);
1158
1159 /* If initializing, increase the error count */
1160 if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
1161
1162 /* Don't do relocation */
1163 Status = STATUS_CONFLICTING_ADDRESSES;
1164 goto NoRelocNeeded;
1165 }
1166
1167 /* Change the protection to prepare for relocation */
1168 Status = LdrpSetProtection(ViewBase, FALSE);
1169
1170 /* Make sure we changed the protection */
1171 if (NT_SUCCESS(Status))
1172 {
1173 /* Do the relocation */
1174 Status = LdrRelocateImageWithBias(ViewBase, 0LL, NULL, STATUS_SUCCESS,
1175 STATUS_CONFLICTING_ADDRESSES, STATUS_INVALID_IMAGE_FORMAT);
1176
1177 if (NT_SUCCESS(Status))
1178 {
1179 /* Stuff the image name in the TIB, for the debugger */
1180 ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
1181 Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
1182
1183 /* Map the DLL */
1184 Status = NtMapViewOfSection(SectionHandle,
1185 NtCurrentProcess(),
1186 &ViewBase,
1187 0,
1188 0,
1189 NULL,
1190 &ViewSize,
1191 ViewShare,
1192 0,
1193 PAGE_READWRITE);
1194
1195 /* Restore */
1196 Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
1197
1198 /* Return the protection */
1199 Status = LdrpSetProtection(ViewBase, TRUE);
1200 }
1201 }
1202 //FailRelocate:
1203 /* Handle any kind of failure */
1204 if (!NT_SUCCESS(Status))
1205 {
1206 /* Remove it from the lists */
1207 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
1208 RemoveEntryList(&LdrEntry->InMemoryOrderModuleList);
1209 RemoveEntryList(&LdrEntry->HashLinks);
1210
1211 /* Unmap it, clear the entry */
1212 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1213 LdrEntry = NULL;
1214 }
1215
1216 /* Show debug message */
1217 if (ShowSnaps)
1218 {
1219 DPRINT1("LDR: Fixups %successfully re-applied @ %p\n",
1220 NT_SUCCESS(Status) ? "s" : "uns", ViewBase);
1221 }
1222 }
1223 else
1224 {
1225 NoRelocNeeded:
1226 /* Not a DLL, or no relocation needed */
1227 Status = STATUS_SUCCESS;
1228
1229 /* Stuff the image name in the TIB, for the debugger */
1230 ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
1231 Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
1232
1233 /* Map the DLL */
1234 Status = NtMapViewOfSection(SectionHandle,
1235 NtCurrentProcess(),
1236 &ViewBase,
1237 0,
1238 0,
1239 NULL,
1240 &ViewSize,
1241 ViewShare,
1242 0,
1243 PAGE_READWRITE);
1244
1245 /* Restore */
1246 Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
1247
1248 /* Show debug message */
1249 if (ShowSnaps)
1250 {
1251 DPRINT1("LDR: Fixups won't be re-applied to non-Dll @ %p\n", ViewBase);
1252 }
1253 }
1254 }
1255
1256 // FIXME: LdrpCheckCorImage() is missing
1257
1258 /* Check if this is an SMP Machine and a DLL */
1259 if ((LdrpNumberOfProcessors > 1) &&
1260 (LdrEntry && (LdrEntry->Flags & LDRP_IMAGE_DLL)))
1261 {
1262 /* Validate the image for MP */
1263 LdrpValidateImageForMp(LdrEntry);
1264 }
1265
1266 // FIXME: LdrpCorUnloadImage() is missing
1267
1268 /* Close section and return status */
1269 NtClose(SectionHandle);
1270 return Status;
1271 }
1272
1273 PLDR_DATA_TABLE_ENTRY
1274 NTAPI
1275 LdrpAllocateDataTableEntry(IN PVOID BaseAddress)
1276 {
1277 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
1278 PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader(BaseAddress);
1279
1280 DPRINT("LdrpAllocateDataTableEntry(%p), NtHeader %p\n", BaseAddress, NtHeader);
1281
1282 /* Make sure the header is valid */
1283 if (NtHeader)
1284 {
1285 /* Allocate an entry */
1286 LdrEntry = RtlAllocateHeap(RtlGetProcessHeap(),
1287 HEAP_ZERO_MEMORY,
1288 sizeof(LDR_DATA_TABLE_ENTRY));
1289
1290 /* Make sure we got one */
1291 if (LdrEntry)
1292 {
1293 /* Set it up */
1294 LdrEntry->DllBase = BaseAddress;
1295 LdrEntry->SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
1296 LdrEntry->TimeDateStamp = NtHeader->FileHeader.TimeDateStamp;
1297 }
1298 }
1299
1300 /* Return the entry */
1301 return LdrEntry;
1302 }
1303
1304 VOID
1305 NTAPI
1306 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
1307 {
1308 PPEB_LDR_DATA PebData = NtCurrentPeb()->Ldr;
1309 ULONG i;
1310
1311 /* Get the Hash entry */
1312 i = LDR_GET_HASH_ENTRY(LdrEntry->BaseDllName.Buffer[0]);
1313
1314 InsertTailList(&LdrpHashTable[i], &LdrEntry->HashLinks);
1315 InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
1316 InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderModuleList);
1317 }
1318
1319 VOID
1320 NTAPI
1321 LdrpFinalizeAndDeallocateDataTableEntry(PLDR_DATA_TABLE_ENTRY Entry)
1322 {
1323 ASSERT(Entry != NULL);
1324
1325 /* Release the activation context if it exists */
1326 if (Entry->EntryPointActivationContext)
1327 {
1328 /* Check if it wasn't already released */
1329 if ((HANDLE)Entry->EntryPointActivationContext != INVALID_HANDLE_VALUE)
1330 {
1331 RtlReleaseActivationContext(Entry->EntryPointActivationContext);
1332
1333 /* Mark it as invalid */
1334 Entry->EntryPointActivationContext = INVALID_HANDLE_VALUE;
1335 }
1336 }
1337
1338 /* Release the full dll name string */
1339 if (Entry->FullDllName.Buffer)
1340 LdrpFreeUnicodeString(&Entry->FullDllName);
1341
1342 /* Finally free the entry's memory */
1343 RtlFreeHeap(RtlGetProcessHeap(), 0, Entry);
1344 }
1345
1346 BOOLEAN
1347 NTAPI
1348 LdrpCheckForLoadedDllHandle(IN PVOID Base,
1349 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
1350 {
1351 PLDR_DATA_TABLE_ENTRY Current;
1352 PLIST_ENTRY ListHead, Next;
1353
1354 /* Check the cache first */
1355 if (LdrpLoadedDllHandleCache && LdrpLoadedDllHandleCache->DllBase == Base)
1356 {
1357 /* We got lucky, return the cached entry */
1358 *LdrEntry = LdrpLoadedDllHandleCache;
1359 return TRUE;
1360 }
1361
1362 /* Time for a lookup */
1363 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1364 Next = ListHead->Flink;
1365 while(Next != ListHead)
1366 {
1367 /* Get the current entry */
1368 Current = CONTAINING_RECORD(Next,
1369 LDR_DATA_TABLE_ENTRY,
1370 InLoadOrderLinks);
1371
1372 /* Make sure it's not unloading and check for a match */
1373 if ((Current->InMemoryOrderModuleList.Flink) && (Base == Current->DllBase))
1374 {
1375 /* Save in cache */
1376 LdrpLoadedDllHandleCache = Current;
1377
1378 /* Return it */
1379 *LdrEntry = Current;
1380 return TRUE;
1381 }
1382
1383 /* Move to the next one */
1384 Next = Next->Flink;
1385 }
1386
1387 /* Nothing found */
1388 return FALSE;
1389 }
1390
1391 BOOLEAN
1392 NTAPI
1393 LdrpCheckForLoadedDll(IN PWSTR DllPath,
1394 IN PUNICODE_STRING DllName,
1395 IN BOOLEAN Flag,
1396 IN BOOLEAN RedirectedDll,
1397 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
1398 {
1399 ULONG HashIndex;
1400 PLIST_ENTRY ListHead, ListEntry;
1401 PLDR_DATA_TABLE_ENTRY CurEntry;
1402 BOOLEAN FullPath = FALSE;
1403 PWCHAR wc;
1404 WCHAR NameBuf[266];
1405 UNICODE_STRING FullDllName, NtPathName;
1406 ULONG Length;
1407 OBJECT_ATTRIBUTES ObjectAttributes;
1408 NTSTATUS Status;
1409 HANDLE FileHandle, SectionHandle;
1410 IO_STATUS_BLOCK Iosb;
1411 PVOID ViewBase = NULL;
1412 SIZE_T ViewSize = 0;
1413 PIMAGE_NT_HEADERS NtHeader, NtHeader2;
1414
1415 DPRINT("LdrpCheckForLoadedDll('%S' '%wZ' %d %d %p)\n", DllPath, DllName, Flag, RedirectedDll, LdrEntry);
1416
1417 /* Check if a dll name was provided */
1418 if (!DllName->Buffer || !DllName->Buffer[0]) return FALSE;
1419
1420 /* Look in the hash table if flag was set */
1421 lookinhash:
1422 if (Flag)
1423 {
1424 /* Get hash index */
1425 HashIndex = LDR_GET_HASH_ENTRY(DllName->Buffer[0]);
1426
1427 /* Traverse that list */
1428 ListHead = &LdrpHashTable[HashIndex];
1429 ListEntry = ListHead->Flink;
1430 while (ListEntry != ListHead)
1431 {
1432 /* Get the current entry */
1433 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1434
1435 /* Check base name of that module */
1436 if (RtlEqualUnicodeString(DllName, &CurEntry->BaseDllName, TRUE))
1437 {
1438 /* It matches, return it */
1439 *LdrEntry = CurEntry;
1440 return TRUE;
1441 }
1442
1443 /* Advance to the next entry */
1444 ListEntry = ListEntry->Flink;
1445 }
1446
1447 /* Module was not found, return failure */
1448 return FALSE;
1449 }
1450
1451 /* Check if there is a full path in this DLL */
1452 wc = DllName->Buffer;
1453 while (*wc)
1454 {
1455 /* Check for a slash in the current position*/
1456 if (*wc == L'\\' || *wc == L'/')
1457 {
1458 /* Found the slash, so dll name contains path */
1459 FullPath = TRUE;
1460
1461 /* Setup full dll name string */
1462 FullDllName.Buffer = NameBuf;
1463
1464 Length = RtlDosSearchPath_U(DllPath ? DllPath : LdrpDefaultPath.Buffer,
1465 DllName->Buffer,
1466 NULL,
1467 sizeof(NameBuf) - sizeof(UNICODE_NULL),
1468 FullDllName.Buffer,
1469 NULL);
1470
1471 /* Check if that was successful */
1472 if (!Length || Length > sizeof(NameBuf) - sizeof(UNICODE_NULL))
1473 {
1474 if (ShowSnaps)
1475 {
1476 DPRINT1("LDR: LdrpCheckForLoadedDll - Unable To Locate %ws: 0x%08x\n",
1477 DllName->Buffer, Length);
1478 }
1479
1480 /* Return failure */
1481 return FALSE;
1482 }
1483
1484 /* Full dll name is found */
1485 FullDllName.Length = Length;
1486 FullDllName.MaximumLength = FullDllName.Length + sizeof(UNICODE_NULL);
1487 break;
1488 }
1489
1490 wc++;
1491 }
1492
1493 /* Go check the hash table */
1494 if (!FullPath)
1495 {
1496 Flag = TRUE;
1497 goto lookinhash;
1498 }
1499
1500 /* Now go through the InLoadOrder module list */
1501 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1502 ListEntry = ListHead->Flink;
1503
1504 while (ListEntry != ListHead)
1505 {
1506 /* Get the containing record of the current entry and advance to the next one */
1507 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1508 ListEntry = ListEntry->Flink;
1509
1510 /* Check if it's already being unloaded */
1511 if (!CurEntry->InMemoryOrderModuleList.Flink) continue;
1512
1513 /* Check if name matches */
1514 if (RtlEqualUnicodeString(&FullDllName,
1515 &CurEntry->FullDllName,
1516 TRUE))
1517 {
1518 /* Found it */
1519 *LdrEntry = CurEntry;
1520
1521 /* Find activation context */
1522 Status = RtlFindActivationContextSectionString(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, DllName, NULL);
1523 if (!NT_SUCCESS(Status))
1524 return FALSE;
1525 else
1526 return TRUE;
1527 }
1528 }
1529
1530 /* The DLL was not found in the load order modules list. Perform a complex check */
1531
1532 /* Convert given path to NT path */
1533 if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
1534 &NtPathName,
1535 NULL,
1536 NULL))
1537 {
1538 /* Fail if conversion failed */
1539 return FALSE;
1540 }
1541
1542 /* Initialize object attributes and open it */
1543 InitializeObjectAttributes(&ObjectAttributes,
1544 &NtPathName,
1545 OBJ_CASE_INSENSITIVE,
1546 NULL,
1547 NULL);
1548
1549 Status = NtOpenFile(&FileHandle,
1550 SYNCHRONIZE | FILE_EXECUTE,
1551 &ObjectAttributes,
1552 &Iosb,
1553 FILE_SHARE_READ | FILE_SHARE_DELETE,
1554 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
1555
1556 /* Free NT path name */
1557 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
1558
1559 /* If opening the file failed - return failure */
1560 if (!NT_SUCCESS(Status)) return FALSE;
1561
1562 /* Create a section for this file */
1563 Status = NtCreateSection(&SectionHandle,
1564 SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
1565 NULL,
1566 NULL,
1567 PAGE_EXECUTE,
1568 SEC_COMMIT,
1569 FileHandle);
1570
1571 /* Close file handle */
1572 NtClose(FileHandle);
1573
1574 /* If creating section failed - return failure */
1575 if (!NT_SUCCESS(Status)) return FALSE;
1576
1577 /* Map view of this section */
1578 Status = ZwMapViewOfSection(SectionHandle,
1579 NtCurrentProcess(),
1580 &ViewBase,
1581 0,
1582 0,
1583 NULL,
1584 &ViewSize,
1585 ViewShare,
1586 0,
1587 PAGE_EXECUTE);
1588 /* Close section handle */
1589 NtClose(SectionHandle);
1590
1591 /* If section mapping failed - return failure */
1592 if (!NT_SUCCESS(Status)) return FALSE;
1593
1594 /* Get pointer to the NT header of this section */
1595 NtHeader = RtlImageNtHeader(ViewBase);
1596 if (!NtHeader)
1597 {
1598 /* Unmap the section and fail */
1599 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1600 return FALSE;
1601 }
1602
1603 /* Go through the list of modules */
1604 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1605 ListEntry = ListHead->Flink;
1606
1607 while (ListEntry != ListHead)
1608 {
1609 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1610 ListEntry = ListEntry->Flink;
1611
1612 /* Check if it's already being unloaded */
1613 if (!CurEntry->InMemoryOrderModuleList.Flink) continue;
1614
1615 _SEH2_TRY
1616 {
1617 /* Check if timedate stamp and sizes match */
1618 if (CurEntry->TimeDateStamp == NtHeader->FileHeader.TimeDateStamp &&
1619 CurEntry->SizeOfImage == NtHeader->OptionalHeader.SizeOfImage)
1620 {
1621 /* Time, date and size match. Let's compare their headers */
1622 NtHeader2 = RtlImageNtHeader(CurEntry->DllBase);
1623
1624 if (RtlCompareMemory(NtHeader2, NtHeader, sizeof(IMAGE_NT_HEADERS)))
1625 {
1626 /* Headers match too! Finally ask the kernel to compare mapped files */
1627 Status = ZwAreMappedFilesTheSame(CurEntry->DllBase, ViewBase);
1628
1629 if (!NT_SUCCESS(Status))
1630 {
1631 _SEH2_YIELD(continue;)
1632 }
1633 else
1634 {
1635 /* This is our entry! */
1636 *LdrEntry = CurEntry;
1637
1638 /* Unmap the section */
1639 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1640
1641 _SEH2_YIELD(return TRUE;)
1642 }
1643 }
1644 }
1645 }
1646 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
1647 {
1648 _SEH2_YIELD(break;)
1649 }
1650 _SEH2_END;
1651 }
1652
1653 /* Unmap the section */
1654 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1655
1656 return FALSE;
1657 }
1658
1659 NTSTATUS
1660 NTAPI
1661 LdrpGetProcedureAddress(IN PVOID BaseAddress,
1662 IN PANSI_STRING Name,
1663 IN ULONG Ordinal,
1664 OUT PVOID *ProcedureAddress,
1665 IN BOOLEAN ExecuteInit)
1666 {
1667 NTSTATUS Status = STATUS_SUCCESS;
1668 UCHAR ImportBuffer[64];
1669 PLDR_DATA_TABLE_ENTRY LdrEntry;
1670 IMAGE_THUNK_DATA Thunk;
1671 PVOID ImageBase;
1672 PIMAGE_IMPORT_BY_NAME ImportName = NULL;
1673 PIMAGE_EXPORT_DIRECTORY ExportDir;
1674 ULONG ExportDirSize;
1675 PLIST_ENTRY Entry;
1676
1677 /* Show debug message */
1678 if (ShowSnaps) DPRINT1("LDR: LdrGetProcedureAddress by ");
1679
1680 /* Check if we got a name */
1681 if (Name)
1682 {
1683 /* Show debug message */
1684 if (ShowSnaps) DbgPrint("NAME - %s\n", Name->Buffer);
1685
1686 /* Make sure it's not too long */
1687 if ((Name->Length + sizeof(CHAR) + sizeof(USHORT)) > MAXLONG)
1688 {
1689 /* Won't have enough space to add the hint */
1690 return STATUS_NAME_TOO_LONG;
1691 }
1692
1693 /* Check if our buffer is large enough */
1694 if (Name->Length >= (sizeof(ImportBuffer) - sizeof(CHAR)))
1695 {
1696 /* Allocate from heap, plus 2 bytes for the Hint */
1697 ImportName = RtlAllocateHeap(RtlGetProcessHeap(),
1698 0,
1699 Name->Length + sizeof(CHAR) +
1700 sizeof(USHORT));
1701 }
1702 else
1703 {
1704 /* Use our internal buffer */
1705 ImportName = (PIMAGE_IMPORT_BY_NAME)ImportBuffer;
1706 }
1707
1708 /* Clear the hint */
1709 ImportName->Hint = 0;
1710
1711 /* Copy the name and null-terminate it */
1712 RtlMoveMemory(ImportName->Name, Name->Buffer, Name->Length);
1713 ImportName->Name[Name->Length] = 0;
1714
1715 /* Clear the high bit */
1716 ImageBase = ImportName;
1717 Thunk.u1.AddressOfData = 0;
1718 }
1719 else
1720 {
1721 /* Do it by ordinal */
1722 ImageBase = NULL;
1723
1724 /* Show debug message */
1725 if (ShowSnaps) DbgPrint("ORDINAL - %lx\n", Ordinal);
1726
1727 if (Ordinal)
1728 {
1729 Thunk.u1.Ordinal = Ordinal | IMAGE_ORDINAL_FLAG;
1730 }
1731 else
1732 {
1733 /* No ordinal */
1734 DPRINT1("No ordinal and no name\n");
1735 return STATUS_INVALID_PARAMETER;
1736 }
1737 }
1738
1739 /* Acquire lock unless we are initting */
1740 if (!LdrpInLdrInit) RtlEnterCriticalSection(&LdrpLoaderLock);
1741
1742 _SEH2_TRY
1743 {
1744 /* Try to find the loaded DLL */
1745 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1746 {
1747 /* Invalid base */
1748 DPRINT1("Invalid base address %p\n", BaseAddress);
1749 Status = STATUS_DLL_NOT_FOUND;
1750 _SEH2_YIELD(goto Quickie;)
1751 }
1752
1753 /* Get the pointer to the export directory */
1754 ExportDir = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1755 TRUE,
1756 IMAGE_DIRECTORY_ENTRY_EXPORT,
1757 &ExportDirSize);
1758
1759 if (!ExportDir)
1760 {
1761 DPRINT1("Image %wZ has no exports, but were trying to get procedure %s. BaseAddress asked %p, got entry BA %p\n", &LdrEntry->BaseDllName, Name ? Name->Buffer : NULL, BaseAddress, LdrEntry->DllBase);
1762 Status = STATUS_PROCEDURE_NOT_FOUND;
1763 _SEH2_YIELD(goto Quickie;)
1764 }
1765
1766 /* Now get the thunk */
1767 Status = LdrpSnapThunk(LdrEntry->DllBase,
1768 ImageBase,
1769 &Thunk,
1770 &Thunk,
1771 ExportDir,
1772 ExportDirSize,
1773 FALSE,
1774 NULL);
1775
1776 /* Finally, see if we're supposed to run the init routines */
1777 if (ExecuteInit)
1778 {
1779 /*
1780 * It's possible a forwarded entry had us load the DLL. In that case,
1781 * then we will call its DllMain. Use the last loaded DLL for this.
1782 */
1783 Entry = NtCurrentPeb()->Ldr->InInitializationOrderModuleList.Blink;
1784 LdrEntry = CONTAINING_RECORD(Entry,
1785 LDR_DATA_TABLE_ENTRY,
1786 InInitializationOrderModuleList);
1787
1788 /* Make sure we didn't process it yet*/
1789 if (!(LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
1790 {
1791 /* Call the init routine */
1792 _SEH2_TRY
1793 {
1794 Status = LdrpRunInitializeRoutines(NULL);
1795 }
1796 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1797 {
1798 /* Get the exception code */
1799 Status = _SEH2_GetExceptionCode();
1800 }
1801 _SEH2_END;
1802 }
1803 }
1804
1805 /* Make sure we're OK till here */
1806 if (NT_SUCCESS(Status))
1807 {
1808 /* Return the address */
1809 *ProcedureAddress = (PVOID)Thunk.u1.Function;
1810 }
1811 }
1812 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1813 {
1814 /* Just ignore exceptions */
1815 }
1816 _SEH2_END;
1817
1818 Quickie:
1819 /* Cleanup */
1820 if (ImportName && (ImportName != (PIMAGE_IMPORT_BY_NAME)ImportBuffer))
1821 {
1822 /* We allocated from heap, free it */
1823 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName);
1824 }
1825
1826 /* Release the CS if we entered it */
1827 if (!LdrpInLdrInit) RtlLeaveCriticalSection(&LdrpLoaderLock);
1828
1829 /* We're done */
1830 return Status;
1831 }
1832
1833 NTSTATUS
1834 NTAPI
1835 LdrpLoadDll(IN BOOLEAN Redirected,
1836 IN PWSTR DllPath OPTIONAL,
1837 IN PULONG DllCharacteristics OPTIONAL,
1838 IN PUNICODE_STRING DllName,
1839 OUT PVOID *BaseAddress,
1840 IN BOOLEAN CallInit)
1841 {
1842 PPEB Peb = NtCurrentPeb();
1843 NTSTATUS Status = STATUS_SUCCESS;
1844 PWCHAR p1, p2;
1845 WCHAR NameBuffer[266];
1846 LPWSTR RawDllName;
1847 UNICODE_STRING RawDllNameString;
1848 PLDR_DATA_TABLE_ENTRY LdrEntry;
1849 BOOLEAN InInit = LdrpInLdrInit;
1850
1851 /* Find the name without the extension */
1852 p1 = DllName->Buffer;
1853 StartLoop:
1854 p2 = NULL;
1855 while (*p1)
1856 {
1857 if (*p1++ == L'.')
1858 {
1859 p2 = p1;
1860 }
1861 else if (*p1 == L'\\')
1862 {
1863 goto StartLoop;
1864 }
1865 }
1866
1867 /* Save the Raw DLL Name */
1868 RawDllName = NameBuffer;
1869 if (DllName->Length >= sizeof(NameBuffer))
1870 {
1871 /* The DLL's name is too long */
1872 return STATUS_NAME_TOO_LONG;
1873 }
1874 RtlMoveMemory(RawDllName, DllName->Buffer, DllName->Length);
1875
1876 /* Check if no extension was found or if we got a slash */
1877 if (!p2 || *p2 == '\\')
1878 {
1879 /* Check that we have space to add one */
1880 if (DllName->Length + LdrApiDefaultExtension.Length >= sizeof(NameBuffer))
1881 {
1882 /* No space to add the extension */
1883 return STATUS_NAME_TOO_LONG;
1884 }
1885
1886 /* Add it */
1887 RtlMoveMemory((PVOID)((ULONG_PTR)RawDllName + DllName->Length),
1888 LdrApiDefaultExtension.Buffer,
1889 LdrApiDefaultExtension.Length);
1890
1891 /* Save the length to a unicode string */
1892 RawDllNameString.Length = DllName->Length + LdrApiDefaultExtension.Length;
1893
1894 /* Null terminate it */
1895 RawDllName[RawDllNameString.Length / sizeof(WCHAR)] = 0;
1896 }
1897 else
1898 {
1899 /* Null terminate it */
1900 RawDllName[DllName->Length / sizeof(WCHAR)] = 0;
1901
1902 /* Save the length to a unicode string */
1903 RawDllNameString.Length = DllName->Length;
1904 }
1905
1906 /* Now create a unicode string for the DLL's name */
1907 RawDllNameString.MaximumLength = sizeof(NameBuffer);
1908 RawDllNameString.Buffer = NameBuffer;
1909
1910 /* Check for init flag and acquire lock */
1911 if (!InInit) RtlEnterCriticalSection(&LdrpLoaderLock);
1912
1913 /* Show debug message */
1914 if (ShowSnaps)
1915 {
1916 DPRINT1("LDR: LdrLoadDll, loading %ws from %ws\n",
1917 RawDllName,
1918 DllPath ? DllPath : L"");
1919 }
1920
1921 /* Check if the DLL is already loaded */
1922 if (!LdrpCheckForLoadedDll(DllPath,
1923 &RawDllNameString,
1924 FALSE,
1925 Redirected,
1926 &LdrEntry))
1927 {
1928 /* Map it */
1929 Status = LdrpMapDll(DllPath,
1930 DllPath,
1931 NameBuffer,
1932 DllCharacteristics,
1933 FALSE,
1934 Redirected,
1935 &LdrEntry);
1936 if (!NT_SUCCESS(Status)) goto Quickie;
1937
1938 /* Check if IMAGE_FILE_EXECUTABLE_IMAGE was provided */
1939 if (DllCharacteristics &&
1940 (*DllCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
1941 {
1942 LdrEntry->EntryPoint = NULL;
1943 LdrEntry->Flags &= ~LDRP_IMAGE_DLL;
1944 }
1945
1946 /* FIXME Mark the DLL Ranges for Stack Traces later */
1947
1948 /* Make sure it's a DLL */
1949 if (LdrEntry->Flags & LDRP_IMAGE_DLL)
1950 {
1951 /* Check if this is a .NET Image */
1952 if (!(LdrEntry->Flags & LDRP_COR_IMAGE))
1953 {
1954 /* Walk the Import Descriptor */
1955 Status = LdrpWalkImportDescriptor(DllPath, LdrEntry);
1956 }
1957
1958 /* Update load count, unless it's locked */
1959 if (LdrEntry->LoadCount != -1) LdrEntry->LoadCount++;
1960 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
1961
1962 /* Check if we failed */
1963 if (!NT_SUCCESS(Status))
1964 {
1965 /* Clear entrypoint, and insert into list */
1966 LdrEntry->EntryPoint = NULL;
1967 InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
1968 &LdrEntry->InInitializationOrderModuleList);
1969
1970 /* Cancel the load and unload the DLL */
1971 LdrpClearLoadInProgress();
1972 LdrUnloadDll(LdrEntry->DllBase);
1973
1974 /* Return the error */
1975 goto Quickie;
1976 }
1977 }
1978 else if (LdrEntry->LoadCount != -1)
1979 {
1980 /* Increase load count */
1981 LdrEntry->LoadCount++;
1982 }
1983
1984 /* Insert it into the list */
1985 InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
1986 &LdrEntry->InInitializationOrderModuleList);
1987
1988 /* If we have to run the entrypoint, make sure the DB is ready */
1989 if (CallInit && LdrpLdrDatabaseIsSetup)
1990 {
1991 /* FIXME: Notify Shim Engine */
1992
1993 /* Run the init routine */
1994 Status = LdrpRunInitializeRoutines(NULL);
1995 if (!NT_SUCCESS(Status))
1996 {
1997 /* Failed, unload the DLL */
1998 LdrUnloadDll(LdrEntry->DllBase);
1999 }
2000 }
2001 else
2002 {
2003 /* The DB isn't ready, which means we were loaded because of a forwarder */
2004 Status = STATUS_SUCCESS;
2005 }
2006 }
2007 else
2008 {
2009 /* We were already loaded. Are we a DLL? */
2010 if ((LdrEntry->Flags & LDRP_IMAGE_DLL) && (LdrEntry->LoadCount != -1))
2011 {
2012 /* Increase load count */
2013 LdrEntry->LoadCount++;
2014 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
2015
2016 /* Clear the load in progress */
2017 LdrpClearLoadInProgress();
2018 }
2019 else
2020 {
2021 /* Not a DLL, just increase the load count */
2022 if (LdrEntry->LoadCount != -1) LdrEntry->LoadCount++;
2023 }
2024 }
2025
2026 Quickie:
2027 /* Release the lock */
2028 if (!InInit) RtlLeaveCriticalSection(Peb->LoaderLock);
2029
2030 /* Check for success */
2031 if (NT_SUCCESS(Status))
2032 {
2033 /* Return the base address */
2034 *BaseAddress = LdrEntry->DllBase;
2035 }
2036 else
2037 {
2038 /* Nothing found */
2039 *BaseAddress = NULL;
2040 }
2041
2042 /* Return status */
2043 return Status;
2044 }
2045
2046 /*
2047 * @implemented
2048 */
2049 NTSTATUS
2050 NTAPI
2051 LdrUnloadDll(IN PVOID BaseAddress)
2052 {
2053 NTSTATUS Status = STATUS_SUCCESS;
2054 PPEB Peb = NtCurrentPeb();
2055 PLDR_DATA_TABLE_ENTRY LdrEntry, CurrentEntry;
2056 PVOID EntryPoint;
2057 PLIST_ENTRY NextEntry;
2058 LIST_ENTRY UnloadList;
2059 RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
2060 PVOID CorImageData;
2061 ULONG ComSectionSize;
2062
2063 /* Get the LDR Lock */
2064 if (!LdrpInLdrInit) RtlEnterCriticalSection(Peb->LoaderLock);
2065
2066 /* Increase the unload count */
2067 LdrpActiveUnloadCount++;
2068
2069 /* Skip unload */
2070 if (LdrpShutdownInProgress) goto Quickie;
2071
2072 /* Make sure the DLL is valid and get its entry */
2073 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
2074 {
2075 Status = STATUS_DLL_NOT_FOUND;
2076 goto Quickie;
2077 }
2078
2079 /* Check the current Load Count */
2080 if (LdrEntry->LoadCount != -1)
2081 {
2082 /* Decrease it */
2083 LdrEntry->LoadCount--;
2084
2085 /* If it's a dll */
2086 if (LdrEntry->Flags & LDRP_IMAGE_DLL)
2087 {
2088 /* Set up the Act Ctx */
2089 ActCtx.Size = sizeof(ActCtx);
2090 ActCtx.Format = 1;
2091 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
2092
2093 /* Activate the ActCtx */
2094 RtlActivateActivationContextUnsafeFast(&ActCtx,
2095 LdrEntry->EntryPointActivationContext);
2096
2097 /* Update the load count */
2098 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_DEREFCOUNT);
2099
2100 /* Release the context */
2101 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
2102 }
2103 }
2104 else
2105 {
2106 /* The DLL is locked */
2107 goto Quickie;
2108 }
2109
2110 /* Show debug message */
2111 if (ShowSnaps) DPRINT1("LDR: UNINIT LIST\n");
2112
2113 /* Check if this is our only unload */
2114 if (LdrpActiveUnloadCount == 1)
2115 {
2116 /* Initialize the unload list */
2117 InitializeListHead(&LdrpUnloadHead);
2118 }
2119
2120 /* Loop the modules to build the list */
2121 NextEntry = Peb->Ldr->InInitializationOrderModuleList.Blink;
2122 while (NextEntry != &Peb->Ldr->InInitializationOrderModuleList)
2123 {
2124 /* Get the entry */
2125 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
2126 NextEntry = NextEntry->Blink;
2127
2128 /* Remove flag */
2129 LdrEntry->Flags &= ~LDRP_UNLOAD_IN_PROGRESS;
2130
2131 /* If the load count is now 0 */
2132 if (!LdrEntry->LoadCount)
2133 {
2134 /* Show message */
2135 if (ShowSnaps)
2136 {
2137 DPRINT1("(%d) [%ws] %ws (%lx) deinit %lx\n",
2138 LdrpActiveUnloadCount,
2139 LdrEntry->BaseDllName.Buffer,
2140 LdrEntry->FullDllName.Buffer,
2141 (ULONG)LdrEntry->LoadCount,
2142 LdrEntry->EntryPoint);
2143 }
2144
2145 /* FIXME: Call Shim Engine and notify */
2146
2147 /* Unlink it */
2148 CurrentEntry = LdrEntry;
2149 RemoveEntryList(&CurrentEntry->InInitializationOrderModuleList);
2150 RemoveEntryList(&CurrentEntry->InMemoryOrderModuleList);
2151 RemoveEntryList(&CurrentEntry->HashLinks);
2152
2153 /* If there's more then one active unload */
2154 if (LdrpActiveUnloadCount > 1)
2155 {
2156 /* Flush the cached DLL handle and clear the list */
2157 LdrpLoadedDllHandleCache = NULL;
2158 CurrentEntry->InMemoryOrderModuleList.Flink = NULL;
2159 }
2160
2161 /* Add the entry on the unload list */
2162 InsertTailList(&LdrpUnloadHead, &CurrentEntry->HashLinks);
2163 }
2164 }
2165
2166 /* Only call the entrypoints once */
2167 if (LdrpActiveUnloadCount > 1) goto Quickie;
2168
2169 /* Now loop the unload list and create our own */
2170 InitializeListHead(&UnloadList);
2171 CurrentEntry = NULL;
2172 NextEntry = LdrpUnloadHead.Flink;
2173 while (NextEntry != &LdrpUnloadHead)
2174 {
2175 /* If we have an active entry */
2176 if (CurrentEntry)
2177 {
2178 /* Remove it */
2179 RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
2180 CurrentEntry = NULL;
2181
2182 /* Reset list pointers */
2183 NextEntry = LdrpUnloadHead.Flink;
2184 if (NextEntry == &LdrpUnloadHead) break;
2185 }
2186
2187 /* Get the current entry */
2188 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
2189
2190 /* Log the Unload Event */
2191 //LdrpRecordUnloadEvent(LdrEntry);
2192
2193 /* Set the entry and clear it from the list */
2194 CurrentEntry = LdrEntry;
2195 LdrpLoadedDllHandleCache = NULL;
2196 CurrentEntry->InMemoryOrderModuleList.Flink = NULL;
2197
2198 /* Move it from the global to the local list */
2199 RemoveEntryList(&CurrentEntry->HashLinks);
2200 InsertTailList(&UnloadList, &CurrentEntry->HashLinks);
2201
2202 /* Get the entrypoint */
2203 EntryPoint = LdrEntry->EntryPoint;
2204
2205 /* Check if we should call it */
2206 if (EntryPoint && (LdrEntry->Flags & LDRP_PROCESS_ATTACH_CALLED))
2207 {
2208 /* Show message */
2209 if (ShowSnaps)
2210 {
2211 DPRINT1("LDR: Calling deinit %lx\n", EntryPoint);
2212 }
2213
2214 /* Set up the Act Ctx */
2215 ActCtx.Size = sizeof(ActCtx);
2216 ActCtx.Format = 1;
2217 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
2218
2219 /* Activate the ActCtx */
2220 RtlActivateActivationContextUnsafeFast(&ActCtx,
2221 LdrEntry->EntryPointActivationContext);
2222
2223 /* Call the entrypoint */
2224 LdrpCallDllEntry(LdrEntry->EntryPoint,
2225 LdrEntry->DllBase,
2226 DLL_PROCESS_DETACH,
2227 NULL);
2228
2229 /* Release the context */
2230 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
2231 }
2232
2233 /* Remove it from the list */
2234 RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
2235 CurrentEntry = NULL;
2236 NextEntry = LdrpUnloadHead.Flink;
2237 }
2238
2239 /* Now loop our local list */
2240 NextEntry = UnloadList.Flink;
2241 while (NextEntry != &UnloadList)
2242 {
2243 /* Get the entry */
2244 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
2245 NextEntry = NextEntry->Flink;
2246 CurrentEntry = LdrEntry;
2247
2248 /* Notify Application Verifier */
2249 if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK)
2250 {
2251 DPRINT1("We don't support Application Verifier yet\n");
2252 }
2253
2254 /* Show message */
2255 if (ShowSnaps)
2256 {
2257 DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
2258 }
2259
2260 /* Check if this is a .NET executable */
2261 if ((CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
2262 TRUE,
2263 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
2264 &ComSectionSize)))
2265 {
2266 /* FIXME */
2267 DPRINT1(".NET Images are not supported yet\n");
2268 }
2269
2270 /* Check if we should unmap*/
2271 if (!(CurrentEntry->Flags & LDR_COR_OWNS_UNMAP))
2272 {
2273 /* Unmap the DLL */
2274 Status = NtUnmapViewOfSection(NtCurrentProcess(),
2275 CurrentEntry->DllBase);
2276 }
2277
2278 /* Unload the alternate resource module, if any */
2279 LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
2280
2281 /* Send shutdown notification */
2282 //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
2283
2284 /* Check if a Hotpatch is active */
2285 if (LdrEntry->PatchInformation)
2286 {
2287 /* FIXME */
2288 DPRINT1("We don't support Hotpatching yet\n");
2289 }
2290
2291 /* Deallocate the Entry */
2292 LdrpFinalizeAndDeallocateDataTableEntry(CurrentEntry);
2293
2294 /* If this is the cached entry, invalide it */
2295 if (LdrpGetModuleHandleCache == CurrentEntry)
2296 {
2297 LdrpGetModuleHandleCache = NULL;
2298 }
2299 }
2300
2301 Quickie:
2302 /* Decrease unload count */
2303 LdrpActiveUnloadCount--;
2304 if (!LdrpInLdrInit) RtlLeaveCriticalSection(Peb->LoaderLock);
2305
2306 /* FIXME: Rundown the Hotpatch data, if present */
2307
2308 /* Return to caller */
2309 return Status;
2310 }
2311
2312 ULONG
2313 NTAPI
2314 LdrpClearLoadInProgress()
2315 {
2316 PLIST_ENTRY ListHead;
2317 PLIST_ENTRY Entry;
2318 PLDR_DATA_TABLE_ENTRY Module;
2319 ULONG ModulesCount = 0;
2320
2321 /* Traverse the init list */
2322 ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
2323 Entry = ListHead->Flink;
2324
2325 while (Entry != ListHead)
2326 {
2327 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
2328
2329 /* Clear load in progress flag */
2330 Module->Flags &= ~LDRP_LOAD_IN_PROGRESS;
2331
2332 /* Increase counter for modules with entry point count but not processed yet */
2333 if (Module->EntryPoint &&
2334 !(Module->Flags & LDRP_ENTRY_PROCESSED)) ModulesCount++;
2335
2336 /* Advance to the next entry */
2337 Entry = Entry->Flink;
2338 }
2339
2340 return ModulesCount;
2341 }
2342
2343 /*
2344 * @implemented
2345 */
2346 NTSTATUS NTAPI
2347 LdrAddRefDll(IN ULONG Flags,
2348 IN PVOID BaseAddress)
2349 {
2350 PLDR_DATA_TABLE_ENTRY LdrEntry;
2351 NTSTATUS Status = STATUS_SUCCESS;
2352 ULONG Cookie;
2353 BOOLEAN Locked = FALSE;
2354
2355 /* Check for invalid flags */
2356 if (Flags & ~(LDR_PIN_MODULE))
2357 {
2358 /* Fail with invalid parameter status if so */
2359 Status = STATUS_INVALID_PARAMETER;
2360 goto quickie;
2361 }
2362
2363 /* Acquire the loader lock if not in init phase */
2364 if (!LdrpInLdrInit)
2365 {
2366 /* Acquire the lock */
2367 Status = LdrLockLoaderLock(0, NULL, &Cookie);
2368 if (!NT_SUCCESS(Status)) goto quickie;
2369 Locked = TRUE;
2370 }
2371
2372 /* Get this module's data table entry */
2373 if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
2374 {
2375 if (!LdrEntry)
2376 {
2377 /* Shouldn't happen */
2378 Status = STATUS_INTERNAL_ERROR;
2379 goto quickie;
2380 }
2381
2382 /* If this is not a pinned module */
2383 if (LdrEntry->LoadCount != -1)
2384 {
2385 /* Update its load count */
2386 if (Flags & LDR_PIN_MODULE)
2387 {
2388 /* Pin it by setting load count to -1 */
2389 LdrEntry->LoadCount = -1;
2390 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_PIN);
2391 }
2392 else
2393 {
2394 /* Increase its load count by one */
2395 LdrEntry->LoadCount++;
2396 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
2397 }
2398
2399 /* Clear load in progress */
2400 LdrpClearLoadInProgress();
2401 }
2402 }
2403 else
2404 {
2405 /* There was an error getting this module's handle, return invalid param status */
2406 Status = STATUS_INVALID_PARAMETER;
2407 goto quickie;
2408 }
2409
2410 quickie:
2411 if (!NT_SUCCESS(Status))
2412 {
2413 if (ShowSnaps ||
2414 (Status != STATUS_NO_SUCH_FILE &&
2415 Status != STATUS_DLL_NOT_FOUND &&
2416 Status != STATUS_OBJECT_NAME_NOT_FOUND))
2417 {
2418 DPRINT1("LDR: LdrAddRefDll(%p) 0x%08lx\n", BaseAddress);
2419 }
2420 }
2421
2422 /* Release the lock if needed */
2423 if (Locked) LdrUnlockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
2424
2425 return Status;
2426 }
2427
2428
2429 BOOLEAN
2430 NTAPI
2431 LdrpFreeUnicodeString(PUNICODE_STRING String)
2432 {
2433 BOOLEAN Result = FALSE;
2434
2435 ASSERT(String != NULL);
2436
2437 /* If Buffer is not NULL - free it */
2438 if (String->Buffer)
2439 Result = RtlFreeHeap(RtlGetProcessHeap(), 0, String->Buffer);
2440
2441 /* Zero it out */
2442 String->Length = 0;
2443 String->MaximumLength = 0;
2444 String->Buffer = NULL;
2445
2446 return Result;
2447 }
2448
2449 /* EOF */