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
;
20 #define LDR_GET_HASH_ENTRY(x) (RtlUpcaseUnicodeChar((x)) & (LDR_HASH_TABLE_ENTRIES - 1))
22 /* FUNCTIONS *****************************************************************/
26 LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint
,
32 return EntryPoint(BaseAddress
, Reason
, Context
);
37 LdrpTlsCallback(PVOID BaseAddress
, ULONG Reason
)
39 PIMAGE_TLS_DIRECTORY TlsDirectory
;
40 PIMAGE_TLS_CALLBACK
*Array
, Callback
;
43 /* Get the TLS Directory */
44 TlsDirectory
= RtlImageDirectoryEntryToData(BaseAddress
,
46 IMAGE_DIRECTORY_ENTRY_TLS
,
49 /* Protect against invalid pointers */
52 /* Make sure it's valid and we have an array */
53 if (TlsDirectory
&& (Array
= (PIMAGE_TLS_CALLBACK
*)TlsDirectory
->AddressOfCallBacks
))
58 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
59 BaseAddress
, TlsDirectory
, Array
);
65 /* Get the TLS Entrypoint */
71 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
72 BaseAddress
, Callback
);
76 LdrpCallDllEntry((PDLLMAIN_FUNC
)Callback
, BaseAddress
, Reason
, NULL
);
80 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
89 LdrpFetchAddressOfEntryPoint(PVOID ImageBase
)
91 PIMAGE_NT_HEADERS NtHeaders
;
94 /* Get entry point offset from NT headers */
95 NtHeaders
= RtlImageNtHeader(ImageBase
);
96 EntryPoint
= NtHeaders
->OptionalHeader
.AddressOfEntryPoint
;
98 /* If it's 0 - return so */
99 if (!EntryPoint
) return NULL
;
102 EntryPoint
+= (ULONG_PTR
)ImageBase
;
104 /* Return calculated pointer */
105 return (PVOID
)EntryPoint
;
110 LdrpMapDll(IN PWSTR SearchPath OPTIONAL
,
112 IN PWSTR DllName OPTIONAL
,
113 IN PULONG DllCharacteristics
,
116 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
)
119 return STATUS_SUCCESS
;
122 PLDR_DATA_TABLE_ENTRY
124 LdrpAllocateDataTableEntry(IN PVOID BaseAddress
)
126 PLDR_DATA_TABLE_ENTRY LdrEntry
= NULL
;
127 PIMAGE_NT_HEADERS NtHeader
= RtlImageNtHeader(BaseAddress
);
129 /* Make sure the header is valid */
132 /* Allocate an entry */
133 LdrEntry
= RtlAllocateHeap(RtlGetProcessHeap(),
135 sizeof(LDR_DATA_TABLE_ENTRY
));
137 /* Make sure we got one */
141 LdrEntry
->DllBase
= BaseAddress
;
142 LdrEntry
->SizeOfImage
= NtHeader
->OptionalHeader
.SizeOfImage
;
143 LdrEntry
->TimeDateStamp
= NtHeader
->FileHeader
.TimeDateStamp
;
147 /* Return the entry */
153 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
155 PPEB_LDR_DATA PebData
= NtCurrentPeb()->Ldr
;
158 /* Get the Hash entry */
159 i
= LDR_GET_HASH_ENTRY(LdrEntry
->BaseDllName
.Buffer
[0]);
161 InsertTailList(&LdrpHashTable
[i
], &LdrEntry
->HashLinks
);
162 InsertTailList(&PebData
->InLoadOrderModuleList
, &LdrEntry
->InLoadOrderLinks
);
163 InsertTailList(&PebData
->InMemoryOrderModuleList
, &LdrEntry
->InMemoryOrderModuleList
);
168 LdrpCheckForLoadedDllHandle(IN PVOID Base
,
169 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
171 PLDR_DATA_TABLE_ENTRY Current
;
172 PLIST_ENTRY ListHead
, Next
;
174 /* Check the cache first */
175 if (LdrpLoadedDllHandleCache
&& LdrpLoadedDllHandleCache
->DllBase
== Base
)
177 /* We got lucky, return the cached entry */
178 *LdrEntry
= LdrpLoadedDllHandleCache
;
182 /* Time for a lookup */
183 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
184 Next
= ListHead
->Flink
;
185 while(Next
!= ListHead
)
187 /* Get the current entry */
188 Current
= CONTAINING_RECORD(Next
,
189 LDR_DATA_TABLE_ENTRY
,
192 /* Make sure it's not unloading and check for a match */
193 if ((Current
->InMemoryOrderModuleList
.Flink
) && (Base
== Current
->DllBase
))
196 LdrpLoadedDllHandleCache
= Current
;
203 /* Move to the next one */
213 LdrpCheckForLoadedDll(IN PWSTR DllPath
,
214 IN PUNICODE_STRING DllName
,
216 IN BOOLEAN RedirectedDll
,
217 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
220 PLIST_ENTRY ListHead
, ListEntry
;
221 PLDR_DATA_TABLE_ENTRY CurEntry
;
222 BOOLEAN FullPath
= FALSE
;
225 UNICODE_STRING FullDllName
, NtPathName
;
227 OBJECT_ATTRIBUTES ObjectAttributes
;
229 HANDLE FileHandle
, SectionHandle
;
230 IO_STATUS_BLOCK Iosb
;
231 PVOID ViewBase
= NULL
;
233 PIMAGE_NT_HEADERS NtHeader
, NtHeader2
;
234 DPRINT1("LdrpCheckForLoadedDll('%S' '%wZ' %d %d %p)\n", DllPath
, DllName
, Flag
, RedirectedDll
, LdrEntry
);
235 /* Check if a dll name was provided */
236 if (!DllName
->Buffer
|| !DllName
->Buffer
[0]) return FALSE
;
238 /* Look in the hash table if flag was set */
243 HashIndex
= LDR_GET_HASH_ENTRY(DllName
->Buffer
[0]);
245 /* Traverse that list */
246 ListHead
= &LdrpHashTable
[HashIndex
];
247 ListEntry
= ListHead
->Flink
;
248 while (ListEntry
!= ListHead
)
250 /* Get the current entry */
251 CurEntry
= CONTAINING_RECORD(ListEntry
, LDR_DATA_TABLE_ENTRY
, HashLinks
);
253 /* Check base name of that module */
254 if (RtlEqualUnicodeString(DllName
, &CurEntry
->BaseDllName
, TRUE
))
256 /* It matches, return it */
257 *LdrEntry
= CurEntry
;
261 /* Advance to the next entry */
262 ListEntry
= ListEntry
->Flink
;
265 /* Module was not found, return failure */
269 /* Check if there is a full path in this DLL */
270 wc
= DllName
->Buffer
;
273 /* Check for a slash in the current position*/
274 if (*wc
== L
'\\' || *wc
== L
'/')
276 /* Found the slash, so dll name contains path */
279 /* Setup full dll name string */
280 FullDllName
.Buffer
= NameBuf
;
282 Length
= RtlDosSearchPath_U(DllPath
? DllPath
: LdrpDefaultPath
.Buffer
,
285 sizeof(NameBuf
) - sizeof(UNICODE_NULL
),
289 /* Check if that was successful */
290 if (!Length
|| Length
> sizeof(NameBuf
) - sizeof(UNICODE_NULL
))
294 DPRINT1("LDR: LdrpCheckForLoadedDll - Unable To Locate %ws: 0x%08x\n",
295 DllName
->Buffer
, Length
);
302 /* Full dll name is found */
303 FullDllName
.Length
= Length
;
304 FullDllName
.MaximumLength
= FullDllName
.Length
+ sizeof(UNICODE_NULL
);
311 /* Go check the hash table */
318 /* Now go through the InLoadOrder module list */
319 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
320 ListEntry
= ListHead
->Flink
;
322 while (ListEntry
!= ListHead
)
324 /* Get the containing record of the current entry and advance to the next one */
325 CurEntry
= CONTAINING_RECORD(ListEntry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
326 ListEntry
= ListEntry
->Flink
;
328 /* Check if it's already being unloaded */
329 if (!CurEntry
->InMemoryOrderModuleList
.Flink
) continue;
331 /* Check if name matches */
332 if (RtlEqualUnicodeString(&FullDllName
,
333 &CurEntry
->FullDllName
,
337 *LdrEntry
= CurEntry
;
339 /* Find activation context */
340 Status
= RtlFindActivationContextSectionString(0, NULL
, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION
, DllName
, NULL
);
341 if (!NT_SUCCESS(Status
))
348 /* The DLL was not found in the load order modules list. Perform a complex check */
350 /* Convert given path to NT path */
351 if (!RtlDosPathNameToNtPathName_U(FullDllName
.Buffer
,
356 /* Fail if conversion failed */
360 /* Initialize object attributes and open it */
361 InitializeObjectAttributes(&ObjectAttributes
,
363 OBJ_CASE_INSENSITIVE
,
367 Status
= NtOpenFile(&FileHandle
,
368 SYNCHRONIZE
| FILE_EXECUTE
,
371 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
372 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
374 /* Free NT path name */
375 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
377 /* If opening the file failed - return failure */
378 if (!NT_SUCCESS(Status
)) return FALSE
;
380 /* Create a section for this file */
381 Status
= NtCreateSection(&SectionHandle
,
382 SECTION_MAP_READ
| SECTION_MAP_EXECUTE
| SECTION_MAP_WRITE
,
389 /* Close file handle */
392 /* If creating section failed - return failure */
393 if (!NT_SUCCESS(Status
)) return FALSE
;
395 /* Map view of this section */
396 Status
= ZwMapViewOfSection(SectionHandle
,
406 /* Close section handle */
407 NtClose(SectionHandle
);
409 /* If section mapping failed - return failure */
410 if (!NT_SUCCESS(Status
)) return FALSE
;
412 /* Get pointer to the NT header of this section */
413 NtHeader
= RtlImageNtHeader(ViewBase
);
416 /* Unmap the section and fail */
417 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
421 /* Go through the list of modules */
422 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
423 ListEntry
= ListHead
->Flink
;
425 while (ListEntry
!= ListHead
)
427 CurEntry
= CONTAINING_RECORD(ListEntry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
428 ListEntry
= ListEntry
->Flink
;
430 /* Check if it's already being unloaded */
431 if (!CurEntry
->InMemoryOrderModuleList
.Flink
) continue;
435 /* Check if timedate stamp and sizes match */
436 if (CurEntry
->TimeDateStamp
== NtHeader
->FileHeader
.TimeDateStamp
&&
437 CurEntry
->SizeOfImage
== NtHeader
->OptionalHeader
.SizeOfImage
)
439 /* Time, date and size match. Let's compare their headers */
440 NtHeader2
= RtlImageNtHeader(CurEntry
->DllBase
);
442 if (RtlCompareMemory(NtHeader2
, NtHeader
, sizeof(IMAGE_NT_HEADERS
)))
444 /* Headers match too! Finally ask the kernel to compare mapped files */
445 Status
= ZwAreMappedFilesTheSame(CurEntry
->DllBase
, ViewBase
);
447 if (!NT_SUCCESS(Status
))
449 _SEH2_YIELD(continue;)
453 /* This is our entry! */
454 *LdrEntry
= CurEntry
;
456 /* Unmap the section */
457 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
459 _SEH2_YIELD(return TRUE
;)
464 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
)
471 /* Unmap the section */
472 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
479 LdrpGetProcedureAddress(IN PVOID BaseAddress
,
480 IN PANSI_STRING Name
,
482 OUT PVOID
*ProcedureAddress
,
483 IN BOOLEAN ExecuteInit
)
485 NTSTATUS Status
= STATUS_SUCCESS
;
486 UCHAR ImportBuffer
[64];
487 PLDR_DATA_TABLE_ENTRY LdrEntry
;
488 IMAGE_THUNK_DATA Thunk
;
490 PIMAGE_IMPORT_BY_NAME ImportName
= NULL
;
491 PIMAGE_EXPORT_DIRECTORY ExportDir
;
495 /* Show debug message */
496 if (ShowSnaps
) DPRINT1("LDR: LdrGetProcedureAddress by ");
498 /* Check if we got a name */
501 /* Show debug message */
502 if (ShowSnaps
) DPRINT1("NAME - %s\n", Name
->Buffer
);
504 /* Make sure it's not too long */
505 if ((Name
->Length
+ sizeof(CHAR
) + sizeof(USHORT
)) > MAXLONG
)
507 /* Won't have enough space to add the hint */
508 return STATUS_NAME_TOO_LONG
;
511 /* Check if our buffer is large enough */
512 if (Name
->Length
>= (sizeof(ImportBuffer
) - sizeof(CHAR
)))
514 /* Allocate from heap, plus 2 bytes for the Hint */
515 ImportName
= RtlAllocateHeap(RtlGetProcessHeap(),
517 Name
->Length
+ sizeof(CHAR
) +
522 /* Use our internal buffer */
523 ImportName
= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
;
527 ImportName
->Hint
= 0;
529 /* Copy the name and null-terminate it */
530 RtlMoveMemory(&ImportName
->Name
, Name
->Buffer
, Name
->Length
);
531 ImportName
->Name
[Name
->Length
+ 1] = 0;
533 /* Clear the high bit */
534 ImageBase
= ImportName
;
535 Thunk
.u1
.AddressOfData
= 0;
539 /* Do it by ordinal */
542 /* Show debug message */
543 if (ShowSnaps
) DPRINT1("ORDINAL - %lx\n", Ordinal
);
547 Thunk
.u1
.Ordinal
= Ordinal
| IMAGE_ORDINAL_FLAG
;
552 DPRINT1("No ordinal and no name\n");
553 return STATUS_INVALID_PARAMETER
;
557 /* Acquire lock unless we are initting */
558 if (!LdrpInLdrInit
) RtlEnterCriticalSection(&LdrpLoaderLock
);
562 /* Try to find the loaded DLL */
563 if (!LdrpCheckForLoadedDllHandle(BaseAddress
, &LdrEntry
))
566 DPRINT1("Invalid base address\n");
567 Status
= STATUS_DLL_NOT_FOUND
;
568 _SEH2_YIELD(goto Quickie
;)
571 /* Get the pointer to the export directory */
572 ExportDir
= RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
574 IMAGE_DIRECTORY_ENTRY_EXPORT
,
579 DPRINT1("Image has no exports\n");
580 Status
= STATUS_PROCEDURE_NOT_FOUND
;
581 _SEH2_YIELD(goto Quickie
;)
584 /* Now get the thunk */
585 Status
= LdrpSnapThunk(LdrEntry
->DllBase
,
594 /* Finally, see if we're supposed to run the init routines */
598 * It's possible a forwarded entry had us load the DLL. In that case,
599 * then we will call its DllMain. Use the last loaded DLL for this.
601 Entry
= NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
.Blink
;
602 LdrEntry
= CONTAINING_RECORD(Entry
,
603 LDR_DATA_TABLE_ENTRY
,
604 InInitializationOrderModuleList
);
606 /* Make sure we didn't process it yet*/
607 if (!(LdrEntry
->Flags
& LDRP_ENTRY_PROCESSED
))
609 /* Call the init routine */
612 Status
= LdrpRunInitializeRoutines(NULL
);
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
616 /* Get the exception code */
617 Status
= _SEH2_GetExceptionCode();
623 /* Make sure we're OK till here */
624 if (NT_SUCCESS(Status
))
626 /* Return the address */
627 *ProcedureAddress
= (PVOID
)Thunk
.u1
.Function
;
630 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
632 /* Just ignore exceptions */
638 if (ImportName
&& (ImportName
!= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
))
640 /* We allocated from heap, free it */
641 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName
);
644 /* Release the CS if we entered it */
645 if (!LdrpInLdrInit
) RtlLeaveCriticalSection(&LdrpLoaderLock
);
653 LdrpLoadDll(IN BOOLEAN Redirected
,
654 IN PWSTR DllPath OPTIONAL
,
655 IN PULONG DllCharacteristics OPTIONAL
,
656 IN PUNICODE_STRING DllName
,
657 OUT PVOID
*BaseAddress
,
661 return STATUS_NOT_IMPLEMENTED
;
666 LdrpClearLoadInProgress()
668 PLIST_ENTRY ListHead
;
670 PLDR_DATA_TABLE_ENTRY Module
;
671 ULONG ModulesCount
= 0;
673 /* Traverse the init list */
674 ListHead
= &NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
;
675 Entry
= ListHead
->Flink
;
677 while (Entry
!= ListHead
)
679 Module
= CONTAINING_RECORD(Entry
, LDR_DATA_TABLE_ENTRY
, InInitializationOrderModuleList
);
681 /* Clear load in progress flag */
682 Module
->Flags
&= ~LDRP_LOAD_IN_PROGRESS
;
684 /* Increase counter for modules with entry point count but not processed yet */
685 if (Module
->EntryPoint
&&
686 !(Module
->Flags
& LDRP_ENTRY_PROCESSED
)) ModulesCount
++;
688 /* Advance to the next entry */
689 Entry
= Entry
->Flink
;