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