Improved internal dll handling
[reactos.git] / reactos / lib / ntdll / ldr / utils.c
1 /* $Id: utils.c,v 1.32 2000/09/01 17:05:09 ekohl Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: lib/ntdll/ldr/startup.c
6 * PURPOSE: Process startup for PE executables
7 * PROGRAMMERS: Jean Michault
8 * Rex Jolliff (rex@lvcablemodem.com)
9 */
10
11 /*
12 * TODO:
13 * - Fix calling of entry points
14 * - Handle loading flags correctly
15 * - any more ??
16 */
17
18 /* INCLUDES *****************************************************************/
19
20 #include <reactos/config.h>
21 #include <ddk/ntddk.h>
22 #include <windows.h>
23 #include <string.h>
24 #include <wchar.h>
25 #include <ntdll/ldr.h>
26 #include <ntos/minmax.h>
27 #include <napi/shared_data.h>
28
29
30 #ifdef DBG_NTDLL_LDR_UTILS
31 #define NDEBUG
32 #endif
33 #include <ntdll/ntdll.h>
34
35 /* PROTOTYPES ****************************************************************/
36
37
38 /* Type for a DLL's entry point */
39 typedef
40 WINBOOL
41 STDCALL
42 (* PDLLMAIN_FUNC) (
43 HANDLE hInst,
44 ULONG ul_reason_for_call,
45 LPVOID lpReserved
46 );
47
48 static
49 NTSTATUS
50 LdrFindDll (PLDR_MODULE *Dll,PUNICODE_STRING Name);
51
52
53 /* FUNCTIONS *****************************************************************/
54
55 /***************************************************************************
56 * NAME LOCAL
57 * LdrAdjustDllName
58 *
59 * DESCRIPTION
60 * Adjusts the name of a dll to a fully qualified name.
61 *
62 * ARGUMENTS
63 * FullDllName: Pointer to caller supplied storage for the fully
64 * qualified dll name.
65 * DllName: Pointer to the dll name.
66 * BaseName: TRUE: Only the file name is passed to FullDllName
67 * FALSE: The full path is preserved in FullDllName
68 *
69 * RETURN VALUE
70 * None
71 *
72 * REVISIONS
73 *
74 * NOTE
75 * A given path is not affected by the adjustment, but the file
76 * name only:
77 * ntdll --> ntdll.dll
78 * ntdll. --> ntdll
79 * ntdll.xyz --> ntdll.xyz
80 */
81
82 static VOID
83 LdrAdjustDllName (PUNICODE_STRING FullDllName,
84 PUNICODE_STRING DllName,
85 BOOLEAN BaseName)
86 {
87 WCHAR Buffer[MAX_PATH];
88 ULONG Length;
89 PWCHAR Extension;
90 PWCHAR Pointer;
91
92 Length = DllName->Length / sizeof(WCHAR);
93
94 if (BaseName == TRUE)
95 {
96 /* get the base dll name */
97 Pointer = DllName->Buffer + Length;
98 Extension = Pointer;
99
100 do
101 {
102 --Pointer;
103 }
104 while (Pointer >= DllName->Buffer && *Pointer != L'\\' && *Pointer != L'/');
105
106 Pointer++;
107 Length = Extension - Pointer;
108 memmove (Buffer, Pointer, Length * sizeof(WCHAR));
109 }
110 else
111 {
112 /* get the full dll name */
113 memmove (Buffer, DllName->Buffer, DllName->Length);
114 }
115
116 /* Build the DLL's absolute name */
117 Extension = wcsrchr (Buffer, L'.');
118 if ((Extension != NULL) && (*Extension == L'.'))
119 {
120 /* with extension - remove dot if it's the last character */
121 if (Buffer[Length - 1] == L'.')
122 Length--;
123 Buffer[Length] = 0;
124 }
125 else
126 {
127 /* name without extension - assume that it is .dll */
128 memmove (Buffer + Length, L".dll", 10);
129 }
130
131 RtlCreateUnicodeString (FullDllName,
132 Buffer);
133 }
134
135
136 /***************************************************************************
137 * NAME EXPORTED
138 * LdrLoadDll
139 *
140 * DESCRIPTION
141 *
142 * ARGUMENTS
143 *
144 * RETURN VALUE
145 *
146 * REVISIONS
147 *
148 * NOTE
149 *
150 */
151
152 NTSTATUS STDCALL
153 LdrLoadDll (IN PWSTR SearchPath OPTIONAL,
154 IN ULONG LoadFlags,
155 IN PUNICODE_STRING Name,
156 OUT PVOID *BaseAddress OPTIONAL)
157 {
158 WCHAR SearchPathBuffer[MAX_PATH];
159 WCHAR FullDosName[MAX_PATH];
160 UNICODE_STRING AdjustedName;
161 UNICODE_STRING FullNtFileName;
162 OBJECT_ATTRIBUTES FileObjectAttributes;
163 char BlockBuffer [1024];
164 PIMAGE_DOS_HEADER DosHeader;
165 NTSTATUS Status;
166 PIMAGE_NT_HEADERS NTHeaders;
167 ULONG ImageSize;
168 ULONG InitialViewSize;
169 PVOID ImageBase;
170 HANDLE FileHandle;
171 HANDLE SectionHandle;
172 PDLLMAIN_FUNC Entrypoint = NULL;
173 PLDR_MODULE Module;
174
175 if ( Name == NULL )
176 {
177 *BaseAddress = NtCurrentPeb()->ImageBaseAddress;
178 return STATUS_SUCCESS;
179 }
180
181 *BaseAddress = NULL;
182
183 DPRINT("LdrLoadDll(Name \"%wZ\" BaseAddress %x)\n",
184 Name, BaseAddress);
185
186 /* adjust the full dll name */
187 LdrAdjustDllName (&AdjustedName,
188 Name,
189 FALSE);
190 DPRINT("AdjustedName: %wZ\n", &AdjustedName);
191
192 /*
193 * Test if dll is already loaded.
194 */
195 if (LdrFindDll(&Module, &AdjustedName) == STATUS_SUCCESS)
196 {
197 DPRINT("DLL %wZ already loaded.\n", &AdjustedName);
198 if (Module->LoadCount != -1)
199 Module->LoadCount++;
200 *BaseAddress = Module->BaseAddress;
201 return STATUS_SUCCESS;
202 }
203
204 if (SearchPath == NULL)
205 {
206 PKUSER_SHARED_DATA SharedUserData =
207 (PKUSER_SHARED_DATA)USER_SHARED_DATA_BASE;
208
209 SearchPath = SearchPathBuffer;
210 wcscpy (SearchPathBuffer, SharedUserData->NtSystemRoot);
211 wcscat (SearchPathBuffer, L"\\system32;");
212 wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot);
213 }
214
215 DPRINT("SearchPath %S\n", SearchPath);
216
217 if (RtlDosSearchPath_U (SearchPath,
218 AdjustedName.Buffer,
219 NULL,
220 MAX_PATH,
221 FullDosName,
222 NULL) == 0)
223 return STATUS_DLL_NOT_FOUND;
224
225 DPRINT("FullDosName %S\n", FullDosName);
226
227 RtlFreeUnicodeString (&AdjustedName);
228
229 if (!RtlDosPathNameToNtPathName_U (FullDosName,
230 &FullNtFileName,
231 NULL,
232 NULL))
233 return STATUS_DLL_NOT_FOUND;
234
235 DPRINT("FullNtFileName %wZ\n", &FullNtFileName);
236
237 InitializeObjectAttributes(
238 & FileObjectAttributes,
239 & FullNtFileName,
240 0,
241 NULL,
242 NULL
243 );
244
245 DPRINT("Opening dll \"%wZ\"\n", &FullNtFileName);
246
247 Status = ZwOpenFile(
248 & FileHandle,
249 FILE_ALL_ACCESS,
250 & FileObjectAttributes,
251 NULL,
252 0,
253 0
254 );
255 if (!NT_SUCCESS(Status))
256 {
257 DbgPrint("Dll open of %wZ failed: Status = 0x%08x\n",
258 &FullNtFileName, Status);
259 RtlFreeUnicodeString (&FullNtFileName);
260 return Status;
261 }
262 RtlFreeUnicodeString (&FullNtFileName);
263
264 Status = ZwReadFile(
265 FileHandle,
266 0,
267 0,
268 0,
269 0,
270 BlockBuffer,
271 sizeof BlockBuffer,
272 0,
273 0
274 );
275 if (!NT_SUCCESS(Status))
276 {
277 DPRINT("Dll header read failed: Status = 0x%08x\n", Status);
278 ZwClose(FileHandle);
279 return Status;
280 }
281 /*
282 * Overlay DOS and NT headers structures to the
283 * buffer with DLL's header raw data.
284 */
285 DosHeader = (PIMAGE_DOS_HEADER) BlockBuffer;
286 NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew);
287 /*
288 * Check it is a PE image file.
289 */
290 if ((DosHeader->e_magic != IMAGE_DOS_MAGIC)
291 || (DosHeader->e_lfanew == 0L)
292 || (*(PULONG)(NTHeaders) != IMAGE_PE_MAGIC))
293 {
294 DPRINT("NTDLL format invalid\n");
295 ZwClose(FileHandle);
296
297 return STATUS_UNSUCCESSFUL;
298 }
299
300 ImageBase = (PVOID) NTHeaders->OptionalHeader.ImageBase;
301 ImageSize = NTHeaders->OptionalHeader.SizeOfImage;
302
303 DPRINT("ImageBase 0x%08x\n", ImageBase);
304
305 /*
306 * Create a section for dll.
307 */
308 Status = ZwCreateSection(
309 & SectionHandle,
310 SECTION_ALL_ACCESS,
311 NULL,
312 NULL,
313 PAGE_READWRITE,
314 MEM_COMMIT,
315 FileHandle
316 );
317 if (!NT_SUCCESS(Status))
318 {
319 DPRINT("NTDLL create section failed: Status = 0x%08x\n", Status);
320 ZwClose(FileHandle);
321 return Status;
322 }
323
324 /*
325 * Map the dll into the process.
326 */
327 InitialViewSize =
328 DosHeader->e_lfanew
329 + sizeof (IMAGE_NT_HEADERS)
330 + sizeof (IMAGE_SECTION_HEADER) * NTHeaders->FileHeader.NumberOfSections;
331 Status = ZwMapViewOfSection(
332 SectionHandle,
333 NtCurrentProcess(),
334 (PVOID*)&ImageBase,
335 0,
336 InitialViewSize,
337 NULL,
338 &InitialViewSize,
339 0,
340 MEM_COMMIT,
341 PAGE_READWRITE
342 );
343 if (!NT_SUCCESS(Status))
344 {
345 DbgPrint("NTDLL.LDR: map view of section failed (Status %x)\n",
346 Status);
347 ZwClose(FileHandle);
348 return(Status);
349 }
350 ZwClose(FileHandle);
351
352 Module = RtlAllocateHeap(
353 RtlGetProcessHeap(),
354 0,
355 sizeof (LDR_MODULE)
356 );
357 Module->BaseAddress = (PVOID)ImageBase;
358 Module->SizeOfImage = ImageSize;
359 if (NtCurrentPeb()->Ldr->Initialized == TRUE)
360 {
361 /* loading while app is running */
362 Module->LoadCount = 1;
363 }
364 else
365 {
366 /*
367 * loading while app is initializing
368 * dll must not be unloaded
369 */
370 Module->LoadCount = -1;
371 }
372
373 Module->TlsIndex = 0; // ???
374 Module->CheckSum = 0; // ???
375 Module->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp;
376
377 RtlCreateUnicodeString (&Module->FullDllName,
378 FullDosName);
379 RtlCreateUnicodeString (&Module->BaseDllName,
380 wcsrchr(FullDosName, L'\\') + 1);
381
382 InsertTailList(&NtCurrentPeb()->Ldr->InLoadOrderModuleList,
383 &Module->InLoadOrderModuleList);
384
385 DPRINT ("BaseDllName %wZ\n", &Module->BaseDllName);
386
387 if ((NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) ==
388 IMAGE_FILE_DLL)
389 {
390 Entrypoint =
391 (PDLLMAIN_FUNC) LdrPEStartup(
392 ImageBase,
393 SectionHandle
394 );
395
396 if (Entrypoint != NULL)
397 {
398 DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
399 if (FALSE == Entrypoint(
400 Module->BaseAddress,
401 DLL_PROCESS_ATTACH,
402 NULL
403 ))
404 {
405 DPRINT("NTDLL.LDR: DLL \"%s\" failed to initialize\n", fqname);
406 /* FIXME: should clean up and fail */
407 }
408 else
409 {
410 DPRINT("NTDLL.LDR: DLL \"%s\" initialized successfully\n", fqname);
411 }
412 }
413 else
414 {
415 DPRINT("NTDLL.LDR: Entrypoint is NULL for \"%s\"\n", fqname);
416 }
417 }
418
419 Module->EntryPoint = (ULONG)Entrypoint;
420
421 *BaseAddress = Module->BaseAddress;
422 return STATUS_SUCCESS;
423 }
424
425
426 /***************************************************************************
427 * NAME LOCAL
428 * LdrFindDll
429 *
430 * DESCRIPTION
431 *
432 * ARGUMENTS
433 *
434 * RETURN VALUE
435 *
436 * REVISIONS
437 *
438 * NOTE
439 *
440 */
441 static NTSTATUS LdrFindDll(PLDR_MODULE *Dll, PUNICODE_STRING Name)
442 {
443 PLIST_ENTRY ModuleListHead;
444 PLIST_ENTRY Entry;
445 PLDR_MODULE Module;
446
447 DPRINT("NTDLL.LdrFindDll(Name %wZ)\n", Name);
448
449 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
450 Entry = ModuleListHead->Flink;
451
452 // NULL is the current process
453 if ( Name == NULL )
454 {
455 *Dll = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
456 return STATUS_SUCCESS;
457 }
458
459 while (Entry != ModuleListHead)
460 {
461 Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
462
463 DPRINT("Scanning %wZ %wZ\n", &Module->BaseDllName, Name);
464
465 if (RtlCompareUnicodeString(&Module->BaseDllName, Name, TRUE) == 0)
466 {
467 *Dll = Module;
468 return STATUS_SUCCESS;
469 }
470
471 Entry = Entry->Flink;
472 }
473
474 DPRINT("Failed to find dll %wZ\n", Name);
475
476 return STATUS_UNSUCCESSFUL;
477 }
478
479
480 /**********************************************************************
481 * NAME
482 * LdrMapSections
483 *
484 * DESCRIPTION
485 *
486 * ARGUMENTS
487 *
488 * RETURN VALUE
489 *
490 * REVISIONS
491 *
492 * NOTE
493 *
494 */
495 NTSTATUS LdrMapSections(HANDLE ProcessHandle,
496 PVOID ImageBase,
497 HANDLE SectionHandle,
498 PIMAGE_NT_HEADERS NTHeaders)
499 {
500 ULONG i;
501 NTSTATUS Status;
502
503 for (i = 0; (i < NTHeaders->FileHeader.NumberOfSections); i++)
504 {
505 PIMAGE_SECTION_HEADER Sections;
506 LARGE_INTEGER Offset;
507 ULONG Base;
508 ULONG Size;
509
510 Sections = (PIMAGE_SECTION_HEADER) SECHDROFFSET(ImageBase);
511 Base = (ULONG) (Sections[i].VirtualAddress + ImageBase);
512 Offset.u.LowPart = Sections[i].PointerToRawData;
513 Offset.u.HighPart = 0;
514
515 Size = max(Sections[i].Misc.VirtualSize, Sections[i].SizeOfRawData);
516
517 DPRINT("Mapping section %d offset %x base %x size %x\n",
518 i, Offset.u.LowPart, Base, Sections[i].Misc.VirtualSize);
519 DPRINT("Size %x\n", Sections[i].SizeOfRawData);
520
521 Status = ZwMapViewOfSection(SectionHandle,
522 ProcessHandle,
523 (PVOID*)&Base,
524 0,
525 Size,
526 &Offset,
527 (PULONG)&Size,
528 0,
529 MEM_COMMIT,
530 PAGE_READWRITE);
531 if (!NT_SUCCESS(Status))
532 {
533 DPRINT("Failed to map section");
534 return(Status);
535 }
536 }
537 return STATUS_SUCCESS;
538 }
539
540
541 /**********************************************************************
542 * NAME LOCAL
543 * LdrGetExportByOrdinal
544 *
545 * DESCRIPTION
546 *
547 * ARGUMENTS
548 *
549 * RETURN VALUE
550 *
551 * REVISIONS
552 *
553 * NOTE
554 *
555 */
556 static PVOID
557 LdrGetExportByOrdinal (
558 PVOID BaseAddress,
559 ULONG Ordinal
560 )
561 {
562 PIMAGE_EXPORT_DIRECTORY ExportDir;
563 PDWORD * ExFunctions;
564 USHORT * ExOrdinals;
565
566 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
567 RtlImageDirectoryEntryToData (BaseAddress,
568 TRUE,
569 IMAGE_DIRECTORY_ENTRY_EXPORT,
570 NULL);
571
572
573 ExOrdinals = (USHORT *)
574 RVA(
575 BaseAddress,
576 ExportDir->AddressOfNameOrdinals
577 );
578 ExFunctions = (PDWORD *)
579 RVA(
580 BaseAddress,
581 ExportDir->AddressOfFunctions
582 );
583 DbgPrint(
584 "LdrGetExportByOrdinal(Ordinal %d) = %x\n",
585 Ordinal,
586 ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]]
587 );
588 return(ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]]);
589 }
590
591
592 /**********************************************************************
593 * NAME LOCAL
594 * LdrGetExportByName
595 *
596 * DESCRIPTION
597 *
598 * ARGUMENTS
599 *
600 * RETURN VALUE
601 *
602 * REVISIONS
603 *
604 * NOTE
605 *
606 */
607 static PVOID
608 LdrGetExportByName (
609 PVOID BaseAddress,
610 PUCHAR SymbolName
611 )
612 {
613 PIMAGE_EXPORT_DIRECTORY ExportDir;
614 PDWORD * ExFunctions;
615 PDWORD * ExNames;
616 USHORT * ExOrdinals;
617 ULONG i;
618 PVOID ExName;
619 ULONG Ordinal;
620
621 // DPRINT(
622 // "LdrFindExport(Module %x, SymbolName %s)\n",
623 // Module,
624 // SymbolName
625 // );
626
627 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
628 RtlImageDirectoryEntryToData (BaseAddress,
629 TRUE,
630 IMAGE_DIRECTORY_ENTRY_EXPORT,
631 NULL);
632
633 /*
634 * Get header pointers
635 */
636 ExNames = (PDWORD *)
637 RVA(
638 BaseAddress,
639 ExportDir->AddressOfNames
640 );
641 ExOrdinals = (USHORT *)
642 RVA(
643 BaseAddress,
644 ExportDir->AddressOfNameOrdinals
645 );
646 ExFunctions = (PDWORD *)
647 RVA(
648 BaseAddress,
649 ExportDir->AddressOfFunctions
650 );
651 for ( i = 0;
652 ( i < ExportDir->NumberOfFunctions);
653 i++
654 )
655 {
656 ExName = RVA(
657 BaseAddress,
658 ExNames[i]
659 );
660 // DPRINT(
661 // "Comparing '%s' '%s'\n",
662 // ExName,
663 // SymbolName
664 // );
665 if (strcmp(ExName,SymbolName) == 0)
666 {
667 Ordinal = ExOrdinals[i];
668 return(RVA(BaseAddress, ExFunctions[Ordinal]));
669 }
670 }
671
672 DbgPrint("LdrGetExportByName() = failed to find %s\n",SymbolName);
673
674 return NULL;
675 }
676
677
678 /**********************************************************************
679 * NAME LOCAL
680 * LdrPerformRelocations
681 *
682 * DESCRIPTION
683 * Relocate a DLL's memory image.
684 *
685 * ARGUMENTS
686 *
687 * RETURN VALUE
688 *
689 * REVISIONS
690 *
691 * NOTE
692 *
693 */
694 static NTSTATUS LdrPerformRelocations (PIMAGE_NT_HEADERS NTHeaders,
695 PVOID ImageBase)
696 {
697 USHORT NumberOfEntries;
698 PUSHORT pValue16;
699 ULONG RelocationRVA;
700 ULONG Delta32;
701 ULONG Offset;
702 PULONG pValue32;
703 PRELOCATION_DIRECTORY RelocationDir;
704 PRELOCATION_ENTRY RelocationBlock;
705 int i;
706
707
708 RelocationRVA = NTHeaders->OptionalHeader
709 .DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
710 .VirtualAddress;
711
712 if (RelocationRVA)
713 {
714 RelocationDir = (PRELOCATION_DIRECTORY)
715 ((PCHAR)ImageBase + RelocationRVA);
716
717 while (RelocationDir->SizeOfBlock)
718 {
719 Delta32 = (ULONG)(ImageBase -
720 NTHeaders->OptionalHeader.ImageBase);
721 RelocationBlock = (PRELOCATION_ENTRY) (
722 RelocationRVA
723 + ImageBase
724 + sizeof (RELOCATION_DIRECTORY)
725 );
726 NumberOfEntries = (
727 RelocationDir->SizeOfBlock
728 - sizeof (RELOCATION_DIRECTORY)
729 )
730 / sizeof (RELOCATION_ENTRY);
731
732 for ( i = 0;
733 (i < NumberOfEntries);
734 i++
735 )
736 {
737 Offset = (
738 RelocationBlock[i].TypeOffset
739 & 0xfff
740 )
741 + RelocationDir->VirtualAddress;
742 /*
743 * What kind of relocations should we perform
744 * for the current entry?
745 */
746 switch (RelocationBlock[i].TypeOffset >> 12)
747 {
748 case TYPE_RELOC_ABSOLUTE:
749 break;
750
751 case TYPE_RELOC_HIGH:
752 pValue16 = (PUSHORT) (ImageBase + Offset);
753 *pValue16 += Delta32 >> 16;
754 break;
755
756 case TYPE_RELOC_LOW:
757 pValue16 = (PUSHORT)(ImageBase + Offset);
758 *pValue16 += Delta32 & 0xffff;
759 break;
760
761 case TYPE_RELOC_HIGHLOW:
762 pValue32 = (PULONG) (ImageBase + Offset);
763 *pValue32 += Delta32;
764 break;
765
766 case TYPE_RELOC_HIGHADJ:
767 /* FIXME: do the highadjust fixup */
768 DPRINT(
769 "TYPE_RELOC_HIGHADJ fixup not implemented"
770 ", sorry\n"
771 );
772 return(STATUS_UNSUCCESSFUL);
773
774 default:
775 DPRINT("unexpected fixup type\n");
776 return STATUS_UNSUCCESSFUL;
777 }
778 }
779 RelocationRVA += RelocationDir->SizeOfBlock;
780 RelocationDir = (PRELOCATION_DIRECTORY) (
781 ImageBase
782 + RelocationRVA
783 );
784 }
785 }
786 return STATUS_SUCCESS;
787 }
788
789
790 /**********************************************************************
791 * NAME LOCAL
792 * LdrFixupImports
793 *
794 * DESCRIPTION
795 * Compute the entry point for every symbol the DLL imports
796 * from other modules.
797 *
798 * ARGUMENTS
799 *
800 * RETURN VALUE
801 *
802 * REVISIONS
803 *
804 * NOTE
805 *
806 */
807 static NTSTATUS LdrFixupImports(PIMAGE_NT_HEADERS NTHeaders,
808 PVOID ImageBase)
809 {
810 PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory;
811 ULONG Ordinal;
812 PVOID BaseAddress;
813 NTSTATUS Status;
814
815 DPRINT("LdrFixupImports(NTHeaders %x, ImageBase %x)\n", NTHeaders,
816 ImageBase);
817
818 /*
819 * Process each import module.
820 */
821 ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY)(
822 ImageBase + NTHeaders->OptionalHeader
823 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
824 .VirtualAddress);
825 DPRINT("ImportModuleDirectory %x\n", ImportModuleDirectory);
826
827 while (ImportModuleDirectory->dwRVAModuleName)
828 {
829 PVOID * ImportAddressList;
830 PULONG FunctionNameList;
831 UNICODE_STRING DllName;
832 DWORD pName;
833 PWORD pHint;
834
835 DPRINT("ImportModule->Directory->dwRVAModuleName %s\n",
836 (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName));
837
838 RtlCreateUnicodeStringFromAsciiz (&DllName,
839 (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName));
840
841 Status = LdrLoadDll(NULL,
842 0,
843 &DllName,
844 &BaseAddress);
845 RtlFreeUnicodeString (&DllName);
846 if (!NT_SUCCESS(Status))
847 {
848 return Status;
849 }
850
851 /*
852 * Get the import address list.
853 */
854 ImportAddressList = (PVOID *)(NTHeaders->OptionalHeader.ImageBase
855 + ImportModuleDirectory->dwRVAFunctionAddressList);
856
857 /*
858 * Get the list of functions to import.
859 */
860 if (ImportModuleDirectory->dwRVAFunctionNameList != 0)
861 {
862 FunctionNameList = (PULONG) (
863 ImageBase
864 + ImportModuleDirectory->dwRVAFunctionNameList
865 );
866 }
867 else
868 {
869 FunctionNameList = (PULONG) (
870 ImageBase
871 + ImportModuleDirectory->dwRVAFunctionAddressList
872 );
873 }
874 /*
875 * Walk through function list and fixup addresses.
876 */
877 while (*FunctionNameList != 0L)
878 {
879 if ((*FunctionNameList) & 0x80000000)
880 {
881 Ordinal = (*FunctionNameList) & 0x7fffffff;
882 *ImportAddressList =
883 LdrGetExportByOrdinal(BaseAddress,
884 Ordinal);
885 }
886 else
887 {
888 pName = (DWORD) (
889 ImageBase
890 + *FunctionNameList
891 + 2);
892 pHint = (PWORD) (
893 ImageBase
894 + *FunctionNameList);
895
896 *ImportAddressList =
897 LdrGetExportByName(BaseAddress,
898 (PUCHAR) pName);
899 if ((*ImportAddressList) == NULL)
900 {
901 DbgPrint("Failed to import %s\n", pName);
902 return STATUS_UNSUCCESSFUL;
903 }
904 }
905 ImportAddressList++;
906 FunctionNameList++;
907 }
908 ImportModuleDirectory++;
909 }
910 return STATUS_SUCCESS;
911 }
912
913
914 /**********************************************************************
915 * NAME
916 * LdrPEStartup
917 *
918 * DESCRIPTION
919 * 1. Map the DLL's sections into memory.
920 * 2. Relocate, if needed the DLL.
921 * 3. Fixup any imported symbol.
922 * 4. Compute the DLL's entry point.
923 *
924 * ARGUMENTS
925 * ImageBase
926 * Address at which the DLL's image
927 * is loaded.
928 *
929 * SectionHandle
930 * Handle of the section that contains
931 * the DLL's image.
932 *
933 * RETURN VALUE
934 * NULL on error; otherwise the entry point
935 * to call for initializing the DLL.
936 *
937 * REVISIONS
938 *
939 * NOTE
940 *
941 */
942 PEPFUNC LdrPEStartup (PVOID ImageBase,
943 HANDLE SectionHandle)
944 {
945 NTSTATUS Status;
946 PEPFUNC EntryPoint = NULL;
947 PIMAGE_DOS_HEADER DosHeader;
948 PIMAGE_NT_HEADERS NTHeaders;
949
950 /*
951 * Overlay DOS and WNT headers structures
952 * to the DLL's image.
953 */
954 DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
955 NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew);
956
957 /*
958 * Initialize image sections.
959 */
960 if (SectionHandle != NULL)
961 {
962 LdrMapSections(NtCurrentProcess(),
963 ImageBase,
964 SectionHandle,
965 NTHeaders);
966 }
967
968 /*
969 * If the base address is different from the
970 * one the DLL is actually loaded, perform any
971 * relocation.
972 */
973 if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
974 {
975 Status = LdrPerformRelocations(NTHeaders, ImageBase);
976 if (!NT_SUCCESS(Status))
977 {
978 DbgPrint("LdrPerformRelocations() failed\n");
979 return NULL;
980 }
981 }
982
983 /*
984 * If the DLL's imports symbols from other
985 * modules, fixup the imported calls entry points.
986 */
987 if (NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
988 .VirtualAddress != 0)
989 {
990 DPRINT("About to fixup imports\n");
991 Status = LdrFixupImports(NTHeaders, ImageBase);
992 if (!NT_SUCCESS(Status))
993 {
994 DbgPrint("LdrFixupImports() failed\n");
995 return NULL;
996 }
997 }
998
999 /*
1000 * Compute the DLL's entry point's address.
1001 */
1002 if (NTHeaders->OptionalHeader.AddressOfEntryPoint != 0)
1003 {
1004 EntryPoint = (PEPFUNC) (ImageBase
1005 + NTHeaders->OptionalHeader.AddressOfEntryPoint);
1006 }
1007 DPRINT("LdrPEStartup() = %x\n",EntryPoint);
1008 return EntryPoint;
1009 }
1010
1011
1012 NTSTATUS STDCALL
1013 LdrUnloadDll (IN PVOID BaseAddress)
1014 {
1015 PIMAGE_NT_HEADERS NtHeaders;
1016 PDLLMAIN_FUNC Entrypoint;
1017 PLIST_ENTRY ModuleListHead;
1018 PLIST_ENTRY Entry;
1019 PLDR_MODULE Module;
1020 NTSTATUS Status;
1021
1022 if (BaseAddress == NULL)
1023 return STATUS_SUCCESS;
1024
1025 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1026 Entry = ModuleListHead->Flink;
1027
1028 while (Entry != ModuleListHead);
1029 {
1030 Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1031 if (Module->BaseAddress == BaseAddress)
1032 {
1033 if (Module->LoadCount == -1)
1034 {
1035 /* never unload this dll */
1036 return STATUS_SUCCESS;
1037 }
1038 else if (Module->LoadCount > 1)
1039 {
1040 Module->LoadCount--;
1041 return STATUS_SUCCESS;
1042 }
1043
1044 NtHeaders = RtlImageNtHeader (Module->BaseAddress);
1045 if ((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL)
1046 {
1047 Entrypoint = (PDLLMAIN_FUNC) LdrPEStartup(Module->BaseAddress,
1048 Module->SectionHandle);
1049 if (Entrypoint != NULL)
1050 {
1051 DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
1052 Entrypoint(Module->BaseAddress,
1053 DLL_PROCESS_DETACH,
1054 NULL);
1055 }
1056 else
1057 {
1058 DPRINT("NTDLL.LDR: Entrypoint is NULL for \n");
1059 }
1060 }
1061 Status = ZwUnmapViewOfSection (NtCurrentProcess (),
1062 Module->BaseAddress);
1063 ZwClose (Module->SectionHandle);
1064
1065 /* remove the module entry from the list */
1066 RtlFreeUnicodeString (&Module->FullDllName);
1067 RtlFreeUnicodeString (&Module->BaseDllName);
1068 RemoveEntryList (Entry);
1069 RtlFreeHeap (RtlGetProcessHeap (), 0, Module);
1070
1071 return Status;
1072 }
1073
1074 Entry = Entry->Flink;
1075 }
1076
1077 DPRINT("NTDLL.LDR: Dll not found\n")
1078
1079 return STATUS_UNSUCCESSFUL;
1080 }
1081
1082
1083 NTSTATUS STDCALL
1084 LdrFindResource_U(PVOID BaseAddress,
1085 PLDR_RESOURCE_INFO ResourceInfo,
1086 ULONG Level,
1087 PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry)
1088 {
1089 PIMAGE_RESOURCE_DIRECTORY ResDir;
1090 PIMAGE_RESOURCE_DIRECTORY ResBase;
1091 PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
1092 NTSTATUS Status = STATUS_SUCCESS;
1093 ULONG EntryCount;
1094 PWCHAR ws;
1095 ULONG i;
1096 ULONG Id;
1097
1098 DPRINT ("LdrFindResource_U()\n");
1099
1100 /* Get the pointer to the resource directory */
1101 ResDir = (PIMAGE_RESOURCE_DIRECTORY)
1102 RtlImageDirectoryEntryToData (BaseAddress,
1103 TRUE,
1104 IMAGE_DIRECTORY_ENTRY_RESOURCE,
1105 &i);
1106 if (ResDir == NULL)
1107 {
1108 return STATUS_RESOURCE_DATA_NOT_FOUND;
1109 }
1110
1111 DPRINT("ResourceDirectory: %x\n", (ULONG)ResDir);
1112
1113 ResBase = ResDir;
1114
1115 /* Let's go into resource tree */
1116 for (i = 0; i < Level; i++)
1117 {
1118 DPRINT("ResDir: %x\n", (ULONG)ResDir);
1119 Id = ((PULONG)ResourceInfo)[i];
1120 EntryCount = ResDir->NumberOfNamedEntries;
1121 ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
1122 DPRINT("ResEntry %x\n", (ULONG)ResEntry);
1123 if (Id & 0xFFFF0000)
1124 {
1125 /* Resource name is a unicode string */
1126 for (; EntryCount--; ResEntry++)
1127 {
1128 /* Scan entries for equal name */
1129 if (ResEntry->Name & 0x80000000)
1130 {
1131 ws = (PWCHAR)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF));
1132 if (!wcsncmp((PWCHAR)Id, ws + 1, *ws ) &&
1133 wcslen((PWCHAR)Id) == (int)*ws )
1134 {
1135 goto found;
1136 }
1137 }
1138 }
1139 }
1140 else
1141 {
1142 /* We use ID number instead of string */
1143 ResEntry += EntryCount;
1144 EntryCount = ResDir->NumberOfIdEntries;
1145 for (; EntryCount--; ResEntry++)
1146 {
1147 /* Scan entries for equal name */
1148 if (ResEntry->Name == Id)
1149 {
1150 DPRINT("ID entry found %x\n", Id);
1151 goto found;
1152 }
1153 }
1154 }
1155 DPRINT("Error %lu\n", i);
1156
1157 switch (i)
1158 {
1159 case 0:
1160 return STATUS_RESOURCE_TYPE_NOT_FOUND;
1161
1162 case 1:
1163 return STATUS_RESOURCE_NAME_NOT_FOUND;
1164
1165 case 2:
1166 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries)
1167 {
1168 /* Use the first available language */
1169 ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
1170 break;
1171 }
1172 return STATUS_RESOURCE_LANG_NOT_FOUND;
1173
1174 case 3:
1175 return STATUS_RESOURCE_DATA_NOT_FOUND;
1176
1177 default:
1178 return STATUS_INVALID_PARAMETER;
1179 }
1180 found:;
1181 ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResBase +
1182 (ResEntry->OffsetToData & 0x7FFFFFFF));
1183 }
1184 DPRINT("ResourceDataEntry: %x\n", (ULONG)ResDir);
1185
1186 if (ResourceDataEntry)
1187 {
1188 *ResourceDataEntry = (PVOID)ResDir;
1189 }
1190
1191 return Status;
1192 }
1193
1194
1195 NTSTATUS STDCALL
1196 LdrAccessResource(IN PVOID BaseAddress,
1197 IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
1198 OUT PVOID *Resource OPTIONAL,
1199 OUT PULONG Size OPTIONAL)
1200 {
1201 PIMAGE_SECTION_HEADER Section;
1202 PIMAGE_NT_HEADERS NtHeader;
1203 ULONG SectionRva;
1204 ULONG SectionVa;
1205 ULONG DataSize;
1206 ULONG Offset = 0;
1207 ULONG Data;
1208
1209 Data = (ULONG)RtlImageDirectoryEntryToData (BaseAddress,
1210 TRUE,
1211 IMAGE_DIRECTORY_ENTRY_RESOURCE,
1212 &DataSize);
1213 if (Data == 0)
1214 return STATUS_RESOURCE_DATA_NOT_FOUND;
1215
1216 if ((ULONG)BaseAddress & 1)
1217 {
1218 /* loaded as ordinary file */
1219 NtHeader = RtlImageNtHeader((PVOID)((ULONG)BaseAddress & ~1UL));
1220 Offset = (ULONG)BaseAddress - Data + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
1221 Section = RtlImageRvaToSection (NtHeader, BaseAddress, NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
1222 if (Section == NULL)
1223 {
1224 return STATUS_RESOURCE_DATA_NOT_FOUND;
1225 }
1226
1227 if (Section->Misc.VirtualSize < ResourceDataEntry->OffsetToData)
1228 {
1229 SectionRva = RtlImageRvaToSection (NtHeader, BaseAddress, ResourceDataEntry->OffsetToData)->VirtualAddress;
1230 SectionVa = RtlImageRvaToVa(NtHeader, BaseAddress, SectionRva, NULL);
1231 Offset = SectionRva - SectionVa + Data - Section->VirtualAddress;
1232 }
1233 }
1234
1235 if (Resource)
1236 {
1237 *Resource = (PVOID)(ResourceDataEntry->OffsetToData - Offset + (ULONG)BaseAddress);
1238 }
1239
1240 if (Size)
1241 {
1242 *Size = ResourceDataEntry->Size;
1243 }
1244
1245 return STATUS_SUCCESS;
1246 }
1247
1248
1249 NTSTATUS STDCALL
1250 LdrDisableThreadCalloutsForDll (IN PVOID BaseAddress)
1251 {
1252 PLIST_ENTRY ModuleListHead;
1253 PLIST_ENTRY Entry;
1254 PLDR_MODULE Module;
1255 NTSTATUS Status;
1256
1257 Status = STATUS_DLL_NOT_FOUND;
1258
1259 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1260 Entry = ModuleListHead->Flink;
1261
1262 while (Entry != ModuleListHead);
1263 {
1264 Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1265 if (Module->BaseAddress == BaseAddress)
1266 {
1267 if (Module->TlsIndex == 0)
1268 {
1269 Module->Flags |= 0x00040000;
1270 Status = STATUS_SUCCESS;
1271 }
1272 return Status;
1273 }
1274
1275 Entry = Entry->Flink;
1276 }
1277
1278 return Status;
1279 }
1280
1281
1282 NTSTATUS STDCALL
1283 LdrFindResourceDirectory_U (IN PVOID BaseAddress,
1284 WCHAR **name,
1285 DWORD level,
1286 OUT PVOID *addr)
1287 {
1288 PIMAGE_RESOURCE_DIRECTORY ResDir;
1289 PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
1290 ULONG EntryCount;
1291 ULONG i;
1292 NTSTATUS Status = STATUS_SUCCESS;
1293 WCHAR *ws;
1294
1295 /* Get the pointer to the resource directory */
1296 ResDir = (PIMAGE_RESOURCE_DIRECTORY)
1297 RtlImageDirectoryEntryToData (BaseAddress,
1298 TRUE,
1299 IMAGE_DIRECTORY_ENTRY_RESOURCE,
1300 &i);
1301 if (ResDir == NULL)
1302 {
1303 return STATUS_RESOURCE_DATA_NOT_FOUND;
1304 }
1305
1306 /* Let's go into resource tree */
1307 for (i = 0; i < level; i++, name++)
1308 {
1309 EntryCount = ResDir->NumberOfNamedEntries;
1310 ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
1311 if ((ULONG)(*name) & 0xFFFF0000)
1312 {
1313 /* Resource name is a unicode string */
1314 for (; EntryCount--; ResEntry++)
1315 {
1316 /* Scan entries for equal name */
1317 if (ResEntry->Name & 0x80000000)
1318 {
1319 ws = (WCHAR*)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF));
1320 if (!wcsncmp( *name, ws + 1, *ws ) && wcslen( *name ) == (int)*ws )
1321 {
1322 goto found;
1323 }
1324 }
1325 }
1326 }
1327 else
1328 {
1329 /* We use ID number instead of string */
1330 ResEntry += EntryCount;
1331 EntryCount = ResDir->NumberOfIdEntries;
1332 for (; EntryCount--; ResEntry++)
1333 {
1334 /* Scan entries for equal name */
1335 if (ResEntry->Name == (ULONG)(*name))
1336 goto found;
1337 }
1338 }
1339
1340 switch (i)
1341 {
1342 case 0:
1343 return STATUS_RESOURCE_TYPE_NOT_FOUND;
1344
1345 case 1:
1346 return STATUS_RESOURCE_NAME_NOT_FOUND;
1347
1348 case 2:
1349 Status = STATUS_RESOURCE_LANG_NOT_FOUND;
1350 /* Just use first language entry */
1351 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries)
1352 {
1353 ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
1354 break;
1355 }
1356 return Status;
1357
1358 case 3:
1359 return STATUS_RESOURCE_DATA_NOT_FOUND;
1360
1361 default:
1362 return STATUS_INVALID_PARAMETER;
1363 }
1364 found:;
1365 ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResDir + ResEntry->OffsetToData);
1366 }
1367
1368 if (addr)
1369 {
1370 *addr = (PVOID)ResDir;
1371 }
1372
1373 return Status;
1374 }
1375
1376
1377 NTSTATUS STDCALL
1378 LdrGetDllHandle (IN ULONG Unknown1,
1379 IN ULONG Unknown2,
1380 IN PUNICODE_STRING DllName,
1381 OUT PVOID *BaseAddress)
1382 {
1383 UNICODE_STRING FullDllName;
1384 PLIST_ENTRY ModuleListHead;
1385 PLIST_ENTRY Entry;
1386 PLDR_MODULE Module;
1387
1388 DPRINT("LdrGetDllHandle (Unknown1 %x Unknown2 %x DllName %wZ BaseAddress %p)\n",
1389 Unknown1, Unknown2, DllName, BaseAddress);
1390
1391 /* NULL is the current executable */
1392 if ( DllName == NULL )
1393 {
1394 *BaseAddress = NtCurrentPeb()->ImageBaseAddress;
1395 DPRINT1("BaseAddress %x\n", *BaseAddress);
1396 return STATUS_SUCCESS;
1397 }
1398
1399 LdrAdjustDllName (&FullDllName,
1400 DllName,
1401 TRUE);
1402
1403 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1404 Entry = ModuleListHead->Flink;
1405
1406 while (Entry != ModuleListHead)
1407 {
1408 Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList);
1409
1410 DPRINT("Scanning %wZ %wZ\n",
1411 &Module->BaseDllName,
1412 FullDllName);
1413
1414 if (!RtlCompareUnicodeString(&Module->BaseDllName, &FullDllName, TRUE))
1415 {
1416 RtlFreeUnicodeString (&FullDllName);
1417 *BaseAddress = Module->BaseAddress;
1418 DPRINT("BaseAddress %x\n", *BaseAddress);
1419 return STATUS_SUCCESS;
1420 }
1421
1422 Entry = Entry->Flink;
1423 }
1424
1425 DbgPrint("Failed to find dll %wZ\n", &FullDllName);
1426 RtlFreeUnicodeString (&FullDllName);
1427 *BaseAddress = NULL;
1428 return STATUS_DLL_NOT_FOUND;
1429 }
1430
1431
1432 NTSTATUS STDCALL
1433 LdrGetProcedureAddress (IN PVOID BaseAddress,
1434 IN PANSI_STRING Name,
1435 IN ULONG Ordinal,
1436 OUT PVOID *ProcedureAddress)
1437 {
1438 PIMAGE_EXPORT_DIRECTORY ExportDir;
1439 PUSHORT OrdinalPtr;
1440 PULONG NamePtr;
1441 PULONG AddressPtr;
1442 ULONG i = 0;
1443
1444 DPRINT("LdrGetProcedureAddress (BaseAddress %x Name %Z Ordinal %lu ProcedureAddress %x)\n",
1445 BaseAddress, Name, Ordinal, ProcedureAddress);
1446
1447 /* Get the pointer to the export directory */
1448 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
1449 RtlImageDirectoryEntryToData (BaseAddress,
1450 TRUE,
1451 IMAGE_DIRECTORY_ENTRY_EXPORT,
1452 &i);
1453
1454 DPRINT("ExportDir %x i %lu\n", ExportDir, i);
1455
1456 if (!ExportDir || !i || !ProcedureAddress)
1457 {
1458 return STATUS_INVALID_PARAMETER;
1459 }
1460
1461 AddressPtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfFunctions);
1462 if (Name && Name->Length)
1463 {
1464 /* by name */
1465 OrdinalPtr = (PUSHORT)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNameOrdinals);
1466 NamePtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNames);
1467 for( i = 0; i < ExportDir->NumberOfNames; i++, NamePtr++, OrdinalPtr++)
1468 {
1469 if (!_strnicmp(Name->Buffer, (char*)(BaseAddress + *NamePtr), Name->Length))
1470 {
1471 *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[*OrdinalPtr]);
1472 return STATUS_SUCCESS;
1473 }
1474 }
1475 DbgPrint("LdrGetProcedureAddress: Can't resolve symbol '%Z'\n", Name);
1476 }
1477 else
1478 {
1479 /* by ordinal */
1480 Ordinal &= 0x0000FFFF;
1481 if (Ordinal - ExportDir->Base < ExportDir->NumberOfFunctions)
1482 {
1483 *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[Ordinal - ExportDir->Base]);
1484 return STATUS_SUCCESS;
1485 }
1486 DbgPrint("LdrGetProcedureAddress: Can't resolve symbol @%d\n", Ordinal);
1487 }
1488
1489 return STATUS_PROCEDURE_NOT_FOUND;
1490 }
1491
1492
1493 NTSTATUS STDCALL
1494 LdrShutdownProcess (VOID)
1495 {
1496
1497 return STATUS_SUCCESS;
1498 }
1499
1500
1501 NTSTATUS STDCALL
1502 LdrShutdownThread (VOID)
1503 {
1504
1505 return STATUS_SUCCESS;
1506 }
1507
1508 /* EOF */