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)
10 /* INCLUDES *****************************************************************/
16 /* GLOBALS *******************************************************************/
18 PLDR_DATA_TABLE_ENTRY LdrpLoadedDllHandleCache
, LdrpGetModuleHandleCache
;
19 BOOLEAN g_ShimsEnabled
;
21 /* FUNCTIONS *****************************************************************/
25 LdrpAllocateUnicodeString(IN OUT PUNICODE_STRING StringOut
,
30 ASSERT(Length
<= UNICODE_STRING_MAX_BYTES
);
33 StringOut
->Length
= 0;
35 /* Make sure it's not mis-aligned */
39 StringOut
->Buffer
= NULL
;
40 StringOut
->MaximumLength
= 0;
41 return STATUS_INVALID_PARAMETER
;
44 /* Allocate the string*/
45 StringOut
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
47 StringOut
->Length
+ sizeof(WCHAR
));
48 if (!StringOut
->Buffer
)
51 StringOut
->MaximumLength
= 0;
52 return STATUS_NO_MEMORY
;
55 /* Null-terminate it */
56 StringOut
->Buffer
[StringOut
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
58 /* Check if this is a maximum-sized string */
59 if (StringOut
->Length
!= UNICODE_STRING_MAX_BYTES
)
61 /* It's not, so set the maximum length to be one char more */
62 StringOut
->MaximumLength
= StringOut
->Length
+ sizeof(UNICODE_NULL
);
66 /* The length is already the maximum possible */
67 StringOut
->MaximumLength
= UNICODE_STRING_MAX_BYTES
;
71 return STATUS_SUCCESS
;
76 LdrpFreeUnicodeString(IN PUNICODE_STRING StringIn
)
78 ASSERT(StringIn
!= NULL
);
80 /* If Buffer is not NULL - free it */
83 RtlFreeHeap(RtlGetProcessHeap(), 0, StringIn
->Buffer
);
87 RtlInitEmptyUnicodeString(StringIn
, NULL
, 0);
91 LdrpCallInitRoutine(IN PDLL_INIT_ROUTINE EntryPoint
,
97 return EntryPoint(BaseAddress
, Reason
, Context
);
100 /* NOTE: This function is broken */
103 LdrpUpdateLoadCount3(IN PLDR_DATA_TABLE_ENTRY LdrEntry
,
105 OUT PUNICODE_STRING UpdateString
)
107 PIMAGE_BOUND_FORWARDER_REF NewImportForwarder
;
108 PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry
;
109 PIMAGE_IMPORT_DESCRIPTOR ImportEntry
;
110 PIMAGE_THUNK_DATA FirstThunk
;
111 PLDR_DATA_TABLE_ENTRY Entry
;
112 PUNICODE_STRING ImportNameUnic
;
113 ANSI_STRING ImportNameAnsi
;
119 /* Check the action we need to perform */
120 if (Flags
== LDRP_UPDATE_REFCOUNT
)
122 /* Make sure entry is not being loaded already */
123 if (LdrEntry
->Flags
& LDRP_LOAD_IN_PROGRESS
)
126 LdrEntry
->Flags
|= LDRP_LOAD_IN_PROGRESS
;
128 else if (Flags
== LDRP_UPDATE_DEREFCOUNT
)
130 /* Make sure the entry is not being unloaded already */
131 if (LdrEntry
->Flags
& LDRP_UNLOAD_IN_PROGRESS
)
134 LdrEntry
->Flags
|= LDRP_UNLOAD_IN_PROGRESS
;
137 /* Go through all bound DLLs and dereference them */
138 ImportNameUnic
= &NtCurrentTeb()->StaticUnicodeString
;
140 /* Try to get the new import entry */
141 BoundEntry
= (PIMAGE_BOUND_IMPORT_DESCRIPTOR
)RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
143 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
,
148 /* Set entry flags if refing/derefing */
149 if (Flags
== LDRP_UPDATE_REFCOUNT
)
150 LdrEntry
->Flags
|= LDRP_LOAD_IN_PROGRESS
;
151 else if (Flags
== LDRP_UPDATE_DEREFCOUNT
)
152 LdrEntry
->Flags
|= LDRP_UNLOAD_IN_PROGRESS
;
154 while (BoundEntry
->OffsetModuleName
)
156 /* Get pointer to the current import name */
157 ImportName
= (PCHAR
)BoundEntry
+ BoundEntry
->OffsetModuleName
;
159 RtlInitAnsiString(&ImportNameAnsi
, ImportName
);
160 Status
= RtlAnsiStringToUnicodeString(ImportNameUnic
, &ImportNameAnsi
, FALSE
);
162 if (NT_SUCCESS(Status
))
164 if (LdrpCheckForLoadedDll(NULL
,
170 if (Entry
->LoadCount
!= 0xFFFF)
172 /* Perform the required action */
175 case LDRP_UPDATE_REFCOUNT
:
178 case LDRP_UPDATE_DEREFCOUNT
:
181 case LDRP_UPDATE_PIN
:
182 Entry
->LoadCount
= 0xFFFF;
189 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags
, ImportNameUnic
, Entry
->LoadCount
);
193 /* Recurse into this entry */
194 LdrpUpdateLoadCount3(Entry
, Flags
, UpdateString
);
198 /* Go through forwarders */
199 NewImportForwarder
= (PIMAGE_BOUND_FORWARDER_REF
)(BoundEntry
+ 1);
200 for (i
=0; i
<BoundEntry
->NumberOfModuleForwarderRefs
; i
++)
202 ImportName
= (PCHAR
)BoundEntry
+ NewImportForwarder
->OffsetModuleName
;
204 RtlInitAnsiString(&ImportNameAnsi
, ImportName
);
205 Status
= RtlAnsiStringToUnicodeString(ImportNameUnic
, &ImportNameAnsi
, FALSE
);
206 if (NT_SUCCESS(Status
))
208 if (LdrpCheckForLoadedDll(NULL
,
214 if (Entry
->LoadCount
!= 0xFFFF)
216 /* Perform the required action */
219 case LDRP_UPDATE_REFCOUNT
:
222 case LDRP_UPDATE_DEREFCOUNT
:
225 case LDRP_UPDATE_PIN
:
226 Entry
->LoadCount
= 0xFFFF;
233 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags
, ImportNameUnic
, Entry
->LoadCount
);
237 /* Recurse into this entry */
238 LdrpUpdateLoadCount3(Entry
, Flags
, UpdateString
);
242 NewImportForwarder
++;
245 BoundEntry
= (PIMAGE_BOUND_IMPORT_DESCRIPTOR
)NewImportForwarder
;
252 /* Check oldstyle import descriptor */
253 ImportEntry
= (PIMAGE_IMPORT_DESCRIPTOR
)RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
255 IMAGE_DIRECTORY_ENTRY_IMPORT
,
259 /* There is old one, so go through its entries */
260 while (ImportEntry
->Name
&& ImportEntry
->FirstThunk
)
262 FirstThunk
= (PIMAGE_THUNK_DATA
)((ULONG_PTR
)LdrEntry
->DllBase
+ ImportEntry
->FirstThunk
);
264 /* Skip this entry if needed */
265 if (!FirstThunk
->u1
.Function
)
271 ImportName
= (PSZ
)((ULONG_PTR
)LdrEntry
->DllBase
+ ImportEntry
->Name
);
273 RtlInitAnsiString(&ImportNameAnsi
, ImportName
);
274 Status
= RtlAnsiStringToUnicodeString(ImportNameUnic
, &ImportNameAnsi
, FALSE
);
275 if (NT_SUCCESS(Status
))
277 if (LdrpCheckForLoadedDll(NULL
,
283 if (Entry
->LoadCount
!= 0xFFFF)
285 /* Perform the required action */
288 case LDRP_UPDATE_REFCOUNT
:
291 case LDRP_UPDATE_DEREFCOUNT
:
294 case LDRP_UPDATE_PIN
:
295 Entry
->LoadCount
= 0xFFFF;
302 DPRINT1("LDR: Flags %d %wZ (%lx)\n", Flags
, ImportNameUnic
, Entry
->LoadCount
);
307 LdrpUpdateLoadCount3(Entry
, Flags
, UpdateString
);
311 /* Go to the next entry */
319 LdrpUpdateLoadCount2(IN PLDR_DATA_TABLE_ENTRY LdrEntry
,
322 WCHAR Buffer
[MAX_PATH
];
323 UNICODE_STRING UpdateString
;
325 /* Setup the string and call the extended API */
326 RtlInitEmptyUnicodeString(&UpdateString
, Buffer
, sizeof(Buffer
));
327 LdrpUpdateLoadCount3(LdrEntry
, Flags
, &UpdateString
);
332 LdrpCallTlsInitializers(IN PVOID BaseAddress
,
335 PIMAGE_TLS_DIRECTORY TlsDirectory
;
336 PIMAGE_TLS_CALLBACK
*Array
, Callback
;
339 /* Get the TLS Directory */
340 TlsDirectory
= RtlImageDirectoryEntryToData(BaseAddress
,
342 IMAGE_DIRECTORY_ENTRY_TLS
,
345 /* Protect against invalid pointers */
348 /* Make sure it's valid */
352 Array
= (PIMAGE_TLS_CALLBACK
*)TlsDirectory
->AddressOfCallBacks
;
358 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
359 BaseAddress
, TlsDirectory
, Array
);
365 /* Get the TLS Entrypoint */
371 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
372 BaseAddress
, Callback
);
376 LdrpCallInitRoutine((PDLL_INIT_ROUTINE
)Callback
,
384 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
393 LdrpCodeAuthzCheckDllAllowed(IN PUNICODE_STRING FullName
,
396 /* Not implemented */
397 return STATUS_SUCCESS
;
402 LdrpCreateDllSection(IN PUNICODE_STRING FullName
,
404 IN PULONG DllCharacteristics OPTIONAL
,
405 OUT PHANDLE SectionHandle
)
409 OBJECT_ATTRIBUTES ObjectAttributes
;
410 IO_STATUS_BLOCK IoStatusBlock
;
411 ULONG_PTR HardErrorParameters
[1];
413 SECTION_IMAGE_INFORMATION SectionImageInfo
;
415 /* Check if we don't already have a handle */
418 /* Create the object attributes */
419 InitializeObjectAttributes(&ObjectAttributes
,
421 OBJ_CASE_INSENSITIVE
,
426 Status
= NtOpenFile(&FileHandle
,
427 SYNCHRONIZE
| FILE_EXECUTE
| FILE_READ_DATA
,
430 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
431 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
433 /* Check if we failed */
434 if (!NT_SUCCESS(Status
))
436 /* Attempt to open for execute only */
437 Status
= NtOpenFile(&FileHandle
,
438 SYNCHRONIZE
| FILE_EXECUTE
,
441 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
442 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
444 /* Check if this failed too */
445 if (!NT_SUCCESS(Status
))
447 /* Show debug message */
450 DPRINT1("LDR: LdrpCreateDllSection - NtOpenFile failed; status = %x\n",
454 /* Make sure to return an expected status code */
455 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
457 /* Callers expect this instead */
458 Status
= STATUS_DLL_NOT_FOUND
;
461 /* Return an empty section handle */
462 *SectionHandle
= NULL
;
469 /* Use the handle we already have */
470 FileHandle
= DllHandle
;
473 /* Create a section for the DLL */
474 Status
= NtCreateSection(SectionHandle
,
475 SECTION_MAP_READ
| SECTION_MAP_EXECUTE
|
476 SECTION_MAP_WRITE
| SECTION_QUERY
,
483 /* If mapping failed, raise a hard error */
484 if (!NT_SUCCESS(Status
))
486 /* Forget the handle */
487 *SectionHandle
= NULL
;
489 /* Give the DLL name */
490 HardErrorParameters
[0] = (ULONG_PTR
)FullName
;
492 /* Raise the error */
493 ZwRaiseHardError(STATUS_INVALID_IMAGE_FORMAT
,
500 /* Increment the error count */
501 if (LdrpInLdrInit
) LdrpFatalHardErrorCount
++;
504 /* Check for Safer restrictions */
505 if (DllCharacteristics
&&
506 !(*DllCharacteristics
& IMAGE_FILE_SYSTEM
))
508 /* Make sure it's executable */
509 Status
= ZwQuerySection(*SectionHandle
,
510 SectionImageInformation
,
512 sizeof(SECTION_IMAGE_INFORMATION
),
514 if (NT_SUCCESS(Status
))
516 /* Bypass the check for .NET images */
517 if (!(SectionImageInfo
.LoaderFlags
& IMAGE_LOADER_FLAGS_COMPLUS
))
519 /* Check with Safer */
520 Status
= LdrpCodeAuthzCheckDllAllowed(FullName
, DllHandle
);
521 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_NOT_FOUND
))
523 /* Show debug message */
526 DPRINT1("LDR: Loading of (%wZ) blocked by Winsafer\n",
530 /* Failure case, close section handle */
531 NtClose(*SectionHandle
);
532 *SectionHandle
= NULL
;
538 /* Failure case, close section handle */
539 NtClose(*SectionHandle
);
540 *SectionHandle
= NULL
;
544 /* Close the file handle, we don't need it */
551 /* NOTE: This function is totally b0rked and doesn't handle the parameters/functionality it should */
554 LdrpResolveDllName(PWSTR DllPath
,
556 PUNICODE_STRING FullDllName
,
557 PUNICODE_STRING BaseDllName
)
559 PWCHAR NameBuffer
, p1
, p2
= 0;
563 /* Allocate space for full DLL name */
564 FullDllName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufSize
+ sizeof(UNICODE_NULL
));
565 if (!FullDllName
->Buffer
) return FALSE
;
567 Length
= RtlDosSearchPath_U(DllPath
? DllPath
: LdrpDefaultPath
.Buffer
,
572 &BaseDllName
->Buffer
);
574 if (!Length
|| Length
> BufSize
)
578 DPRINT1("LDR: LdrResolveDllName - Unable to find ");
579 DPRINT1("%ws from %ws\n", DllName
, DllPath
? DllPath
: LdrpDefaultPath
.Buffer
);
582 RtlFreeUnicodeString(FullDllName
);
586 /* Construct full DLL name */
587 FullDllName
->Length
= Length
;
588 FullDllName
->MaximumLength
= FullDllName
->Length
+ sizeof(UNICODE_NULL
);
590 /* Allocate a new buffer */
591 NameBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FullDllName
->MaximumLength
);
594 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName
->Buffer
);
598 /* Copy over the contents from the previous one and free it */
599 RtlCopyMemory(NameBuffer
, FullDllName
->Buffer
, FullDllName
->MaximumLength
);
600 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName
->Buffer
);
601 FullDllName
->Buffer
= NameBuffer
;
603 /* Find last backslash */
604 p1
= FullDllName
->Buffer
;
613 /* If found, set p1 to it, otherwise p1 points to the beginning of DllName */
621 /* Calculate remaining length */
624 /* Construct base DLL name */
625 BaseDllName
->Length
= (ULONG_PTR
)p1
- (ULONG_PTR
)p2
;
626 BaseDllName
->MaximumLength
= BaseDllName
->Length
+ sizeof(UNICODE_NULL
);
627 BaseDllName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BaseDllName
->MaximumLength
);
629 if (!BaseDllName
->Buffer
)
631 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
635 /* Copy base dll name to the new buffer */
636 RtlMoveMemory(BaseDllName
->Buffer
,
638 BaseDllName
->Length
);
640 /* Null-terminate the string */
641 BaseDllName
->Buffer
[BaseDllName
->Length
/ sizeof(WCHAR
)] = 0;
648 LdrpFetchAddressOfEntryPoint(IN PVOID ImageBase
)
650 PIMAGE_NT_HEADERS NtHeaders
;
651 ULONG_PTR EntryPoint
= 0;
653 /* Get entry point offset from NT headers */
654 NtHeaders
= RtlImageNtHeader(ImageBase
);
658 EntryPoint
= NtHeaders
->OptionalHeader
.AddressOfEntryPoint
;
659 if (EntryPoint
) EntryPoint
+= (ULONG_PTR
)ImageBase
;
662 /* Return calculated pointer (or zero in case of failure) */
663 return (PVOID
)EntryPoint
;
666 /* NOTE: This function is broken, wrong number of parameters, no SxS, etc */
669 LdrpCheckForKnownDll(PWSTR DllName
,
670 PUNICODE_STRING FullDllName
,
671 PUNICODE_STRING BaseDllName
)
673 OBJECT_ATTRIBUTES ObjectAttributes
;
674 HANDLE Section
= NULL
;
675 UNICODE_STRING DllNameUnic
;
680 /* Upgrade DllName to a unicode string */
681 RtlInitUnicodeString(&DllNameUnic
, DllName
);
683 /* Get the activation context */
684 Status
= RtlFindActivationContextSectionString(0,
686 ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION
,
690 /* Check if it's a SxS or not */
691 if (Status
== STATUS_SXS_SECTION_NOT_FOUND
||
692 Status
== STATUS_SXS_KEY_NOT_FOUND
)
694 /* Set up BaseDllName */
695 BaseDllName
->Length
= DllNameUnic
.Length
;
696 BaseDllName
->MaximumLength
= DllNameUnic
.MaximumLength
;
697 BaseDllName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
699 DllNameUnic
.MaximumLength
);
700 if (!BaseDllName
->Buffer
) return NULL
;
702 /* Copy the contents there */
703 RtlMoveMemory(BaseDllName
->Buffer
, DllNameUnic
.Buffer
, DllNameUnic
.MaximumLength
);
705 /* Set up FullDllName */
706 FullDllName
->Length
= LdrpKnownDllPath
.Length
+ BaseDllName
->Length
+ sizeof(WCHAR
);
707 FullDllName
->MaximumLength
= FullDllName
->Length
+ sizeof(UNICODE_NULL
);
708 FullDllName
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FullDllName
->MaximumLength
);
709 if (!FullDllName
->Buffer
)
711 /* Free base name and fail */
712 RtlFreeHeap(RtlGetProcessHeap(), 0, BaseDllName
->Buffer
);
716 RtlMoveMemory(FullDllName
->Buffer
, LdrpKnownDllPath
.Buffer
, LdrpKnownDllPath
.Length
);
718 /* Put a slash there */
719 p1
= (PCHAR
)FullDllName
->Buffer
+ LdrpKnownDllPath
.Length
;
724 /* Set up DllNameUnic for a relative path */
725 DllNameUnic
.Buffer
= (PWSTR
)p1
;
726 DllNameUnic
.Length
= BaseDllName
->Length
;
727 DllNameUnic
.MaximumLength
= DllNameUnic
.Length
+ sizeof(UNICODE_NULL
);
729 /* Copy the contents */
730 RtlMoveMemory(p1
, BaseDllName
->Buffer
, BaseDllName
->MaximumLength
);
732 /* There are all names, init attributes and open the section */
733 InitializeObjectAttributes(&ObjectAttributes
,
735 OBJ_CASE_INSENSITIVE
,
736 LdrpKnownDllObjectDirectory
,
739 Status
= NtOpenSection(&Section
,
740 SECTION_MAP_READ
| SECTION_MAP_EXECUTE
| SECTION_MAP_WRITE
,
742 if (!NT_SUCCESS(Status
))
744 /* Opening failed, free resources */
746 RtlFreeHeap(RtlGetProcessHeap(), 0, BaseDllName
->Buffer
);
747 RtlFreeHeap(RtlGetProcessHeap(), 0, FullDllName
->Buffer
);
752 if (!NT_SUCCESS(Status
)) Section
= NULL
;
755 /* Return section handle */
761 LdrpSetProtection(PVOID ViewBase
,
764 PIMAGE_NT_HEADERS NtHeaders
;
765 PIMAGE_SECTION_HEADER Section
;
769 ULONG NewProtection
, OldProtection
, i
;
771 /* Get the NT headers */
772 NtHeaders
= RtlImageNtHeader(ViewBase
);
773 if (!NtHeaders
) return STATUS_INVALID_IMAGE_FORMAT
;
775 /* Compute address of the first section header */
776 Section
= IMAGE_FIRST_SECTION(NtHeaders
);
778 /* Go through all sections */
779 for (i
= 0; i
< NtHeaders
->FileHeader
.NumberOfSections
; i
++)
781 /* Check for read-only non-zero section */
782 if ((Section
->SizeOfRawData
) &&
783 !(Section
->Characteristics
& IMAGE_SCN_MEM_WRITE
))
785 /* Check if we are setting or restoring protection */
788 /* Set it to either EXECUTE or READONLY */
789 if (Section
->Characteristics
& IMAGE_SCN_MEM_EXECUTE
)
791 NewProtection
= PAGE_EXECUTE
;
795 NewProtection
= PAGE_READONLY
;
798 /* Add PAGE_NOCACHE if needed */
799 if (Section
->Characteristics
& IMAGE_SCN_MEM_NOT_CACHED
)
801 NewProtection
|= PAGE_NOCACHE
;
806 /* Enable write access */
807 NewProtection
= PAGE_READWRITE
;
810 /* Get the section VA */
811 SectionBase
= (PVOID
)((ULONG_PTR
)ViewBase
+ Section
->VirtualAddress
);
812 SectionSize
= Section
->SizeOfRawData
;
816 Status
= ZwProtectVirtualMemory(NtCurrentProcess(),
821 if (!NT_SUCCESS(Status
)) return Status
;
825 /* Move to the next section */
829 /* Flush instruction cache if necessary */
830 if (Restore
) ZwFlushInstructionCache(NtCurrentProcess(), NULL
, 0);
831 return STATUS_SUCCESS
;
834 /* NOTE: Not yet reviewed */
837 LdrpMapDll(IN PWSTR SearchPath OPTIONAL
,
839 IN PWSTR DllName OPTIONAL
,
840 IN PULONG DllCharacteristics
,
843 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
)
845 PTEB Teb
= NtCurrentTeb();
846 PPEB Peb
= NtCurrentPeb();
849 BOOLEAN KnownDll
= FALSE
;
850 UNICODE_STRING FullDllName
, BaseDllName
;
851 HANDLE SectionHandle
= NULL
, DllHandle
= 0;
852 UNICODE_STRING NtPathDllName
;
853 ULONG_PTR HardErrorParameters
[2];
854 UNICODE_STRING HardErrorDllName
, HardErrorDllPath
;
857 PVOID ViewBase
= NULL
;
858 PVOID ArbitraryUserPointer
;
859 PIMAGE_NT_HEADERS NtHeaders
;
860 NTSTATUS HardErrorStatus
, Status
;
861 BOOLEAN OverlapDllFound
= FALSE
;
862 ULONG_PTR ImageBase
, ImageEnd
;
863 PLIST_ENTRY ListHead
, NextEntry
;
864 PLDR_DATA_TABLE_ENTRY CandidateEntry
, LdrEntry
;
865 ULONG_PTR CandidateBase
, CandidateEnd
;
866 UNICODE_STRING OverlapDll
;
867 BOOLEAN RelocatableDll
= TRUE
;
868 UNICODE_STRING IllegalDll
;
870 ULONG RelocDataSize
= 0;
872 // FIXME: AppCompat stuff is missing
876 DPRINT1("LDR: LdrpMapDll: Image Name %ws, Search Path %ws\n",
878 SearchPath
? SearchPath
: L
"");
881 /* Check if we have a known dll directory */
882 if (LdrpKnownDllObjectDirectory
)
884 /* Check if the path is full */
888 if (TempChar
== '\\' || TempChar
== '/' )
890 /* Complete path, don't do Known Dll lookup */
895 /* Try to find a Known DLL */
896 SectionHandle
= LdrpCheckForKnownDll(DllName
,
903 /* Check if the Known DLL Check returned something */
906 /* It didn't, so try to resolve the name now */
907 if (LdrpResolveDllName(SearchPath
,
912 /* Got a name, display a message */
915 DPRINT1("LDR: Loading (%s) %wZ\n",
916 Static
? "STATIC" : "DYNAMIC",
920 /* Convert to NT Name */
921 if (!RtlDosPathNameToNtPathName_U(FullDllName
.Buffer
,
926 /* Path was invalid */
927 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
930 /* Create a section for this dLL */
931 Status
= LdrpCreateDllSection(&NtPathDllName
,
936 /* Free the NT Name */
937 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathDllName
.Buffer
);
940 if (!NT_SUCCESS(Status
))
942 /* Free the name strings and return */
943 RtlFreeUnicodeString(&FullDllName
);
944 RtlFreeUnicodeString(&BaseDllName
);
950 /* We couldn't resolve the name, is this a static load? */
954 * This is BAD! Static loads are CRITICAL. Bugcheck!
955 * Initialize the strings for the error
957 RtlInitUnicodeString(&HardErrorDllName
, DllName
);
958 RtlInitUnicodeString(&HardErrorDllPath
,
959 DllPath2
? DllPath2
: LdrpDefaultPath
.Buffer
);
961 /* Set them as error parameters */
962 HardErrorParameters
[0] = (ULONG_PTR
)&HardErrorDllName
;
963 HardErrorParameters
[1] = (ULONG_PTR
)&HardErrorDllPath
;
965 /* Raise the hard error */
966 NtRaiseHardError(STATUS_DLL_NOT_FOUND
,
973 /* We're back, where we initializing? */
974 if (LdrpInLdrInit
) LdrpFatalHardErrorCount
++;
978 return STATUS_DLL_NOT_FOUND
;
983 /* We have a section handle, so this is a known dll */
987 /* Stuff the image name in the TIB, for the debugger */
988 ArbitraryUserPointer
= Teb
->NtTib
.ArbitraryUserPointer
;
989 Teb
->NtTib
.ArbitraryUserPointer
= FullDllName
.Buffer
;
994 Status
= NtMapViewOfSection(SectionHandle
,
1006 Teb
->NtTib
.ArbitraryUserPointer
= ArbitraryUserPointer
;
1008 /* Fail if we couldn't map it */
1009 if (!NT_SUCCESS(Status
))
1011 /* Close and return */
1012 NtClose(SectionHandle
);
1016 /* Get the NT Header */
1017 if (!(NtHeaders
= RtlImageNtHeader(ViewBase
)))
1019 /* Invalid image, unmap, close handle and fail */
1020 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
1021 NtClose(SectionHandle
);
1022 return STATUS_INVALID_IMAGE_FORMAT
;
1025 // FIXME: .NET support is missing
1027 /* Allocate an entry */
1028 if (!(LdrEntry
= LdrpAllocateDataTableEntry(ViewBase
)))
1030 /* Invalid image, unmap, close handle and fail */
1031 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
1032 NtClose(SectionHandle
);
1033 return STATUS_NO_MEMORY
;
1036 /* Setup the entry */
1037 LdrEntry
->Flags
= Static
? LDRP_STATIC_LINK
: 0;
1038 if (Redirect
) LdrEntry
->Flags
|= LDRP_REDIRECTED
;
1039 LdrEntry
->LoadCount
= 0;
1040 LdrEntry
->FullDllName
= FullDllName
;
1041 LdrEntry
->BaseDllName
= BaseDllName
;
1042 LdrEntry
->EntryPoint
= LdrpFetchAddressOfEntryPoint(LdrEntry
->DllBase
);
1044 /* Show debug message */
1047 DPRINT1("LDR: LdrpMapDll: Full Name %wZ, Base Name %wZ\n",
1052 /* Insert this entry */
1053 LdrpInsertMemoryTableEntry(LdrEntry
);
1055 // LdrpSendDllNotifications(LdrEntry, TRUE, Status == STATUS_IMAGE_NOT_AT_BASE)
1057 /* Check for invalid CPU Image */
1058 if (Status
== STATUS_IMAGE_MACHINE_TYPE_MISMATCH
)
1060 /* Load our header */
1061 PIMAGE_NT_HEADERS ImageNtHeader
= RtlImageNtHeader(Peb
->ImageBaseAddress
);
1063 /* Assume defaults if we don't have to run the Hard Error path */
1064 HardErrorStatus
= STATUS_SUCCESS
;
1065 Response
= ResponseCancel
;
1067 /* Are we an NT 3.0 image? [Do these still exist? LOL -- IAI] */
1068 if (ImageNtHeader
->OptionalHeader
.MajorSubsystemVersion
<= 3)
1070 /* Reset the entrypoint, save our Dll Name */
1071 LdrEntry
->EntryPoint
= 0;
1072 HardErrorParameters
[0] = (ULONG_PTR
)&FullDllName
;
1074 /* Raise the error */
1075 HardErrorStatus
= ZwRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH
,
1078 HardErrorParameters
,
1083 /* Check if the user pressed cancel */
1084 if (NT_SUCCESS(HardErrorStatus
) && Response
== ResponseCancel
)
1086 /* Remove the DLL from the lists */
1087 RemoveEntryList(&LdrEntry
->InLoadOrderLinks
);
1088 RemoveEntryList(&LdrEntry
->InMemoryOrderModuleList
);
1089 RemoveEntryList(&LdrEntry
->HashLinks
);
1091 /* Remove the LDR Entry */
1092 RtlFreeHeap(RtlGetProcessHeap(), 0, LdrEntry
);
1094 /* Unmap and close section */
1095 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
1096 NtClose(SectionHandle
);
1098 /* Did we do a hard error? */
1099 if (ImageNtHeader
->OptionalHeader
.MajorSubsystemVersion
<= 3)
1101 /* Yup, so increase fatal error count if we are initializing */
1102 if (LdrpInLdrInit
) LdrpFatalHardErrorCount
++;
1105 /* Return failure */
1106 return STATUS_INVALID_IMAGE_FORMAT
;
1111 /* The image was valid. Is it a DLL? */
1112 if (NtHeaders
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
1114 /* Set the DLL Flag */
1115 LdrEntry
->Flags
|= LDRP_IMAGE_DLL
;
1118 /* If we're not a DLL, clear the entrypoint */
1119 if (!(LdrEntry
->Flags
& LDRP_IMAGE_DLL
))
1121 LdrEntry
->EntryPoint
= 0;
1125 /* Return it for the caller */
1126 *DataTableEntry
= LdrEntry
;
1128 /* Check if we loaded somewhere else */
1129 if (Status
== STATUS_IMAGE_NOT_AT_BASE
)
1131 /* Write the flag */
1132 LdrEntry
->Flags
|= LDRP_IMAGE_NOT_AT_BASE
;
1134 /* Find our region */
1135 ImageBase
= (ULONG_PTR
)NtHeaders
->OptionalHeader
.ImageBase
;
1136 ImageEnd
= ImageBase
+ ViewSize
;
1138 DPRINT1("LDR: LdrpMapDll Relocating Image Name %ws (%p -> %p)\n", DllName
, ImageBase
, ViewBase
);
1140 /* Scan all the modules */
1141 ListHead
= &Peb
->Ldr
->InLoadOrderModuleList
;
1142 NextEntry
= ListHead
->Flink
;
1143 while (NextEntry
!= ListHead
)
1146 CandidateEntry
= CONTAINING_RECORD(NextEntry
,
1147 LDR_DATA_TABLE_ENTRY
,
1149 NextEntry
= NextEntry
->Flink
;
1151 /* Get the entry's bounds */
1152 CandidateBase
= (ULONG_PTR
)CandidateEntry
->DllBase
;
1153 CandidateEnd
= CandidateBase
+ CandidateEntry
->SizeOfImage
;
1155 /* Make sure this entry isn't unloading */
1156 if (!CandidateEntry
->InMemoryOrderModuleList
.Flink
) continue;
1158 /* Check if our regions are colliding */
1159 if ((ImageBase
>= CandidateBase
&& ImageBase
<= CandidateEnd
) ||
1160 (ImageEnd
>= CandidateBase
&& ImageEnd
<= CandidateEnd
) ||
1161 (CandidateBase
>= ImageBase
&& CandidateBase
<= ImageEnd
))
1163 /* Found who is overlapping */
1164 OverlapDllFound
= TRUE
;
1165 OverlapDll
= CandidateEntry
->FullDllName
;
1170 /* Check if we found the DLL overlapping with us */
1171 if (!OverlapDllFound
)
1173 /* It's not another DLL, it's memory already here */
1174 RtlInitUnicodeString(&OverlapDll
, L
"Dynamically Allocated Memory");
1177 DPRINT1("Overlapping DLL: %wZ\n", &OverlapDll
);
1179 /* Are we dealing with a DLL? */
1180 if (LdrEntry
->Flags
& LDRP_IMAGE_DLL
)
1182 /* Check if relocs were stripped */
1183 if (!(NtHeaders
->FileHeader
.Characteristics
& IMAGE_FILE_RELOCS_STRIPPED
))
1185 /* Get the relocation data */
1186 RelocData
= RtlImageDirectoryEntryToData(ViewBase
,
1188 IMAGE_DIRECTORY_ENTRY_BASERELOC
,
1191 /* Does the DLL not have any? */
1192 if (!RelocData
&& !RelocDataSize
)
1194 /* We'll allow this and simply continue */
1199 /* See if this is an Illegal DLL - IE: user32 and kernel32 */
1200 RtlInitUnicodeString(&IllegalDll
,L
"user32.dll");
1201 if (RtlEqualUnicodeString(&BaseDllName
, &IllegalDll
, TRUE
))
1203 /* Can't relocate user32 */
1204 RelocatableDll
= FALSE
;
1208 RtlInitUnicodeString(&IllegalDll
, L
"kernel32.dll");
1209 if (RtlEqualUnicodeString(&BaseDllName
, &IllegalDll
, TRUE
))
1211 /* Can't relocate kernel32 */
1212 RelocatableDll
= FALSE
;
1216 /* Check if this was a non-relocatable DLL or a known dll */
1217 if (!RelocatableDll
&& KnownDll
)
1219 /* Setup for hard error */
1220 HardErrorParameters
[0] = (ULONG_PTR
)&IllegalDll
;
1221 HardErrorParameters
[1] = (ULONG_PTR
)&OverlapDll
;
1223 /* Raise the error */
1224 ZwRaiseHardError(STATUS_ILLEGAL_DLL_RELOCATION
,
1227 HardErrorParameters
,
1231 /* If initializing, increase the error count */
1232 if (LdrpInLdrInit
) LdrpFatalHardErrorCount
++;
1234 /* Don't do relocation */
1235 Status
= STATUS_CONFLICTING_ADDRESSES
;
1239 /* Change the protection to prepare for relocation */
1240 Status
= LdrpSetProtection(ViewBase
, FALSE
);
1242 /* Make sure we changed the protection */
1243 if (NT_SUCCESS(Status
))
1245 /* Do the relocation */
1246 Status
= LdrRelocateImageWithBias(ViewBase
, 0LL, NULL
, STATUS_SUCCESS
,
1247 STATUS_CONFLICTING_ADDRESSES
, STATUS_INVALID_IMAGE_FORMAT
);
1249 if (NT_SUCCESS(Status
))
1251 /* Stuff the image name in the TIB, for the debugger */
1252 ArbitraryUserPointer
= Teb
->NtTib
.ArbitraryUserPointer
;
1253 Teb
->NtTib
.ArbitraryUserPointer
= FullDllName
.Buffer
;
1256 Status
= NtMapViewOfSection(SectionHandle
,
1268 Teb
->NtTib
.ArbitraryUserPointer
= ArbitraryUserPointer
;
1270 /* Return the protection */
1271 Status
= LdrpSetProtection(ViewBase
, TRUE
);
1275 /* Handle any kind of failure */
1276 if (!NT_SUCCESS(Status
))
1278 /* Remove it from the lists */
1279 RemoveEntryList(&LdrEntry
->InLoadOrderLinks
);
1280 RemoveEntryList(&LdrEntry
->InMemoryOrderModuleList
);
1281 RemoveEntryList(&LdrEntry
->HashLinks
);
1283 /* Unmap it, clear the entry */
1284 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
1288 /* Show debug message */
1291 DPRINT1("LDR: Fixups %successfully re-applied @ %p\n",
1292 NT_SUCCESS(Status
) ? "s" : "uns", ViewBase
);
1298 /* Not a DLL, or no relocation needed */
1299 Status
= STATUS_SUCCESS
;
1301 /* Stuff the image name in the TIB, for the debugger */
1302 ArbitraryUserPointer
= Teb
->NtTib
.ArbitraryUserPointer
;
1303 Teb
->NtTib
.ArbitraryUserPointer
= FullDllName
.Buffer
;
1306 Status
= NtMapViewOfSection(SectionHandle
,
1318 Teb
->NtTib
.ArbitraryUserPointer
= ArbitraryUserPointer
;
1320 /* Show debug message */
1323 DPRINT1("LDR: Fixups won't be re-applied to non-Dll @ %p\n", ViewBase
);
1328 // FIXME: LdrpCheckCorImage() is missing
1330 /* Check if this is an SMP Machine and a DLL */
1331 if ((LdrpNumberOfProcessors
> 1) &&
1332 (LdrEntry
&& (LdrEntry
->Flags
& LDRP_IMAGE_DLL
)))
1334 /* Validate the image for MP */
1335 LdrpValidateImageForMp(LdrEntry
);
1338 // FIXME: LdrpCorUnloadImage() is missing
1340 /* Close section and return status */
1341 NtClose(SectionHandle
);
1345 PLDR_DATA_TABLE_ENTRY
1347 LdrpAllocateDataTableEntry(IN PVOID BaseAddress
)
1349 PLDR_DATA_TABLE_ENTRY LdrEntry
= NULL
;
1350 PIMAGE_NT_HEADERS NtHeader
;
1352 /* Make sure the header is valid */
1353 NtHeader
= RtlImageNtHeader(BaseAddress
);
1354 DPRINT("LdrpAllocateDataTableEntry(%p), NtHeader %p\n", BaseAddress
, NtHeader
);
1358 /* Allocate an entry */
1359 LdrEntry
= RtlAllocateHeap(RtlGetProcessHeap(),
1361 sizeof(LDR_DATA_TABLE_ENTRY
));
1363 /* Make sure we got one */
1367 LdrEntry
->DllBase
= BaseAddress
;
1368 LdrEntry
->SizeOfImage
= NtHeader
->OptionalHeader
.SizeOfImage
;
1369 LdrEntry
->TimeDateStamp
= NtHeader
->FileHeader
.TimeDateStamp
;
1370 LdrEntry
->PatchInformation
= NULL
;
1374 /* Return the entry */
1380 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
1382 PPEB_LDR_DATA PebData
= NtCurrentPeb()->Ldr
;
1385 /* Insert into hash table */
1386 i
= LDR_GET_HASH_ENTRY(LdrEntry
->BaseDllName
.Buffer
[0]);
1387 InsertTailList(&LdrpHashTable
[i
], &LdrEntry
->HashLinks
);
1389 /* Insert into other lists */
1390 InsertTailList(&PebData
->InLoadOrderModuleList
, &LdrEntry
->InLoadOrderLinks
);
1391 InsertTailList(&PebData
->InMemoryOrderModuleList
, &LdrEntry
->InMemoryOrderModuleList
);
1396 LdrpFinalizeAndDeallocateDataTableEntry(IN PLDR_DATA_TABLE_ENTRY Entry
)
1399 ASSERT(Entry
!= NULL
);
1401 /* Release the activation context if it exists and wasn't already released */
1402 if ((Entry
->EntryPointActivationContext
) &&
1403 (Entry
->EntryPointActivationContext
!= INVALID_HANDLE_VALUE
))
1405 /* Mark it as invalid */
1406 RtlReleaseActivationContext(Entry
->EntryPointActivationContext
);
1407 Entry
->EntryPointActivationContext
= INVALID_HANDLE_VALUE
;
1410 /* Release the full dll name string */
1411 if (Entry
->FullDllName
.Buffer
) LdrpFreeUnicodeString(&Entry
->FullDllName
);
1413 /* Finally free the entry's memory */
1414 RtlFreeHeap(RtlGetProcessHeap(), 0, Entry
);
1419 LdrpCheckForLoadedDllHandle(IN PVOID Base
,
1420 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
1422 PLDR_DATA_TABLE_ENTRY Current
;
1423 PLIST_ENTRY ListHead
, Next
;
1425 /* Check the cache first */
1426 if ((LdrpLoadedDllHandleCache
) &&
1427 (LdrpLoadedDllHandleCache
->DllBase
== Base
))
1429 /* We got lucky, return the cached entry */
1430 *LdrEntry
= LdrpLoadedDllHandleCache
;
1434 /* Time for a lookup */
1435 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
1436 Next
= ListHead
->Flink
;
1437 while (Next
!= ListHead
)
1439 /* Get the current entry */
1440 Current
= CONTAINING_RECORD(Next
,
1441 LDR_DATA_TABLE_ENTRY
,
1444 /* Make sure it's not unloading and check for a match */
1445 if ((Current
->InMemoryOrderModuleList
.Flink
) && (Base
== Current
->DllBase
))
1448 LdrpLoadedDllHandleCache
= Current
;
1451 *LdrEntry
= Current
;
1455 /* Move to the next one */
1465 LdrpResolveFullName(IN PUNICODE_STRING OriginalName
,
1466 IN PUNICODE_STRING PathName
,
1467 IN PUNICODE_STRING FullPathName
,
1468 IN PUNICODE_STRING
*ExpandedName
)
1470 NTSTATUS Status
= STATUS_SUCCESS
;
1471 // RTL_PATH_TYPE PathType;
1472 // BOOLEAN InvalidName;
1475 /* Display debug output if snaps are on */
1478 DbgPrintEx(81, //DPFLTR_LDR_ID,
1480 "LDR: %s - Expanding full name of %wZ\n",
1485 /* FIXME: Lock the PEB */
1486 //RtlEnterCriticalSection(&FastPebLock);
1488 /* Get the path name */
1489 Length
= RtlGetFullPathName_Ustr(OriginalName
,
1498 if (!(Length
) || (Length
> UNICODE_STRING_MAX_BYTES
))
1501 Status
= STATUS_NAME_TOO_LONG
;
1505 /* Check if the length hasn't changed */
1506 if (Length
<= PathName
->Length
)
1508 /* Return the same thing */
1509 *ExpandedName
= PathName
;
1510 PathName
->Length
= (USHORT
)Length
;
1515 ASSERT(Length
>= sizeof(WCHAR
));
1517 /* Allocate a string */
1518 Status
= LdrpAllocateUnicodeString(FullPathName
, Length
- sizeof(WCHAR
));
1519 if (!NT_SUCCESS(Status
)) goto Quickie
;
1521 /* Now get the full path again */
1523 Length
= RtlGetFullPathName_Ustr(OriginalName
,
1524 FullPathName
->Length
,
1525 FullPathName
->Buffer
,
1532 if (!(Length
) || (Length
> FullPathName
->Length
))
1535 LdrpFreeUnicodeString(FullPathName
);
1536 Status
= STATUS_NAME_TOO_LONG
;
1540 /* Return the expanded name */
1541 *ExpandedName
= FullPathName
;
1542 FullPathName
->Length
= (USHORT
)Length
;
1546 /* FIXME: Unlock the PEB */
1547 //RtlLeaveCriticalSection(&FastPebLock);
1549 /* Display debug output if snaps are on */
1552 /* Check which output to use -- failure or success */
1553 if (NT_SUCCESS(Status
))
1555 DbgPrintEx(81, //DPFLTR_LDR_ID,
1557 "LDR: %s - Expanded to %wZ\n",
1563 DbgPrintEx(81, //DPFLTR_LDR_ID,
1565 "LDR: %s - Failed to expand %wZ; 0x%08x\n",
1572 /* If we failed, return NULL */
1573 if (!NT_SUCCESS(Status
)) *ExpandedName
= NULL
;
1581 LdrpSearchPath(IN PWCHAR
*SearchPath
,
1583 IN PUNICODE_STRING PathName
,
1584 IN PUNICODE_STRING FullPathName
,
1585 IN PUNICODE_STRING
*ExpandedName
)
1587 BOOLEAN TryAgain
= FALSE
;
1588 PWCHAR ActualSearchPath
= *SearchPath
;
1589 UNICODE_STRING TestName
;
1591 PWCHAR Buffer
, BufEnd
= NULL
;
1596 /* Check if we don't have a search path */
1597 if (!ActualSearchPath
) *SearchPath
= LdrpDefaultPath
.Buffer
;
1599 /* Display debug output if snaps are on */
1602 DbgPrintEx(81, //DPFLTR_LDR_ID,
1604 "LDR: %s - Looking for %ws in %ws\n",
1610 /* Check if we're dealing with a relative path */
1611 if (RtlDetermineDosPathNameType_U(DllName
) != RtlPathTypeRelative
)
1613 /* Good, we're not. Create the name string */
1614 Status
= RtlInitUnicodeStringEx(&TestName
, DllName
);
1615 if (!NT_SUCCESS(Status
)) goto Quickie
;
1617 /* Make sure it exists */
1619 if (!RtlDoesFileExists_UstrEx(&TestName
, TRUE
))
1621 /* It doesn't, fail */
1622 Status
= STATUS_DLL_NOT_FOUND
;
1627 /* Resolve the full name */
1628 Status
= LdrpResolveFullName(&TestName
,
1635 /* FIXME: Handle relative case semicolon-lookup here */
1637 /* Calculate length */
1638 Length
+= (ULONG
)wcslen(DllName
) + sizeof(UNICODE_NULL
);
1639 if (Length
> UNICODE_STRING_MAX_CHARS
)
1641 /* Too long, fail */
1642 Status
= STATUS_NAME_TOO_LONG
;
1646 /* Allocate buffer */
1647 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1651 Status
= STATUS_NO_MEMORY
;
1655 /* FIXME: Setup TestName here */
1656 Status
= STATUS_NOT_FOUND
;
1662 p
= *ActualSearchPath
;
1663 if (!(p
) || (p
== ';'))
1665 /* FIXME: We don't have a character, or is a semicolon.*/
1667 /* Display debug output if snaps are on */
1670 DbgPrintEx(81, //DPFLTR_LDR_ID,
1672 "LDR: %s - Looking for %ws\n",
1678 TestName
.Length
= (USHORT
)ALIGN_DOWN((BufEnd
- Buffer
), WCHAR
);
1679 ASSERT(TestName
.Length
< TestName
.MaximumLength
);
1681 /* Check if the file exists */
1683 if (RtlDoesFileExists_UstrEx(&TestName
, FALSE
))
1686 /* It does. Reallocate the buffer */
1687 TestName
.MaximumLength
= (USHORT
)ALIGN_DOWN((BufEnd
- Buffer
), WCHAR
) + sizeof(WCHAR
);
1688 TestName
.Buffer
= RtlReAllocateHeap(RtlGetProcessHeap(),
1691 TestName
.MaximumLength
);
1692 if (!TestName
.Buffer
)
1694 /* Keep the old one */
1695 TestName
.Buffer
= Buffer
;
1700 Buffer
= TestName
.Buffer
;
1703 /* Make sure we have a buffer at least */
1704 ASSERT(TestName
.Buffer
);
1706 /* Resolve the name */
1707 *SearchPath
= ActualSearchPath
++;
1708 Status
= LdrpResolveFullName(&TestName
,
1715 /* Update buffer end */
1718 /* Update string position */
1719 pp
= ActualSearchPath
++;
1723 /* Otherwise, write the character */
1728 /* Check if the string is empty, meaning we're done */
1729 if (!(*ActualSearchPath
)) TryAgain
= TRUE
;
1731 /* Advance in the string */
1733 } while (!TryAgain
);
1735 /* Check if we had a buffer and free it */
1736 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1739 /* Check if we got here through failure */
1740 if (!NT_SUCCESS(Status
)) *ExpandedName
= NULL
;
1742 /* Display debug output if snaps are on */
1745 /* Check which output to use -- failure or success */
1746 if (NT_SUCCESS(Status
))
1748 DbgPrintEx(81, //DPFLTR_LDR_ID,
1750 "LDR: %s - Returning %wZ\n",
1756 DbgPrintEx(81, //DPFLTR_LDR_ID,
1758 "LDR: %s - Unable to locate %ws in %ws: 0x%08x\n",
1771 /* NOTE: This function is b0rked and in the process of being slowly unf*cked */
1774 LdrpCheckForLoadedDll(IN PWSTR DllPath
,
1775 IN PUNICODE_STRING DllName
,
1777 IN BOOLEAN RedirectedDll
,
1778 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
1781 PLIST_ENTRY ListHead
, ListEntry
;
1782 PLDR_DATA_TABLE_ENTRY CurEntry
;
1783 BOOLEAN FullPath
= FALSE
;
1786 UNICODE_STRING FullDllName
, NtPathName
;
1788 OBJECT_ATTRIBUTES ObjectAttributes
;
1790 HANDLE FileHandle
, SectionHandle
;
1791 IO_STATUS_BLOCK Iosb
;
1792 PVOID ViewBase
= NULL
;
1793 SIZE_T ViewSize
= 0;
1794 PIMAGE_NT_HEADERS NtHeader
, NtHeader2
;
1795 DPRINT("LdrpCheckForLoadedDll('%S' '%wZ' %d %d %p)\n", DllPath
, DllName
, Flag
, RedirectedDll
, LdrEntry
);
1797 /* Check if a dll name was provided */
1798 if (!(DllName
->Buffer
) || !(DllName
->Buffer
[0])) return FALSE
;
1800 /* FIXME: Warning, "Flag" is used as magic instead of "Static" */
1801 /* FIXME: Warning, code does not support redirection at all */
1803 /* Look in the hash table if flag was set */
1807 /* Get hash index */
1808 HashIndex
= LDR_GET_HASH_ENTRY(DllName
->Buffer
[0]);
1810 /* Traverse that list */
1811 ListHead
= &LdrpHashTable
[HashIndex
];
1812 ListEntry
= ListHead
->Flink
;
1813 while (ListEntry
!= ListHead
)
1815 /* Get the current entry */
1816 CurEntry
= CONTAINING_RECORD(ListEntry
, LDR_DATA_TABLE_ENTRY
, HashLinks
);
1818 /* Check base name of that module */
1819 if (RtlEqualUnicodeString(DllName
, &CurEntry
->BaseDllName
, TRUE
))
1821 /* It matches, return it */
1822 *LdrEntry
= CurEntry
;
1826 /* Advance to the next entry */
1827 ListEntry
= ListEntry
->Flink
;
1830 /* Module was not found, return failure */
1834 /* Check if there is a full path in this DLL */
1835 wc
= DllName
->Buffer
;
1838 /* Check for a slash in the current position*/
1839 if ((*wc
== L
'\\') || (*wc
== L
'/'))
1841 /* Found the slash, so dll name contains path */
1844 /* Setup full dll name string */
1845 FullDllName
.Buffer
= NameBuf
;
1847 /* FIXME: This is from the Windows 2000 loader, not XP/2003, we should call LdrpSearchPath */
1848 Length
= RtlDosSearchPath_U(DllPath
? DllPath
: LdrpDefaultPath
.Buffer
,
1851 sizeof(NameBuf
) - sizeof(UNICODE_NULL
),
1855 /* Check if that was successful */
1856 if (!(Length
) || (Length
> (sizeof(NameBuf
) - sizeof(UNICODE_NULL
))))
1860 DPRINT1("LDR: LdrpCheckForLoadedDll - Unable To Locate %ws: 0x%08x\n",
1861 DllName
->Buffer
, Length
);
1864 /* Return failure */
1868 /* Full dll name is found */
1869 FullDllName
.Length
= Length
;
1870 FullDllName
.MaximumLength
= FullDllName
.Length
+ sizeof(UNICODE_NULL
);
1877 /* Go check the hash table */
1884 /* FIXME: Warning, activation context missing */
1885 /* NOTE: From here on down, everything looks good */
1887 /* Loop the module list */
1888 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
1889 ListEntry
= ListHead
->Flink
;
1890 while (ListEntry
!= ListHead
)
1892 /* Get the current entry and advance to the next one */
1893 CurEntry
= CONTAINING_RECORD(ListEntry
,
1894 LDR_DATA_TABLE_ENTRY
,
1896 ListEntry
= ListEntry
->Flink
;
1898 /* Check if it's being unloaded */
1899 if (!CurEntry
->InMemoryOrderModuleList
.Flink
) continue;
1901 /* Check if name matches */
1902 if (RtlEqualUnicodeString(&FullDllName
,
1903 &CurEntry
->FullDllName
,
1907 *LdrEntry
= CurEntry
;
1912 /* Convert given path to NT path */
1913 if (!RtlDosPathNameToNtPathName_U(FullDllName
.Buffer
,
1918 /* Fail if conversion failed */
1922 /* Initialize object attributes and open it */
1923 InitializeObjectAttributes(&ObjectAttributes
,
1925 OBJ_CASE_INSENSITIVE
,
1928 Status
= NtOpenFile(&FileHandle
,
1929 SYNCHRONIZE
| FILE_EXECUTE
,
1932 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
1933 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1935 /* Free NT path name */
1936 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
1938 /* If opening the file failed - return failure */
1939 if (!NT_SUCCESS(Status
)) return FALSE
;
1941 /* Create a section for this file */
1942 Status
= NtCreateSection(&SectionHandle
,
1944 SECTION_MAP_EXECUTE
|
1952 /* Close file handle */
1953 NtClose(FileHandle
);
1955 /* If creating section failed - return failure */
1956 if (!NT_SUCCESS(Status
)) return FALSE
;
1958 /* Map view of this section */
1959 Status
= ZwMapViewOfSection(SectionHandle
,
1970 /* Close section handle */
1971 NtClose(SectionHandle
);
1973 /* If section mapping failed - return failure */
1974 if (!NT_SUCCESS(Status
)) return FALSE
;
1976 /* Get pointer to the NT header of this section */
1977 Status
= RtlImageNtHeaderEx(0, ViewBase
, ViewSize
, &NtHeader
);
1978 if (!(NT_SUCCESS(Status
)) || !(NtHeader
))
1980 /* Unmap the section and fail */
1981 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
1985 /* Go through the list of modules again */
1986 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
1987 ListEntry
= ListHead
->Flink
;
1988 while (ListEntry
!= ListHead
)
1990 /* Get the current entry and advance to the next one */
1991 CurEntry
= CONTAINING_RECORD(ListEntry
,
1992 LDR_DATA_TABLE_ENTRY
,
1994 ListEntry
= ListEntry
->Flink
;
1996 /* Check if it's in the process of being unloaded */
1997 if (!CurEntry
->InMemoryOrderModuleList
.Flink
) continue;
1999 /* The header is untrusted, use SEH */
2002 /* Check if timedate stamp and sizes match */
2003 if ((CurEntry
->TimeDateStamp
== NtHeader
->FileHeader
.TimeDateStamp
) &&
2004 (CurEntry
->SizeOfImage
== NtHeader
->OptionalHeader
.SizeOfImage
))
2006 /* Time, date and size match. Let's compare their headers */
2007 NtHeader2
= RtlImageNtHeader(CurEntry
->DllBase
);
2008 if (RtlCompareMemory(NtHeader2
, NtHeader
, sizeof(IMAGE_NT_HEADERS
)))
2010 /* Headers match too! Finally ask the kernel to compare mapped files */
2011 Status
= ZwAreMappedFilesTheSame(CurEntry
->DllBase
, ViewBase
);
2012 if (!NT_SUCCESS(Status
))
2014 /* Almost identical, but not quite, keep trying */
2015 _SEH2_YIELD(continue;)
2019 /* This is our entry!, unmap and return success */
2020 *LdrEntry
= CurEntry
;
2021 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
2022 _SEH2_YIELD(return TRUE
;)
2027 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
)
2034 /* Unmap the section and fail */
2035 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
2041 LdrpGetProcedureAddress(IN PVOID BaseAddress
,
2042 IN PANSI_STRING Name
,
2044 OUT PVOID
*ProcedureAddress
,
2045 IN BOOLEAN ExecuteInit
)
2047 NTSTATUS Status
= STATUS_SUCCESS
;
2048 UCHAR ImportBuffer
[64];
2049 PLDR_DATA_TABLE_ENTRY LdrEntry
;
2050 IMAGE_THUNK_DATA Thunk
;
2052 PIMAGE_IMPORT_BY_NAME ImportName
= NULL
;
2053 PIMAGE_EXPORT_DIRECTORY ExportDir
;
2054 ULONG ExportDirSize
, Length
;
2057 /* Show debug message */
2058 if (ShowSnaps
) DPRINT1("LDR: LdrGetProcedureAddress by ");
2060 /* Check if we got a name */
2063 /* Show debug message */
2064 if (ShowSnaps
) DbgPrint("NAME - %s\n", Name
->Buffer
);
2066 /* Make sure it's not too long */
2067 Length
= Name
->Length
+
2069 FIELD_OFFSET(IMAGE_IMPORT_BY_NAME
, Name
);
2070 if (Length
> UNICODE_STRING_MAX_BYTES
)
2072 /* Won't have enough space to add the hint */
2073 return STATUS_NAME_TOO_LONG
;
2076 /* Check if our buffer is large enough */
2077 if (Name
->Length
> sizeof(ImportBuffer
))
2079 /* Allocate from heap, plus 2 bytes for the Hint */
2080 ImportName
= RtlAllocateHeap(RtlGetProcessHeap(),
2086 /* Use our internal buffer */
2087 ImportName
= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
;
2090 /* Clear the hint */
2091 ImportName
->Hint
= 0;
2093 /* Copy the name and null-terminate it */
2094 RtlCopyMemory(ImportName
->Name
, Name
->Buffer
, Name
->Length
);
2095 ImportName
->Name
[Name
->Length
] = ANSI_NULL
;
2097 /* Clear the high bit */
2098 ImageBase
= ImportName
;
2099 Thunk
.u1
.AddressOfData
= 0;
2103 /* Do it by ordinal */
2106 /* Show debug message */
2107 if (ShowSnaps
) DbgPrint("ORDINAL - %lx\n", Ordinal
);
2109 /* Make sure an ordinal was given */
2113 DPRINT1("No ordinal and no name\n");
2114 return STATUS_INVALID_PARAMETER
;
2117 /* Set the orginal flag in the thunk */
2118 Thunk
.u1
.Ordinal
= Ordinal
| IMAGE_ORDINAL_FLAG
;
2121 /* Acquire lock unless we are initting */
2122 if (!LdrpInLdrInit
) RtlEnterCriticalSection(&LdrpLoaderLock
);
2126 /* Try to find the loaded DLL */
2127 if (!LdrpCheckForLoadedDllHandle(BaseAddress
, &LdrEntry
))
2130 DPRINT1("Invalid base address %p\n", BaseAddress
);
2131 Status
= STATUS_DLL_NOT_FOUND
;
2132 _SEH2_YIELD(goto Quickie
;)
2135 /* Get the pointer to the export directory */
2136 ExportDir
= RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
2138 IMAGE_DIRECTORY_ENTRY_EXPORT
,
2143 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
);
2144 Status
= STATUS_PROCEDURE_NOT_FOUND
;
2145 _SEH2_YIELD(goto Quickie
;)
2148 /* Now get the thunk */
2149 Status
= LdrpSnapThunk(LdrEntry
->DllBase
,
2158 /* Finally, see if we're supposed to run the init routines */
2159 if ((NT_SUCCESS(Status
)) && (ExecuteInit
))
2162 * It's possible a forwarded entry had us load the DLL. In that case,
2163 * then we will call its DllMain. Use the last loaded DLL for this.
2165 Entry
= NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
.Blink
;
2166 LdrEntry
= CONTAINING_RECORD(Entry
,
2167 LDR_DATA_TABLE_ENTRY
,
2168 InInitializationOrderModuleList
);
2170 /* Make sure we didn't process it yet*/
2171 if (!(LdrEntry
->Flags
& LDRP_ENTRY_PROCESSED
))
2173 /* Call the init routine */
2176 Status
= LdrpRunInitializeRoutines(NULL
);
2178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2180 /* Get the exception code */
2181 Status
= _SEH2_GetExceptionCode();
2187 /* Make sure we're OK till here */
2188 if (NT_SUCCESS(Status
))
2190 /* Return the address */
2191 *ProcedureAddress
= (PVOID
)Thunk
.u1
.Function
;
2194 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2196 /* Just ignore exceptions */
2202 if (ImportName
&& (ImportName
!= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
))
2204 /* We allocated from heap, free it */
2205 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName
);
2208 /* Release the CS if we entered it */
2209 if (!LdrpInLdrInit
) RtlLeaveCriticalSection(&LdrpLoaderLock
);
2217 LdrpLoadDll(IN BOOLEAN Redirected
,
2218 IN PWSTR DllPath OPTIONAL
,
2219 IN PULONG DllCharacteristics OPTIONAL
,
2220 IN PUNICODE_STRING DllName
,
2221 OUT PVOID
*BaseAddress
,
2222 IN BOOLEAN CallInit
)
2224 PPEB Peb
= NtCurrentPeb();
2225 NTSTATUS Status
= STATUS_SUCCESS
;
2228 WCHAR NameBuffer
[266];
2230 UNICODE_STRING RawDllNameString
;
2231 PLDR_DATA_TABLE_ENTRY LdrEntry
;
2232 BOOLEAN InInit
= LdrpInLdrInit
;
2234 /* Find the name without the extension */
2235 p1
= DllName
->Buffer
;
2244 else if (c
== L
'\\')
2250 /* Save the Raw DLL Name */
2251 RawDllName
= NameBuffer
;
2252 if (DllName
->Length
>= sizeof(NameBuffer
)) return STATUS_NAME_TOO_LONG
;
2253 RtlMoveMemory(RawDllName
, DllName
->Buffer
, DllName
->Length
);
2255 /* Check if no extension was found or if we got a slash */
2256 if (!(p2
) || (*p2
== '\\'))
2258 /* Check that we have space to add one */
2259 if ((DllName
->Length
+ LdrApiDefaultExtension
.Length
+ sizeof(UNICODE_NULL
)) >=
2262 /* No space to add the extension */
2263 DbgPrintEx(81, //DPFLTR_LDR_ID,
2265 "LDR: %s - Dll name missing extension; with extension "
2266 "added the name is too long\n"
2267 " DllName: (@ %p) \"%wZ\"\n"
2268 " DllName->Length: %u\n",
2273 return STATUS_NAME_TOO_LONG
;
2276 /* FIXME: CLEAN THIS UP WITH Rtl String Functions */
2278 RtlMoveMemory((PVOID
)((ULONG_PTR
)RawDllName
+ DllName
->Length
),
2279 LdrApiDefaultExtension
.Buffer
,
2280 LdrApiDefaultExtension
.Length
);
2282 /* Save the length to a unicode string */
2283 RawDllNameString
.Length
= DllName
->Length
+ LdrApiDefaultExtension
.Length
;
2285 /* Null terminate it */
2286 RawDllName
[RawDllNameString
.Length
/ sizeof(WCHAR
)] = 0;
2290 /* Null terminate it */
2291 RawDllName
[DllName
->Length
/ sizeof(WCHAR
)] = 0;
2293 /* Save the length to a unicode string */
2294 RawDllNameString
.Length
= DllName
->Length
;
2297 /* Now create a unicode string for the DLL's name */
2298 RawDllNameString
.MaximumLength
= sizeof(NameBuffer
);
2299 RawDllNameString
.Buffer
= NameBuffer
;
2301 /* Check for init flag and acquire lock */
2302 if (!InInit
) RtlEnterCriticalSection(&LdrpLoaderLock
);
2304 /* Show debug message */
2307 DPRINT1("LDR: LdrLoadDll, loading %ws from %ws\n",
2309 DllPath
? DllPath
: L
"");
2312 /* Check if the DLL is already loaded */
2313 if (!LdrpCheckForLoadedDll(DllPath
,
2320 Status
= LdrpMapDll(DllPath
,
2327 if (!NT_SUCCESS(Status
)) goto Quickie
;
2329 /* FIXME: Need to mark the DLL range for the stack DB */
2330 //RtlpStkMarkDllRange(LdrEntry);
2332 /* Check if IMAGE_FILE_EXECUTABLE_IMAGE was provided */
2333 if ((DllCharacteristics
) &&
2334 (*DllCharacteristics
& IMAGE_FILE_EXECUTABLE_IMAGE
))
2336 /* This is not a DLL, so remove such data */
2337 LdrEntry
->EntryPoint
= NULL
;
2338 LdrEntry
->Flags
&= ~LDRP_IMAGE_DLL
;
2341 /* Make sure it's a DLL */
2342 if (LdrEntry
->Flags
& LDRP_IMAGE_DLL
)
2344 /* Check if this is a .NET Image */
2345 if (!(LdrEntry
->Flags
& LDRP_COR_IMAGE
))
2347 /* Walk the Import Descriptor */
2348 Status
= LdrpWalkImportDescriptor(DllPath
, LdrEntry
);
2351 /* Update load count, unless it's locked */
2352 if (LdrEntry
->LoadCount
!= 0xFFFF) LdrEntry
->LoadCount
++;
2353 LdrpUpdateLoadCount2(LdrEntry
, LDRP_UPDATE_REFCOUNT
);
2355 /* Check if we failed */
2356 if (!NT_SUCCESS(Status
))
2358 /* Clear entrypoint, and insert into list */
2359 LdrEntry
->EntryPoint
= NULL
;
2360 InsertTailList(&Peb
->Ldr
->InInitializationOrderModuleList
,
2361 &LdrEntry
->InInitializationOrderModuleList
);
2363 /* Cancel the load */
2364 LdrpClearLoadInProgress();
2366 /* Unload the DLL */
2369 DbgPrint("LDR: Unloading %wZ due to error %x walking "
2370 "import descriptors",
2374 LdrUnloadDll(LdrEntry
->DllBase
);
2376 /* Return the error */
2380 else if (LdrEntry
->LoadCount
!= 0xFFFF)
2382 /* Increase load count */
2383 LdrEntry
->LoadCount
++;
2386 /* Insert it into the list */
2387 InsertTailList(&Peb
->Ldr
->InInitializationOrderModuleList
,
2388 &LdrEntry
->InInitializationOrderModuleList
);
2390 /* If we have to run the entrypoint, make sure the DB is ready */
2391 if (CallInit
&& LdrpLdrDatabaseIsSetup
)
2393 /* FIXME: Notify Shim Engine */
2397 //ShimLoadCallback = RtlDecodeSystemPointer(g_pfnSE_DllLoaded);
2398 //ShimLoadCallback(LdrEntry);
2401 /* Run the init routine */
2402 Status
= LdrpRunInitializeRoutines(NULL
);
2403 if (!NT_SUCCESS(Status
))
2405 /* Failed, unload the DLL */
2408 DbgPrint("LDR: Unloading %wZ because either its init "
2409 "routine or one of its static imports failed; "
2410 "status = 0x%08lx\n",
2414 LdrUnloadDll(LdrEntry
->DllBase
);
2419 /* The DB isn't ready, which means we were loaded because of a forwarder */
2420 Status
= STATUS_SUCCESS
;
2425 /* We were already loaded. Are we a DLL? */
2426 if ((LdrEntry
->Flags
& LDRP_IMAGE_DLL
) && (LdrEntry
->LoadCount
!= 0xFFFF))
2428 /* Increase load count */
2429 LdrEntry
->LoadCount
++;
2430 LdrpUpdateLoadCount2(LdrEntry
, LDRP_UPDATE_REFCOUNT
);
2432 /* Clear the load in progress */
2433 LdrpClearLoadInProgress();
2437 /* Not a DLL, just increase the load count */
2438 if (LdrEntry
->LoadCount
!= 0xFFFF) LdrEntry
->LoadCount
++;
2443 /* Release the lock */
2444 if (!InInit
) RtlLeaveCriticalSection(Peb
->LoaderLock
);
2446 /* Check for success */
2447 if (NT_SUCCESS(Status
))
2449 /* Return the base address */
2450 *BaseAddress
= LdrEntry
->DllBase
;
2455 *BaseAddress
= NULL
;
2464 LdrpClearLoadInProgress(VOID
)
2466 PLIST_ENTRY ListHead
, Entry
;
2467 PLDR_DATA_TABLE_ENTRY LdrEntry
;
2468 ULONG ModulesCount
= 0;
2470 /* Traverse the init list */
2471 ListHead
= &NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
;
2472 Entry
= ListHead
->Flink
;
2473 while (Entry
!= ListHead
)
2475 /* Get the loader entry */
2476 LdrEntry
= CONTAINING_RECORD(Entry
,
2477 LDR_DATA_TABLE_ENTRY
,
2478 InInitializationOrderModuleList
);
2480 /* Clear load in progress flag */
2481 LdrEntry
->Flags
&= ~LDRP_LOAD_IN_PROGRESS
;
2483 /* Check for modules with entry point count but not processed yet */
2484 if ((LdrEntry
->EntryPoint
) &&
2485 !(LdrEntry
->Flags
& LDRP_ENTRY_PROCESSED
))
2487 /* Increase counter */
2491 /* Advance to the next entry */
2492 Entry
= Entry
->Flink
;
2495 /* Return final count */
2496 return ModulesCount
;