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
;
19 LIST_ENTRY LdrpHashTable
[LDR_HASH_TABLE_ENTRIES
];
21 #define LDR_GET_HASH_ENTRY(x) (RtlUpcaseUnicodeChar((x)) & (LDR_HASH_TABLE_ENTRIES - 1))
23 /* FUNCTIONS *****************************************************************/
27 LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint
,
33 return EntryPoint(BaseAddress
, Reason
, Context
);
38 LdrpTlsCallback(PVOID BaseAddress
, ULONG Reason
)
40 PIMAGE_TLS_DIRECTORY TlsDirectory
;
41 PIMAGE_TLS_CALLBACK
*Array
, Callback
;
44 /* Get the TLS Directory */
45 TlsDirectory
= RtlImageDirectoryEntryToData(BaseAddress
,
47 IMAGE_DIRECTORY_ENTRY_TLS
,
50 /* Protect against invalid pointers */
53 /* Make sure it's valid and we have an array */
54 if (TlsDirectory
&& (Array
= (PIMAGE_TLS_CALLBACK
*)TlsDirectory
->AddressOfCallBacks
))
59 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
60 BaseAddress
, TlsDirectory
, Array
);
66 /* Get the TLS Entrypoint */
72 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
73 BaseAddress
, Callback
);
77 LdrpCallDllEntry((PDLLMAIN_FUNC
)Callback
, BaseAddress
, Reason
, NULL
);
81 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
90 LdrpFetchAddressOfEntryPoint(PVOID ImageBase
)
92 PIMAGE_NT_HEADERS NtHeaders
;
95 /* Get entry point offset from NT headers */
96 NtHeaders
= RtlImageNtHeader(ImageBase
);
97 EntryPoint
= NtHeaders
->OptionalHeader
.AddressOfEntryPoint
;
99 /* If it's 0 - return so */
100 if (!EntryPoint
) return NULL
;
103 EntryPoint
+= (ULONG_PTR
)ImageBase
;
105 /* Return calculated pointer */
106 return (PVOID
)EntryPoint
;
111 LdrpMapDll(IN PWSTR SearchPath OPTIONAL
,
113 IN PWSTR DllName OPTIONAL
,
114 IN PULONG DllCharacteristics
,
117 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
)
120 return STATUS_SUCCESS
;
123 PLDR_DATA_TABLE_ENTRY
125 LdrpAllocateDataTableEntry(IN PVOID BaseAddress
)
127 PLDR_DATA_TABLE_ENTRY LdrEntry
= NULL
;
128 PIMAGE_NT_HEADERS NtHeader
= RtlImageNtHeader(BaseAddress
);
130 /* Make sure the header is valid */
133 /* Allocate an entry */
134 LdrEntry
= RtlAllocateHeap(RtlGetProcessHeap(),
136 sizeof(LDR_DATA_TABLE_ENTRY
));
138 /* Make sure we got one */
142 LdrEntry
->DllBase
= BaseAddress
;
143 LdrEntry
->SizeOfImage
= NtHeader
->OptionalHeader
.SizeOfImage
;
144 LdrEntry
->TimeDateStamp
= NtHeader
->FileHeader
.TimeDateStamp
;
148 /* Return the entry */
154 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
156 PPEB_LDR_DATA PebData
= NtCurrentPeb()->Ldr
;
159 /* Get the Hash entry */
160 i
= LDR_GET_HASH_ENTRY(LdrEntry
->BaseDllName
.Buffer
[0]);
162 InsertTailList(&LdrpHashTable
[i
], &LdrEntry
->HashLinks
);
163 InsertTailList(&PebData
->InLoadOrderModuleList
, &LdrEntry
->InLoadOrderLinks
);
164 InsertTailList(&PebData
->InMemoryOrderModuleList
, &LdrEntry
->InMemoryOrderModuleList
);
169 LdrpCheckForLoadedDllHandle(IN PVOID Base
,
170 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
172 PLDR_DATA_TABLE_ENTRY Current
;
173 PLIST_ENTRY ListHead
, Next
;
175 /* Check the cache first */
176 if (LdrpLoadedDllHandleCache
&& LdrpLoadedDllHandleCache
->DllBase
== Base
)
178 /* We got lucky, return the cached entry */
179 *LdrEntry
= LdrpLoadedDllHandleCache
;
183 /* Time for a lookup */
184 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
185 Next
= ListHead
->Flink
;
186 while(Next
!= ListHead
)
188 /* Get the current entry */
189 Current
= CONTAINING_RECORD(Next
,
190 LDR_DATA_TABLE_ENTRY
,
193 /* Make sure it's not unloading and check for a match */
194 if ((Current
->InMemoryOrderModuleList
.Flink
) && (Base
== Current
->DllBase
))
197 LdrpLoadedDllHandleCache
= Current
;
204 /* Move to the next one */
214 LdrpCheckForLoadedDll(IN PWSTR DllPath
,
215 IN PUNICODE_STRING DllName
,
217 IN BOOLEAN RedirectedDll
,
218 OUT PLDR_DATA_TABLE_ENTRY
*LdrEntry
)
226 LdrpGetProcedureAddress(IN PVOID BaseAddress
,
227 IN PANSI_STRING Name
,
229 OUT PVOID
*ProcedureAddress
,
230 IN BOOLEAN ExecuteInit
)
232 NTSTATUS Status
= STATUS_SUCCESS
;
233 UCHAR ImportBuffer
[64];
234 PLDR_DATA_TABLE_ENTRY LdrEntry
;
235 IMAGE_THUNK_DATA Thunk
;
237 PIMAGE_IMPORT_BY_NAME ImportName
= NULL
;
238 PIMAGE_EXPORT_DIRECTORY ExportDir
;
242 /* Show debug message */
243 if (ShowSnaps
) DPRINT1("LDR: LdrGetProcedureAddress by ");
245 /* Check if we got a name */
248 /* Show debug message */
249 if (ShowSnaps
) DPRINT1("NAME - %s\n", Name
->Buffer
);
251 /* Make sure it's not too long */
252 if ((Name
->Length
+ sizeof(CHAR
) + sizeof(USHORT
)) > MAXLONG
)
254 /* Won't have enough space to add the hint */
255 return STATUS_NAME_TOO_LONG
;
258 /* Check if our buffer is large enough */
259 if (Name
->Length
>= (sizeof(ImportBuffer
) - sizeof(CHAR
)))
261 /* Allocate from heap, plus 2 bytes for the Hint */
262 ImportName
= RtlAllocateHeap(RtlGetProcessHeap(),
264 Name
->Length
+ sizeof(CHAR
) +
269 /* Use our internal buffer */
270 ImportName
= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
;
274 ImportName
->Hint
= 0;
276 /* Copy the name and null-terminate it */
277 RtlMoveMemory(&ImportName
->Name
, Name
->Buffer
, Name
->Length
);
278 ImportName
->Name
[Name
->Length
+ 1] = 0;
280 /* Clear the high bit */
281 ImageBase
= ImportName
;
282 Thunk
.u1
.AddressOfData
= 0;
286 /* Do it by ordinal */
289 /* Show debug message */
290 if (ShowSnaps
) DPRINT1("ORDINAL - %lx\n", Ordinal
);
294 Thunk
.u1
.Ordinal
= Ordinal
| IMAGE_ORDINAL_FLAG
;
299 DPRINT1("No ordinal and no name\n");
300 return STATUS_INVALID_PARAMETER
;
304 /* Acquire lock unless we are initting */
305 if (!LdrpInLdrInit
) RtlEnterCriticalSection(&LdrpLoaderLock
);
309 /* Try to find the loaded DLL */
310 if (!LdrpCheckForLoadedDllHandle(BaseAddress
, &LdrEntry
))
313 DPRINT1("Invalid base address\n");
314 Status
= STATUS_DLL_NOT_FOUND
;
315 _SEH2_YIELD(goto Quickie
;)
318 /* Get the pointer to the export directory */
319 ExportDir
= RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
321 IMAGE_DIRECTORY_ENTRY_EXPORT
,
326 DPRINT1("Image has no exports\n");
327 Status
= STATUS_PROCEDURE_NOT_FOUND
;
328 _SEH2_YIELD(goto Quickie
;)
331 /* Now get the thunk */
332 Status
= LdrpSnapThunk(LdrEntry
->DllBase
,
341 /* Finally, see if we're supposed to run the init routines */
345 * It's possible a forwarded entry had us load the DLL. In that case,
346 * then we will call its DllMain. Use the last loaded DLL for this.
348 Entry
= NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
.Blink
;
349 LdrEntry
= CONTAINING_RECORD(Entry
,
350 LDR_DATA_TABLE_ENTRY
,
351 InInitializationOrderModuleList
);
353 /* Make sure we didn't process it yet*/
354 if (!(LdrEntry
->Flags
& LDRP_ENTRY_PROCESSED
))
356 /* Call the init routine */
359 Status
= LdrpRunInitializeRoutines(NULL
);
361 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
363 /* Get the exception code */
364 Status
= _SEH2_GetExceptionCode();
370 /* Make sure we're OK till here */
371 if (NT_SUCCESS(Status
))
373 /* Return the address */
374 *ProcedureAddress
= (PVOID
)Thunk
.u1
.Function
;
377 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
379 /* Just ignore exceptions */
385 if (ImportName
&& (ImportName
!= (PIMAGE_IMPORT_BY_NAME
)ImportBuffer
))
387 /* We allocated from heap, free it */
388 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName
);
391 /* Release the CS if we entered it */
392 if (!LdrpInLdrInit
) RtlLeaveCriticalSection(&LdrpLoaderLock
);
400 LdrpLoadDll(IN BOOLEAN Redirected
,
401 IN PWSTR DllPath OPTIONAL
,
402 IN PULONG DllCharacteristics OPTIONAL
,
403 IN PUNICODE_STRING DllName
,
404 OUT PVOID
*BaseAddress
,
408 return STATUS_NOT_IMPLEMENTED
;
413 LdrpClearLoadInProgress()
415 PLIST_ENTRY ListHead
;
417 PLDR_DATA_TABLE_ENTRY Module
;
418 ULONG ModulesCount
= 0;
420 /* Traverse the init list */
421 ListHead
= &NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
;
422 Entry
= ListHead
->Flink
;
424 while (Entry
!= ListHead
)
426 Module
= CONTAINING_RECORD(Entry
, LDR_DATA_TABLE_ENTRY
, InInitializationOrderModuleList
);
428 /* Clear load in progress flag */
429 Module
->Flags
&= ~LDRP_LOAD_IN_PROGRESS
;
431 /* Increase counter for modules with entry point count but not processed yet */
432 if (Module
->EntryPoint
&&
433 !(Module
->Flags
& LDRP_ENTRY_PROCESSED
)) ModulesCount
++;
435 /* Advance to the next entry */
436 Entry
= Entry
->Flink
;