[NTDLL]
[reactos.git] / reactos / dll / ntdll / ldr / ldrutils.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User-Mode Library
4 * FILE: dll/ntdll/ldr/ldrutils.c
5 * PURPOSE: Internal Loader Utility Functions
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Aleksey Bragin (aleksey@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 PLDR_DATA_TABLE_ENTRY LdrpLoadedDllHandleCache;
19 LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];
20
21 #define LDR_GET_HASH_ENTRY(x) (RtlUpcaseUnicodeChar((x)) & (LDR_HASH_TABLE_ENTRIES - 1))
22
23 /* FUNCTIONS *****************************************************************/
24
25 BOOLEAN
26 NTAPI
27 LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint,
28 PVOID BaseAddress,
29 ULONG Reason,
30 PVOID Context)
31 {
32 /* Call the entry */
33 return EntryPoint(BaseAddress, Reason, Context);
34 }
35
36 VOID
37 NTAPI
38 LdrpTlsCallback(PVOID BaseAddress, ULONG Reason)
39 {
40 PIMAGE_TLS_DIRECTORY TlsDirectory;
41 PIMAGE_TLS_CALLBACK *Array, Callback;
42 ULONG Size;
43
44 /* Get the TLS Directory */
45 TlsDirectory = RtlImageDirectoryEntryToData(BaseAddress,
46 TRUE,
47 IMAGE_DIRECTORY_ENTRY_TLS,
48 &Size);
49
50 /* Protect against invalid pointers */
51 _SEH2_TRY
52 {
53 /* Make sure it's valid and we have an array */
54 if (TlsDirectory && (Array = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks))
55 {
56 /* Display debug */
57 if (ShowSnaps)
58 {
59 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
60 BaseAddress, TlsDirectory, Array);
61 }
62
63 /* Loop the array */
64 while (*Array)
65 {
66 /* Get the TLS Entrypoint */
67 Callback = *Array++;
68
69 /* Display debug */
70 if (ShowSnaps)
71 {
72 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
73 BaseAddress, Callback);
74 }
75
76 /* Call it */
77 LdrpCallDllEntry((PDLLMAIN_FUNC)Callback, BaseAddress, Reason, NULL);
78 }
79 }
80 }
81 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
82 {
83 /* Do nothing */
84 }
85 _SEH2_END;
86 }
87
88 PVOID
89 NTAPI
90 LdrpFetchAddressOfEntryPoint(PVOID ImageBase)
91 {
92 PIMAGE_NT_HEADERS NtHeaders;
93 ULONG_PTR EntryPoint;
94
95 /* Get entry point offset from NT headers */
96 NtHeaders = RtlImageNtHeader(ImageBase);
97 EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
98
99 /* If it's 0 - return so */
100 if (!EntryPoint) return NULL;
101
102 /* Add image base */
103 EntryPoint += (ULONG_PTR)ImageBase;
104
105 /* Return calculated pointer */
106 return (PVOID)EntryPoint;
107 }
108
109 NTSTATUS
110 NTAPI
111 LdrpMapDll(IN PWSTR SearchPath OPTIONAL,
112 IN PWSTR DllPath2,
113 IN PWSTR DllName OPTIONAL,
114 IN PULONG DllCharacteristics,
115 IN BOOLEAN Static,
116 IN BOOLEAN Redirect,
117 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
118 {
119 UNIMPLEMENTED;
120 return STATUS_SUCCESS;
121 }
122
123 PLDR_DATA_TABLE_ENTRY
124 NTAPI
125 LdrpAllocateDataTableEntry(IN PVOID BaseAddress)
126 {
127 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
128 PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader(BaseAddress);
129
130 /* Make sure the header is valid */
131 if (NtHeader)
132 {
133 /* Allocate an entry */
134 LdrEntry = RtlAllocateHeap(RtlGetProcessHeap(),
135 HEAP_ZERO_MEMORY,
136 sizeof(LDR_DATA_TABLE_ENTRY));
137
138 /* Make sure we got one */
139 if (LdrEntry)
140 {
141 /* Set it up */
142 LdrEntry->DllBase = BaseAddress;
143 LdrEntry->SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
144 LdrEntry->TimeDateStamp = NtHeader->FileHeader.TimeDateStamp;
145 }
146 }
147
148 /* Return the entry */
149 return LdrEntry;
150 }
151
152 VOID
153 NTAPI
154 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
155 {
156 PPEB_LDR_DATA PebData = NtCurrentPeb()->Ldr;
157 ULONG i;
158
159 /* Get the Hash entry */
160 i = LDR_GET_HASH_ENTRY(LdrEntry->BaseDllName.Buffer[0]);
161
162 InsertTailList(&LdrpHashTable[i], &LdrEntry->HashLinks);
163 InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
164 InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderModuleList);
165 }
166
167 BOOLEAN
168 NTAPI
169 LdrpCheckForLoadedDllHandle(IN PVOID Base,
170 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
171 {
172 PLDR_DATA_TABLE_ENTRY Current;
173 PLIST_ENTRY ListHead, Next;
174
175 /* Check the cache first */
176 if (LdrpLoadedDllHandleCache && LdrpLoadedDllHandleCache->DllBase == Base)
177 {
178 /* We got lucky, return the cached entry */
179 *LdrEntry = LdrpLoadedDllHandleCache;
180 return TRUE;
181 }
182
183 /* Time for a lookup */
184 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
185 Next = ListHead->Flink;
186 while(Next != ListHead)
187 {
188 /* Get the current entry */
189 Current = CONTAINING_RECORD(Next,
190 LDR_DATA_TABLE_ENTRY,
191 InLoadOrderLinks);
192
193 /* Make sure it's not unloading and check for a match */
194 if ((Current->InMemoryOrderModuleList.Flink) && (Base == Current->DllBase))
195 {
196 /* Save in cache */
197 LdrpLoadedDllHandleCache = Current;
198
199 /* Return it */
200 *LdrEntry = Current;
201 return TRUE;
202 }
203
204 /* Move to the next one */
205 Next = Next->Flink;
206 }
207
208 /* Nothing found */
209 return FALSE;
210 }
211
212 BOOLEAN
213 NTAPI
214 LdrpCheckForLoadedDll(IN PWSTR DllPath,
215 IN PUNICODE_STRING DllName,
216 IN BOOLEAN Flag,
217 IN BOOLEAN RedirectedDll,
218 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
219 {
220 UNIMPLEMENTED;
221 return FALSE;
222 }
223
224 NTSTATUS
225 NTAPI
226 LdrpGetProcedureAddress(IN PVOID BaseAddress,
227 IN PANSI_STRING Name,
228 IN ULONG Ordinal,
229 OUT PVOID *ProcedureAddress,
230 IN BOOLEAN ExecuteInit)
231 {
232 NTSTATUS Status = STATUS_SUCCESS;
233 UCHAR ImportBuffer[64];
234 PLDR_DATA_TABLE_ENTRY LdrEntry;
235 IMAGE_THUNK_DATA Thunk;
236 PVOID ImageBase;
237 PIMAGE_IMPORT_BY_NAME ImportName = NULL;
238 PIMAGE_EXPORT_DIRECTORY ExportDir;
239 ULONG ExportDirSize;
240 PLIST_ENTRY Entry;
241
242 /* Show debug message */
243 if (ShowSnaps) DPRINT1("LDR: LdrGetProcedureAddress by ");
244
245 /* Check if we got a name */
246 if (Name)
247 {
248 /* Show debug message */
249 if (ShowSnaps) DPRINT1("NAME - %s\n", Name->Buffer);
250
251 /* Make sure it's not too long */
252 if ((Name->Length + sizeof(CHAR) + sizeof(USHORT)) > MAXLONG)
253 {
254 /* Won't have enough space to add the hint */
255 return STATUS_NAME_TOO_LONG;
256 }
257
258 /* Check if our buffer is large enough */
259 if (Name->Length >= (sizeof(ImportBuffer) - sizeof(CHAR)))
260 {
261 /* Allocate from heap, plus 2 bytes for the Hint */
262 ImportName = RtlAllocateHeap(RtlGetProcessHeap(),
263 0,
264 Name->Length + sizeof(CHAR) +
265 sizeof(USHORT));
266 }
267 else
268 {
269 /* Use our internal buffer */
270 ImportName = (PIMAGE_IMPORT_BY_NAME)ImportBuffer;
271 }
272
273 /* Clear the hint */
274 ImportName->Hint = 0;
275
276 /* Copy the name and null-terminate it */
277 RtlMoveMemory(&ImportName->Name, Name->Buffer, Name->Length);
278 ImportName->Name[Name->Length + 1] = 0;
279
280 /* Clear the high bit */
281 ImageBase = ImportName;
282 Thunk.u1.AddressOfData = 0;
283 }
284 else
285 {
286 /* Do it by ordinal */
287 ImageBase = NULL;
288
289 /* Show debug message */
290 if (ShowSnaps) DPRINT1("ORDINAL - %lx\n", Ordinal);
291
292 if (Ordinal)
293 {
294 Thunk.u1.Ordinal = Ordinal | IMAGE_ORDINAL_FLAG;
295 }
296 else
297 {
298 /* No ordinal */
299 DPRINT1("No ordinal and no name\n");
300 return STATUS_INVALID_PARAMETER;
301 }
302 }
303
304 /* Acquire lock unless we are initting */
305 if (!LdrpInLdrInit) RtlEnterCriticalSection(&LdrpLoaderLock);
306
307 _SEH2_TRY
308 {
309 /* Try to find the loaded DLL */
310 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
311 {
312 /* Invalid base */
313 DPRINT1("Invalid base address\n");
314 Status = STATUS_DLL_NOT_FOUND;
315 _SEH2_YIELD(goto Quickie;)
316 }
317
318 /* Get the pointer to the export directory */
319 ExportDir = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
320 TRUE,
321 IMAGE_DIRECTORY_ENTRY_EXPORT,
322 &ExportDirSize);
323
324 if (!ExportDir)
325 {
326 DPRINT1("Image has no exports\n");
327 Status = STATUS_PROCEDURE_NOT_FOUND;
328 _SEH2_YIELD(goto Quickie;)
329 }
330
331 /* Now get the thunk */
332 Status = LdrpSnapThunk(LdrEntry->DllBase,
333 ImageBase,
334 &Thunk,
335 &Thunk,
336 ExportDir,
337 ExportDirSize,
338 FALSE,
339 NULL);
340
341 /* Finally, see if we're supposed to run the init routines */
342 if (ExecuteInit)
343 {
344 /*
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.
347 */
348 Entry = NtCurrentPeb()->Ldr->InInitializationOrderModuleList.Blink;
349 LdrEntry = CONTAINING_RECORD(Entry,
350 LDR_DATA_TABLE_ENTRY,
351 InInitializationOrderModuleList);
352
353 /* Make sure we didn't process it yet*/
354 if (!(LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
355 {
356 /* Call the init routine */
357 _SEH2_TRY
358 {
359 Status = LdrpRunInitializeRoutines(NULL);
360 }
361 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
362 {
363 /* Get the exception code */
364 Status = _SEH2_GetExceptionCode();
365 }
366 _SEH2_END;
367 }
368 }
369
370 /* Make sure we're OK till here */
371 if (NT_SUCCESS(Status))
372 {
373 /* Return the address */
374 *ProcedureAddress = (PVOID)Thunk.u1.Function;
375 }
376 }
377 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
378 {
379 /* Just ignore exceptions */
380 }
381 _SEH2_END;
382
383 Quickie:
384 /* Cleanup */
385 if (ImportName && (ImportName != (PIMAGE_IMPORT_BY_NAME)ImportBuffer))
386 {
387 /* We allocated from heap, free it */
388 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName);
389 }
390
391 /* Release the CS if we entered it */
392 if (!LdrpInLdrInit) RtlLeaveCriticalSection(&LdrpLoaderLock);
393
394 /* We're done */
395 return Status;
396 }
397
398 NTSTATUS
399 NTAPI
400 LdrpLoadDll(IN BOOLEAN Redirected,
401 IN PWSTR DllPath OPTIONAL,
402 IN PULONG DllCharacteristics OPTIONAL,
403 IN PUNICODE_STRING DllName,
404 OUT PVOID *BaseAddress,
405 IN BOOLEAN CallInit)
406 {
407 UNIMPLEMENTED;
408 return STATUS_NOT_IMPLEMENTED;
409 }
410
411 ULONG
412 NTAPI
413 LdrpClearLoadInProgress()
414 {
415 PLIST_ENTRY ListHead;
416 PLIST_ENTRY Entry;
417 PLDR_DATA_TABLE_ENTRY Module;
418 ULONG ModulesCount = 0;
419
420 /* Traverse the init list */
421 ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
422 Entry = ListHead->Flink;
423
424 while (Entry != ListHead)
425 {
426 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
427
428 /* Clear load in progress flag */
429 Module->Flags &= ~LDRP_LOAD_IN_PROGRESS;
430
431 /* Increase counter for modules with entry point count but not processed yet */
432 if (Module->EntryPoint &&
433 !(Module->Flags & LDRP_ENTRY_PROCESSED)) ModulesCount++;
434
435 /* Advance to the next entry */
436 Entry = Entry->Flink;
437 }
438
439 return ModulesCount;
440 }
441
442 /* EOF */