* Reverse merge ldr rewrite related commits.
[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 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 PLDR_DATA_TABLE_ENTRY LdrpLoadedDllHandleCache;
19
20 #define LDR_GET_HASH_ENTRY(x) (RtlUpcaseUnicodeChar((x)) & (LDR_HASH_TABLE_ENTRIES - 1))
21
22 /* FUNCTIONS *****************************************************************/
23
24 BOOLEAN
25 NTAPI
26 LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint,
27 PVOID BaseAddress,
28 ULONG Reason,
29 PVOID Context)
30 {
31 /* Call the entry */
32 return EntryPoint(BaseAddress, Reason, Context);
33 }
34
35 VOID
36 NTAPI
37 LdrpTlsCallback(PVOID BaseAddress, ULONG Reason)
38 {
39 PIMAGE_TLS_DIRECTORY TlsDirectory;
40 PIMAGE_TLS_CALLBACK *Array, Callback;
41 ULONG Size;
42
43 /* Get the TLS Directory */
44 TlsDirectory = RtlImageDirectoryEntryToData(BaseAddress,
45 TRUE,
46 IMAGE_DIRECTORY_ENTRY_TLS,
47 &Size);
48
49 /* Protect against invalid pointers */
50 _SEH2_TRY
51 {
52 /* Make sure it's valid and we have an array */
53 if (TlsDirectory && (Array = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks))
54 {
55 /* Display debug */
56 if (ShowSnaps)
57 {
58 DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
59 BaseAddress, TlsDirectory, Array);
60 }
61
62 /* Loop the array */
63 while (*Array)
64 {
65 /* Get the TLS Entrypoint */
66 Callback = *Array++;
67
68 /* Display debug */
69 if (ShowSnaps)
70 {
71 DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
72 BaseAddress, Callback);
73 }
74
75 /* Call it */
76 LdrpCallDllEntry((PDLLMAIN_FUNC)Callback, BaseAddress, Reason, NULL);
77 }
78 }
79 }
80 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
81 {
82 /* Do nothing */
83 }
84 _SEH2_END;
85 }
86
87 PVOID
88 NTAPI
89 LdrpFetchAddressOfEntryPoint(PVOID ImageBase)
90 {
91 PIMAGE_NT_HEADERS NtHeaders;
92 ULONG_PTR EntryPoint;
93
94 /* Get entry point offset from NT headers */
95 NtHeaders = RtlImageNtHeader(ImageBase);
96 EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
97
98 /* If it's 0 - return so */
99 if (!EntryPoint) return NULL;
100
101 /* Add image base */
102 EntryPoint += (ULONG_PTR)ImageBase;
103
104 /* Return calculated pointer */
105 return (PVOID)EntryPoint;
106 }
107
108 NTSTATUS
109 NTAPI
110 LdrpMapDll(IN PWSTR SearchPath OPTIONAL,
111 IN PWSTR DllPath2,
112 IN PWSTR DllName OPTIONAL,
113 IN PULONG DllCharacteristics,
114 IN BOOLEAN Static,
115 IN BOOLEAN Redirect,
116 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
117 {
118 UNIMPLEMENTED;
119 return STATUS_SUCCESS;
120 }
121
122 PLDR_DATA_TABLE_ENTRY
123 NTAPI
124 LdrpAllocateDataTableEntry(IN PVOID BaseAddress)
125 {
126 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
127 PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader(BaseAddress);
128
129 /* Make sure the header is valid */
130 if (NtHeader)
131 {
132 /* Allocate an entry */
133 LdrEntry = RtlAllocateHeap(RtlGetProcessHeap(),
134 HEAP_ZERO_MEMORY,
135 sizeof(LDR_DATA_TABLE_ENTRY));
136
137 /* Make sure we got one */
138 if (LdrEntry)
139 {
140 /* Set it up */
141 LdrEntry->DllBase = BaseAddress;
142 LdrEntry->SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
143 LdrEntry->TimeDateStamp = NtHeader->FileHeader.TimeDateStamp;
144 }
145 }
146
147 /* Return the entry */
148 return LdrEntry;
149 }
150
151 VOID
152 NTAPI
153 LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
154 {
155 PPEB_LDR_DATA PebData = NtCurrentPeb()->Ldr;
156 ULONG i;
157
158 /* Get the Hash entry */
159 i = LDR_GET_HASH_ENTRY(LdrEntry->BaseDllName.Buffer[0]);
160
161 InsertTailList(&LdrpHashTable[i], &LdrEntry->HashLinks);
162 InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
163 InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderModuleList);
164 }
165
166 BOOLEAN
167 NTAPI
168 LdrpCheckForLoadedDllHandle(IN PVOID Base,
169 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
170 {
171 PLDR_DATA_TABLE_ENTRY Current;
172 PLIST_ENTRY ListHead, Next;
173
174 /* Check the cache first */
175 if (LdrpLoadedDllHandleCache && LdrpLoadedDllHandleCache->DllBase == Base)
176 {
177 /* We got lucky, return the cached entry */
178 *LdrEntry = LdrpLoadedDllHandleCache;
179 return TRUE;
180 }
181
182 /* Time for a lookup */
183 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
184 Next = ListHead->Flink;
185 while(Next != ListHead)
186 {
187 /* Get the current entry */
188 Current = CONTAINING_RECORD(Next,
189 LDR_DATA_TABLE_ENTRY,
190 InLoadOrderLinks);
191
192 /* Make sure it's not unloading and check for a match */
193 if ((Current->InMemoryOrderModuleList.Flink) && (Base == Current->DllBase))
194 {
195 /* Save in cache */
196 LdrpLoadedDllHandleCache = Current;
197
198 /* Return it */
199 *LdrEntry = Current;
200 return TRUE;
201 }
202
203 /* Move to the next one */
204 Next = Next->Flink;
205 }
206
207 /* Nothing found */
208 return FALSE;
209 }
210
211 BOOLEAN
212 NTAPI
213 LdrpCheckForLoadedDll(IN PWSTR DllPath,
214 IN PUNICODE_STRING DllName,
215 IN BOOLEAN Flag,
216 IN BOOLEAN RedirectedDll,
217 OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
218 {
219 ULONG HashIndex;
220 PLIST_ENTRY ListHead, ListEntry;
221 PLDR_DATA_TABLE_ENTRY CurEntry;
222 BOOLEAN FullPath = FALSE;
223 PWCHAR wc;
224 WCHAR NameBuf[266];
225 UNICODE_STRING FullDllName, NtPathName;
226 ULONG Length;
227 OBJECT_ATTRIBUTES ObjectAttributes;
228 NTSTATUS Status;
229 HANDLE FileHandle, SectionHandle;
230 IO_STATUS_BLOCK Iosb;
231 PVOID ViewBase = NULL;
232 SIZE_T ViewSize = 0;
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;
237
238 /* Look in the hash table if flag was set */
239 lookinhash:
240 if (Flag)
241 {
242 /* Get hash index */
243 HashIndex = LDR_GET_HASH_ENTRY(DllName->Buffer[0]);
244
245 /* Traverse that list */
246 ListHead = &LdrpHashTable[HashIndex];
247 ListEntry = ListHead->Flink;
248 while (ListEntry != ListHead)
249 {
250 /* Get the current entry */
251 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
252
253 /* Check base name of that module */
254 if (RtlEqualUnicodeString(DllName, &CurEntry->BaseDllName, TRUE))
255 {
256 /* It matches, return it */
257 *LdrEntry = CurEntry;
258 return TRUE;
259 }
260
261 /* Advance to the next entry */
262 ListEntry = ListEntry->Flink;
263 }
264
265 /* Module was not found, return failure */
266 return FALSE;
267 }
268
269 /* Check if there is a full path in this DLL */
270 wc = DllName->Buffer;
271 while (*wc)
272 {
273 /* Check for a slash in the current position*/
274 if (*wc == L'\\' || *wc == L'/')
275 {
276 /* Found the slash, so dll name contains path */
277 FullPath = TRUE;
278
279 /* Setup full dll name string */
280 FullDllName.Buffer = NameBuf;
281
282 Length = RtlDosSearchPath_U(DllPath ? DllPath : LdrpDefaultPath.Buffer,
283 DllName->Buffer,
284 NULL,
285 sizeof(NameBuf) - sizeof(UNICODE_NULL),
286 FullDllName.Buffer,
287 NULL);
288
289 /* Check if that was successful */
290 if (!Length || Length > sizeof(NameBuf) - sizeof(UNICODE_NULL))
291 {
292 if (ShowSnaps)
293 {
294 DPRINT1("LDR: LdrpCheckForLoadedDll - Unable To Locate %ws: 0x%08x\n",
295 DllName->Buffer, Length);
296 }
297
298 /* Return failure */
299 return FALSE;
300 }
301
302 /* Full dll name is found */
303 FullDllName.Length = Length;
304 FullDllName.MaximumLength = FullDllName.Length + sizeof(UNICODE_NULL);
305 break;
306 }
307
308 wc++;
309 }
310
311 /* Go check the hash table */
312 if (!FullPath)
313 {
314 Flag = TRUE;
315 goto lookinhash;
316 }
317
318 /* Now go through the InLoadOrder module list */
319 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
320 ListEntry = ListHead->Flink;
321
322 while (ListEntry != ListHead)
323 {
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;
327
328 /* Check if it's already being unloaded */
329 if (!CurEntry->InMemoryOrderModuleList.Flink) continue;
330
331 /* Check if name matches */
332 if (RtlEqualUnicodeString(&FullDllName,
333 &CurEntry->FullDllName,
334 TRUE))
335 {
336 /* Found it */
337 *LdrEntry = CurEntry;
338
339 /* Find activation context */
340 Status = RtlFindActivationContextSectionString(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, DllName, NULL);
341 if (!NT_SUCCESS(Status))
342 return FALSE;
343 else
344 return TRUE;
345 }
346 }
347
348 /* The DLL was not found in the load order modules list. Perform a complex check */
349
350 /* Convert given path to NT path */
351 if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
352 &NtPathName,
353 NULL,
354 NULL))
355 {
356 /* Fail if conversion failed */
357 return FALSE;
358 }
359
360 /* Initialize object attributes and open it */
361 InitializeObjectAttributes(&ObjectAttributes,
362 &NtPathName,
363 OBJ_CASE_INSENSITIVE,
364 NULL,
365 NULL);
366
367 Status = NtOpenFile(&FileHandle,
368 SYNCHRONIZE | FILE_EXECUTE,
369 &ObjectAttributes,
370 &Iosb,
371 FILE_SHARE_READ | FILE_SHARE_DELETE,
372 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
373
374 /* Free NT path name */
375 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
376
377 /* If opening the file failed - return failure */
378 if (!NT_SUCCESS(Status)) return FALSE;
379
380 /* Create a section for this file */
381 Status = NtCreateSection(&SectionHandle,
382 SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
383 NULL,
384 NULL,
385 PAGE_EXECUTE,
386 SEC_COMMIT,
387 FileHandle);
388
389 /* Close file handle */
390 NtClose(FileHandle);
391
392 /* If creating section failed - return failure */
393 if (!NT_SUCCESS(Status)) return FALSE;
394
395 /* Map view of this section */
396 Status = ZwMapViewOfSection(SectionHandle,
397 NtCurrentProcess(),
398 &ViewBase,
399 0,
400 0,
401 NULL,
402 &ViewSize,
403 ViewShare,
404 0,
405 PAGE_EXECUTE);
406 /* Close section handle */
407 NtClose(SectionHandle);
408
409 /* If section mapping failed - return failure */
410 if (!NT_SUCCESS(Status)) return FALSE;
411
412 /* Get pointer to the NT header of this section */
413 NtHeader = RtlImageNtHeader(ViewBase);
414 if (!NtHeader)
415 {
416 /* Unmap the section and fail */
417 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
418 return FALSE;
419 }
420
421 /* Go through the list of modules */
422 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
423 ListEntry = ListHead->Flink;
424
425 while (ListEntry != ListHead)
426 {
427 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
428 ListEntry = ListEntry->Flink;
429
430 /* Check if it's already being unloaded */
431 if (!CurEntry->InMemoryOrderModuleList.Flink) continue;
432
433 _SEH2_TRY
434 {
435 /* Check if timedate stamp and sizes match */
436 if (CurEntry->TimeDateStamp == NtHeader->FileHeader.TimeDateStamp &&
437 CurEntry->SizeOfImage == NtHeader->OptionalHeader.SizeOfImage)
438 {
439 /* Time, date and size match. Let's compare their headers */
440 NtHeader2 = RtlImageNtHeader(CurEntry->DllBase);
441
442 if (RtlCompareMemory(NtHeader2, NtHeader, sizeof(IMAGE_NT_HEADERS)))
443 {
444 /* Headers match too! Finally ask the kernel to compare mapped files */
445 Status = ZwAreMappedFilesTheSame(CurEntry->DllBase, ViewBase);
446
447 if (!NT_SUCCESS(Status))
448 {
449 _SEH2_YIELD(continue;)
450 }
451 else
452 {
453 /* This is our entry! */
454 *LdrEntry = CurEntry;
455
456 /* Unmap the section */
457 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
458
459 _SEH2_YIELD(return TRUE;)
460 }
461 }
462 }
463 }
464 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
465 {
466 _SEH2_YIELD(break;)
467 }
468 _SEH2_END;
469 }
470
471 /* Unmap the section */
472 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
473
474 return FALSE;
475 }
476
477 NTSTATUS
478 NTAPI
479 LdrpGetProcedureAddress(IN PVOID BaseAddress,
480 IN PANSI_STRING Name,
481 IN ULONG Ordinal,
482 OUT PVOID *ProcedureAddress,
483 IN BOOLEAN ExecuteInit)
484 {
485 NTSTATUS Status = STATUS_SUCCESS;
486 UCHAR ImportBuffer[64];
487 PLDR_DATA_TABLE_ENTRY LdrEntry;
488 IMAGE_THUNK_DATA Thunk;
489 PVOID ImageBase;
490 PIMAGE_IMPORT_BY_NAME ImportName = NULL;
491 PIMAGE_EXPORT_DIRECTORY ExportDir;
492 ULONG ExportDirSize;
493 PLIST_ENTRY Entry;
494
495 /* Show debug message */
496 if (ShowSnaps) DPRINT1("LDR: LdrGetProcedureAddress by ");
497
498 /* Check if we got a name */
499 if (Name)
500 {
501 /* Show debug message */
502 if (ShowSnaps) DPRINT1("NAME - %s\n", Name->Buffer);
503
504 /* Make sure it's not too long */
505 if ((Name->Length + sizeof(CHAR) + sizeof(USHORT)) > MAXLONG)
506 {
507 /* Won't have enough space to add the hint */
508 return STATUS_NAME_TOO_LONG;
509 }
510
511 /* Check if our buffer is large enough */
512 if (Name->Length >= (sizeof(ImportBuffer) - sizeof(CHAR)))
513 {
514 /* Allocate from heap, plus 2 bytes for the Hint */
515 ImportName = RtlAllocateHeap(RtlGetProcessHeap(),
516 0,
517 Name->Length + sizeof(CHAR) +
518 sizeof(USHORT));
519 }
520 else
521 {
522 /* Use our internal buffer */
523 ImportName = (PIMAGE_IMPORT_BY_NAME)ImportBuffer;
524 }
525
526 /* Clear the hint */
527 ImportName->Hint = 0;
528
529 /* Copy the name and null-terminate it */
530 RtlMoveMemory(&ImportName->Name, Name->Buffer, Name->Length);
531 ImportName->Name[Name->Length + 1] = 0;
532
533 /* Clear the high bit */
534 ImageBase = ImportName;
535 Thunk.u1.AddressOfData = 0;
536 }
537 else
538 {
539 /* Do it by ordinal */
540 ImageBase = NULL;
541
542 /* Show debug message */
543 if (ShowSnaps) DPRINT1("ORDINAL - %lx\n", Ordinal);
544
545 if (Ordinal)
546 {
547 Thunk.u1.Ordinal = Ordinal | IMAGE_ORDINAL_FLAG;
548 }
549 else
550 {
551 /* No ordinal */
552 DPRINT1("No ordinal and no name\n");
553 return STATUS_INVALID_PARAMETER;
554 }
555 }
556
557 /* Acquire lock unless we are initting */
558 if (!LdrpInLdrInit) RtlEnterCriticalSection(&LdrpLoaderLock);
559
560 _SEH2_TRY
561 {
562 /* Try to find the loaded DLL */
563 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
564 {
565 /* Invalid base */
566 DPRINT1("Invalid base address\n");
567 Status = STATUS_DLL_NOT_FOUND;
568 _SEH2_YIELD(goto Quickie;)
569 }
570
571 /* Get the pointer to the export directory */
572 ExportDir = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
573 TRUE,
574 IMAGE_DIRECTORY_ENTRY_EXPORT,
575 &ExportDirSize);
576
577 if (!ExportDir)
578 {
579 DPRINT1("Image has no exports\n");
580 Status = STATUS_PROCEDURE_NOT_FOUND;
581 _SEH2_YIELD(goto Quickie;)
582 }
583
584 /* Now get the thunk */
585 Status = LdrpSnapThunk(LdrEntry->DllBase,
586 ImageBase,
587 &Thunk,
588 &Thunk,
589 ExportDir,
590 ExportDirSize,
591 FALSE,
592 NULL);
593
594 /* Finally, see if we're supposed to run the init routines */
595 if (ExecuteInit)
596 {
597 /*
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.
600 */
601 Entry = NtCurrentPeb()->Ldr->InInitializationOrderModuleList.Blink;
602 LdrEntry = CONTAINING_RECORD(Entry,
603 LDR_DATA_TABLE_ENTRY,
604 InInitializationOrderModuleList);
605
606 /* Make sure we didn't process it yet*/
607 if (!(LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
608 {
609 /* Call the init routine */
610 _SEH2_TRY
611 {
612 Status = LdrpRunInitializeRoutines(NULL);
613 }
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
615 {
616 /* Get the exception code */
617 Status = _SEH2_GetExceptionCode();
618 }
619 _SEH2_END;
620 }
621 }
622
623 /* Make sure we're OK till here */
624 if (NT_SUCCESS(Status))
625 {
626 /* Return the address */
627 *ProcedureAddress = (PVOID)Thunk.u1.Function;
628 }
629 }
630 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
631 {
632 /* Just ignore exceptions */
633 }
634 _SEH2_END;
635
636 Quickie:
637 /* Cleanup */
638 if (ImportName && (ImportName != (PIMAGE_IMPORT_BY_NAME)ImportBuffer))
639 {
640 /* We allocated from heap, free it */
641 RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName);
642 }
643
644 /* Release the CS if we entered it */
645 if (!LdrpInLdrInit) RtlLeaveCriticalSection(&LdrpLoaderLock);
646
647 /* We're done */
648 return Status;
649 }
650
651 NTSTATUS
652 NTAPI
653 LdrpLoadDll(IN BOOLEAN Redirected,
654 IN PWSTR DllPath OPTIONAL,
655 IN PULONG DllCharacteristics OPTIONAL,
656 IN PUNICODE_STRING DllName,
657 OUT PVOID *BaseAddress,
658 IN BOOLEAN CallInit)
659 {
660 UNIMPLEMENTED;
661 return STATUS_NOT_IMPLEMENTED;
662 }
663
664 ULONG
665 NTAPI
666 LdrpClearLoadInProgress()
667 {
668 PLIST_ENTRY ListHead;
669 PLIST_ENTRY Entry;
670 PLDR_DATA_TABLE_ENTRY Module;
671 ULONG ModulesCount = 0;
672
673 /* Traverse the init list */
674 ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
675 Entry = ListHead->Flink;
676
677 while (Entry != ListHead)
678 {
679 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
680
681 /* Clear load in progress flag */
682 Module->Flags &= ~LDRP_LOAD_IN_PROGRESS;
683
684 /* Increase counter for modules with entry point count but not processed yet */
685 if (Module->EntryPoint &&
686 !(Module->Flags & LDRP_ENTRY_PROCESSED)) ModulesCount++;
687
688 /* Advance to the next entry */
689 Entry = Entry->Flink;
690 }
691
692 return ModulesCount;
693 }
694
695 /* EOF */