1 /* $Id: utils.c,v 1.19 1999/12/11 21:14:47 dwelch Exp $
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)
11 /* INCLUDES *****************************************************************/
13 #include <reactos/config.h>
14 #define WIN32_NO_STATUS
15 #define WIN32_NO_PEHDR
17 #include <ddk/ntddk.h>
20 #include <internal/string.h>
22 #include <ntdll/ldr.h>
24 #ifdef DBG_NTDLL_LDR_UTILS
27 #include <ntdll/ntdll.h>
29 /* FUNCTIONS *****************************************************************/
32 /* Type for a DLL's entry point */
38 ULONG ul_reason_for_call
,
44 LdrFindDll (PDLL
* Dll
,PCHAR Name
);
46 /**********************************************************************
62 NTSTATUS
LdrLoadDll (PDLL
* Dll
,
65 char fqname
[255] = "\\??\\C:\\reactos\\system32\\";
66 ANSI_STRING AnsiString
;
67 UNICODE_STRING UnicodeString
;
68 OBJECT_ATTRIBUTES FileObjectAttributes
;
69 char BlockBuffer
[1024];
70 PIMAGE_DOS_HEADER DosHeader
;
72 PIMAGE_NT_HEADERS NTHeaders
;
73 PEPFUNC DllStartupAddr
;
75 ULONG InitialViewSize
;
79 PDLLMAIN_FUNC Entrypoint
;
85 *Dll
= &LdrDllListHead
;
86 return STATUS_SUCCESS
;
89 DPRINT("LdrLoadDll(Base %x, Name \"%s\")\n", Dll
, Name
);
92 * Build the DLL's absolute name
95 if ( strncmp(Name
,"\\??\\",3) != 0 ) {
100 strncpy(fqname
, Name
, 256);
102 DPRINT("fqname \"%s\"\n", fqname
);
104 * Open the DLL's image file.
107 if (LdrFindDll(Dll
, Name
) == STATUS_SUCCESS
)
108 return STATUS_SUCCESS
;
115 RtlAnsiStringToUnicodeString(
121 InitializeObjectAttributes(
122 & FileObjectAttributes
,
129 DPRINT("Opening dll \"%s\"\n", fqname
);
134 & FileObjectAttributes
,
139 if (!NT_SUCCESS(Status
))
141 dprintf("Dll open of %s failed: Status = 0x%08x\n",
156 if (!NT_SUCCESS(Status
))
158 DPRINT("Dll header read failed: Status = 0x%08x\n", Status
);
163 * Overlay DOS and NT headers structures to the
164 * buffer with DLL's header raw data.
166 DosHeader
= (PIMAGE_DOS_HEADER
) BlockBuffer
;
167 NTHeaders
= (PIMAGE_NT_HEADERS
) (BlockBuffer
+ DosHeader
->e_lfanew
);
169 * Check it is a PE image file.
171 if ( (DosHeader
->e_magic
!= IMAGE_DOS_MAGIC
)
172 || (DosHeader
->e_lfanew
== 0L)
173 // || (*(PULONG)((PUCHAR)BlockBuffer + DosHeader->e_lfanew) != IMAGE_PE_MAGIC)
174 || (*(PULONG
)(NTHeaders
) != IMAGE_PE_MAGIC
)
177 DPRINT("NTDLL format invalid\n");
180 return STATUS_UNSUCCESSFUL
;
183 // NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew);
184 ImageBase
= (PVOID
) NTHeaders
->OptionalHeader
.ImageBase
;
185 ImageSize
= NTHeaders
->OptionalHeader
.SizeOfImage
;
187 DPRINT("ImageBase 0x%08x\n", ImageBase
);
192 + NTHeaders
->OptionalHeader
.AddressOfEntryPoint
195 * Create a section for NTDLL.
197 Status
= ZwCreateSection(
206 if (!NT_SUCCESS(Status
))
208 DPRINT("NTDLL create section failed: Status = 0x%08x\n", Status
);
213 * Map the NTDLL into the process.
217 + sizeof (IMAGE_NT_HEADERS
)
218 + sizeof (IMAGE_SECTION_HEADER
) * NTHeaders
->FileHeader
.NumberOfSections
;
219 Status
= ZwMapViewOfSection(
231 if (!NT_SUCCESS(Status
))
233 dprintf("NTDLL.LDR: map view of section failed (Status %x)\n",
240 (*Dll
) = RtlAllocateHeap(
245 (*Dll
)->Headers
= NTHeaders
;
246 (*Dll
)->BaseAddress
= (PVOID
)ImageBase
;
247 (*Dll
)->Next
= LdrDllListHead
.Next
;
248 (*Dll
)->Prev
= & LdrDllListHead
;
249 (*Dll
)->ReferenceCount
= 1;
250 LdrDllListHead
.Next
->Prev
= (*Dll
);
251 LdrDllListHead
.Next
= (*Dll
);
254 if (((*Dll
)->Headers
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
) ==
259 (PDLLMAIN_FUNC
) LdrPEStartup(
263 if (Entrypoint
!= NULL
)
265 DPRINT("Calling entry point at 0x%08x\n", Entrypoint
);
266 if (FALSE
== Entrypoint(
272 DPRINT("NTDLL.LDR: DLL \"%s\" failed to initialize\n", fqname
);
273 /* FIXME: should clean up and fail */
277 DPRINT("NTDLL.LDR: DLL \"%s\" initialized successfully\n", fqname
);
282 DPRINT("NTDLL.LDR: Entrypoint is NULL for \"%s\"\n", fqname
);
285 return STATUS_SUCCESS
;
289 /**********************************************************************
304 static NTSTATUS
LdrFindDll(PDLL
* Dll
, PCHAR Name
)
306 PIMAGE_EXPORT_DIRECTORY ExportDir
;
308 PIMAGE_OPTIONAL_HEADER OptionalHeader
;
311 DPRINT("NTDLL.LdrFindDll(Name %s)\n", Name
);
313 current
= & LdrDllListHead
;
315 // NULL is the current process
320 return STATUS_SUCCESS
;
325 OptionalHeader
= & current
->Headers
->OptionalHeader
;
326 ExportDir
= (PIMAGE_EXPORT_DIRECTORY
)
327 OptionalHeader
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
]
329 ExportDir
= (PIMAGE_EXPORT_DIRECTORY
)
330 ((ULONG
)ExportDir
+ (ULONG
)current
->BaseAddress
);
332 DPRINT("Scanning %x %x %x\n",ExportDir
->Name
,
333 current
->BaseAddress
,
334 (ExportDir
->Name
+ current
->BaseAddress
));
335 DPRINT("Scanning %s %s\n",
336 ExportDir
->Name
+ current
->BaseAddress
, Name
);
338 if (_stricmp(ExportDir
->Name
+ current
->BaseAddress
, Name
) == 0)
341 current
->ReferenceCount
++;
342 return STATUS_SUCCESS
;
345 current
= current
->Next
;
347 } while (current
!= & LdrDllListHead
);
349 DPRINT("Failed to find dll %s\n",Name
);
355 /**********************************************************************
370 NTSTATUS
LdrMapSections(HANDLE ProcessHandle
,
372 HANDLE SectionHandle
,
373 PIMAGE_NT_HEADERS NTHeaders
)
379 for (i
= 0; (i
< NTHeaders
->FileHeader
.NumberOfSections
); i
++)
381 PIMAGE_SECTION_HEADER Sections
;
382 LARGE_INTEGER Offset
;
386 Sections
= (PIMAGE_SECTION_HEADER
) SECHDROFFSET(ImageBase
);
387 Base
= (ULONG
) (Sections
[i
].VirtualAddress
+ ImageBase
);
388 Offset
.u
.LowPart
= Sections
[i
].PointerToRawData
;
389 Offset
.u
.HighPart
= 0;
391 Size
= max(Sections
[i
].Misc
.VirtualSize
, Sections
[i
].SizeOfRawData
);
393 DPRINT("Mapping section %d offset %x base %x size %x\n",
394 i
, Offset
.u
.LowPart
, Base
, Sections
[i
].Misc
.VirtualSize
);
395 DPRINT("Size %x\n", Sections
[i
].SizeOfRawData
);
397 Status
= ZwMapViewOfSection(SectionHandle
,
407 if (!NT_SUCCESS(Status
))
409 DPRINT("Failed to map section");
413 return STATUS_SUCCESS
;
417 /**********************************************************************
419 * LdrGetExportByOrdinal
434 LdrGetExportByOrdinal (
439 PIMAGE_EXPORT_DIRECTORY ExportDir
;
440 PDWORD
* ExFunctions
;
445 + (Module
->Headers
->OptionalHeader
446 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
]
451 ExOrdinals
= (USHORT
*)
454 ExportDir
->AddressOfNameOrdinals
456 ExFunctions
= (PDWORD
*)
459 ExportDir
->AddressOfFunctions
462 "LdrGetExportByOrdinal(Ordinal %d) = %x\n",
464 ExFunctions
[ExOrdinals
[Ordinal
- ExportDir
->Base
]]
466 return(ExFunctions
[ExOrdinals
[Ordinal
- ExportDir
->Base
]]);
470 /**********************************************************************
492 PIMAGE_EXPORT_DIRECTORY ExportDir
;
493 PDWORD
* ExFunctions
;
501 // "LdrFindExport(Module %x, SymbolName %s)\n",
508 + (Module
->Headers
->OptionalHeader
509 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
]
514 * Get header pointers
519 ExportDir
->AddressOfNames
521 ExOrdinals
= (USHORT
*)
524 ExportDir
->AddressOfNameOrdinals
526 ExFunctions
= (PDWORD
*)
529 ExportDir
->AddressOfFunctions
532 ( i
< ExportDir
->NumberOfFunctions
);
541 // "Comparing '%s' '%s'\n",
545 if (strcmp(ExName
,SymbolName
) == 0)
547 Ordinal
= ExOrdinals
[i
];
548 return(RVA(Module
->BaseAddress
, ExFunctions
[Ordinal
]));
552 dprintf("LdrGetExportByName() = failed to find %s\n",SymbolName
);
558 /**********************************************************************
560 * LdrPerformRelocations
563 * Relocate a DLL's memory image.
576 LdrPerformRelocations (
577 PIMAGE_NT_HEADERS NTHeaders
,
581 USHORT NumberOfEntries
;
587 PRELOCATION_DIRECTORY RelocationDir
;
588 PRELOCATION_ENTRY RelocationBlock
;
593 NTHeaders
->OptionalHeader
594 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
]
598 RelocationDir
= (PRELOCATION_DIRECTORY
)
599 ((PCHAR
)ImageBase
+ RelocationRVA
);
601 while (RelocationDir
->SizeOfBlock
)
603 Delta32
= (unsigned long) (
605 - NTHeaders
->OptionalHeader
.ImageBase
607 RelocationBlock
= (PRELOCATION_ENTRY
) (
610 + sizeof (RELOCATION_DIRECTORY
)
613 RelocationDir
->SizeOfBlock
614 - sizeof (RELOCATION_DIRECTORY
)
616 / sizeof (RELOCATION_ENTRY
);
619 (i
< NumberOfEntries
);
624 RelocationBlock
[i
].TypeOffset
627 + RelocationDir
->VirtualAddress
;
629 * What kind of relocations should we perform
630 * for the current entry?
632 switch (RelocationBlock
[i
].TypeOffset
>> 12)
634 case TYPE_RELOC_ABSOLUTE
:
637 case TYPE_RELOC_HIGH
:
638 pValue16
= (PUSHORT
) (ImageBase
+ Offset
);
639 *pValue16
+= Delta32
>> 16;
643 pValue16
= (PUSHORT
)(ImageBase
+ Offset
);
644 *pValue16
+= Delta32
& 0xffff;
647 case TYPE_RELOC_HIGHLOW
:
648 pValue32
= (PULONG
) (ImageBase
+ Offset
);
649 *pValue32
+= Delta32
;
652 case TYPE_RELOC_HIGHADJ
:
653 /* FIXME: do the highadjust fixup */
655 "TYPE_RELOC_HIGHADJ fixup not implemented"
658 return(STATUS_UNSUCCESSFUL
);
661 DPRINT("unexpected fixup type\n");
662 return STATUS_UNSUCCESSFUL
;
665 RelocationRVA
+= RelocationDir
->SizeOfBlock
;
666 RelocationDir
= (PRELOCATION_DIRECTORY
) (
672 return STATUS_SUCCESS
;
676 /**********************************************************************
681 * Compute the entry point for every symbol the DLL imports
682 * from other modules.
693 static NTSTATUS
LdrFixupImports(PIMAGE_NT_HEADERS NTHeaders
,
696 PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory
;
701 DPRINT1("LdrFixupImports(NTHeaders %x, ImageBase %x)\n", NTHeaders
,
705 * Process each import module.
707 ImportModuleDirectory
= (PIMAGE_IMPORT_MODULE_DIRECTORY
)(
708 ImageBase
+ NTHeaders
->OptionalHeader
709 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
711 DPRINT1("ImportModuleDirectory %x\n", ImportModuleDirectory
);
713 while (ImportModuleDirectory
->dwRVAModuleName
)
715 PVOID
* ImportAddressList
;
716 PULONG FunctionNameList
;
720 DPRINT1("ImportModule->Directory->dwRVAModuleName %s\n",
721 (PCHAR
)(ImageBase
+ ImportModuleDirectory
->dwRVAModuleName
));
723 Status
= LdrLoadDll(&Module
,
725 +ImportModuleDirectory
->dwRVAModuleName
));
726 if (!NT_SUCCESS(Status
))
731 * Get the import address list.
733 ImportAddressList
= (PVOID
*)(NTHeaders
->OptionalHeader
.ImageBase
734 + ImportModuleDirectory
->dwRVAFunctionAddressList
);
737 * Get the list of functions to import.
739 if (ImportModuleDirectory
->dwRVAFunctionNameList
!= 0)
741 FunctionNameList
= (PULONG
) (
743 + ImportModuleDirectory
->dwRVAFunctionNameList
748 FunctionNameList
= (PULONG
) (
750 + ImportModuleDirectory
->dwRVAFunctionAddressList
754 * Walk through function list and fixup addresses.
756 while (*FunctionNameList
!= 0L)
758 if ((*FunctionNameList
) & 0x80000000)
760 Ordinal
= (*FunctionNameList
) & 0x7fffffff;
762 LdrGetExportByOrdinal(
784 if ((*ImportAddressList
) == NULL
)
786 dprintf("Failed to import %s\n", pName
);
787 return STATUS_UNSUCCESSFUL
;
793 ImportModuleDirectory
++;
795 return STATUS_SUCCESS
;
799 /**********************************************************************
804 * 1. Map the DLL's sections into memory.
805 * 2. Relocate, if needed the DLL.
806 * 3. Fixup any imported symbol.
807 * 4. Compute the DLL's entry point.
811 * Address at which the DLL's image
815 * Handle of the section that contains
819 * NULL on error; otherwise the entry point
820 * to call for initializing the DLL.
827 PEPFUNC
LdrPEStartup (PVOID ImageBase
,
828 HANDLE SectionHandle
)
832 PIMAGE_DOS_HEADER DosHeader
;
833 PIMAGE_NT_HEADERS NTHeaders
;
837 * Overlay DOS and WNT headers structures
838 * to the DLL's image.
840 DosHeader
= (PIMAGE_DOS_HEADER
) ImageBase
;
841 NTHeaders
= (PIMAGE_NT_HEADERS
) (ImageBase
+ DosHeader
->e_lfanew
);
844 * Initialize image sections.
846 LdrMapSections(NtCurrentProcess(),
852 * If the base address is different from the
853 * one the DLL is actually loaded, perform any
856 if (ImageBase
!= (PVOID
) NTHeaders
->OptionalHeader
.ImageBase
)
858 Status
= LdrPerformRelocations(NTHeaders
, ImageBase
);
859 if (!NT_SUCCESS(Status
))
861 dprintf("LdrPerformRelocations() failed\n");
867 * If the DLL's imports symbols from other
868 * modules, fixup the imported calls entry points.
870 if (NTHeaders
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
871 .VirtualAddress
!= 0)
873 DPRINT("About to fixup imports\n");
874 Status
= LdrFixupImports(NTHeaders
, ImageBase
);
875 if (!NT_SUCCESS(Status
))
877 dprintf("LdrFixupImports() failed\n");
883 * Compute the DLL's entry point's address.
885 EntryPoint
= (PEPFUNC
) (ImageBase
886 + NTHeaders
->OptionalHeader
.AddressOfEntryPoint
);
887 DPRINT("LdrPEStartup() = %x\n",EntryPoint
);
891 NTSTATUS
LdrUnloadDll(PDLL Dll
)
893 PDLLMAIN_FUNC Entrypoint
;
896 if ( Dll
== NULL
|| Dll
== &LdrDllListHead
)
900 if ( Dll
->ReferenceCount
> 1 )
902 Dll
->ReferenceCount
--;
903 return STATUS_SUCCESS
;
906 if (( Dll
->Headers
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
) == IMAGE_FILE_DLL
) {
908 Entrypoint
= (PDLLMAIN_FUNC
) LdrPEStartup(Dll
->BaseAddress
,
910 if (Entrypoint
!= NULL
)
912 DPRINT("Calling entry point at 0x%08x\n", Entrypoint
);
913 if (FALSE
== Entrypoint(Dll
,
917 DPRINT("NTDLL.LDR: DLL failed to detach\n");
922 DPRINT("NTDLL.LDR: DLL detached successfully\n");
927 DPRINT("NTDLL.LDR: Entrypoint is NULL for \n");
931 Status
= ZwUnmapViewOfSection(NtCurrentProcess(),
934 ZwClose(Dll
->SectionHandle
);
939 static IMAGE_RESOURCE_DIRECTORY_ENTRY
* LdrGetNextEntry(IMAGE_RESOURCE_DIRECTORY
*ResourceDir
, LPCWSTR ResourceName
, ULONG Offset
)
943 WORD NumberOfNamedEntries
;
944 WORD NumberOfIdEntries
;
948 if ( (((ULONG
)ResourceDir
) & 0xF0000000) != 0 ) {
949 return (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)NULL
;
953 NumberOfIdEntries
= ResourceDir
->NumberOfIdEntries
;
954 NumberOfNamedEntries
= ResourceDir
->NumberOfNamedEntries
;
955 if ( ( NumberOfIdEntries
+ NumberOfNamedEntries
) == 0) {
956 return &ResourceDir
->DirectoryEntries
[0];
959 if ( HIWORD(ResourceName
) != 0 ) {
960 Length
= wcslen(ResourceName
);
961 Entries
= ResourceDir
->NumberOfNamedEntries
;
963 IMAGE_RESOURCE_DIR_STRING_U
*DirString
;
966 DirString
= (IMAGE_RESOURCE_DIR_STRING_U
*)(((ULONG
)ResourceDir
->DirectoryEntries
[Entries
].Name
& (~0xF0000000)) + Offset
);
968 if ( DirString
->Length
== Length
&& wcscmp(DirString
->NameString
, ResourceName
) == 0 ) {
969 return &ResourceDir
->DirectoryEntries
[Entries
];
971 } while (Entries
> 0);
974 Entries
= ResourceDir
->NumberOfIdEntries
+ ResourceDir
->NumberOfNamedEntries
;
978 if ( (LPWSTR
)ResourceDir
->DirectoryEntries
[Entries
].Name
== ResourceName
) {
979 return &ResourceDir
->DirectoryEntries
[Entries
];
981 } while (Entries
> ResourceDir
->NumberOfNamedEntries
);
993 NTSTATUS
LdrFindResource_U(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
**ResourceDataEntry
,LPCWSTR ResourceName
, ULONG ResourceType
,ULONG Language
)
995 IMAGE_RESOURCE_DIRECTORY
*ResourceTypeDir
;
996 IMAGE_RESOURCE_DIRECTORY
*ResourceNameDir
;
997 IMAGE_RESOURCE_DIRECTORY
*ResourceLangDir
;
999 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceTypeDirEntry
;
1000 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceNameDirEntry
;
1001 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceLangDirEntry
;
1003 PIMAGE_OPTIONAL_HEADER OptionalHeader
;
1009 OptionalHeader
= & Dll
->Headers
->OptionalHeader
;
1010 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1011 OptionalHeader
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
;
1012 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1013 ((ULONG
)ResourceTypeDir
+ (ULONG
)Dll
->BaseAddress
);
1016 Offset
= (ULONG
)ResourceTypeDir
;
1018 ResourceTypeDirEntry
= LdrGetNextEntry(ResourceTypeDir
, (LPWSTR
)ResourceType
, Offset
);
1020 if ( ResourceTypeDirEntry
!= NULL
) {
1021 ResourceNameDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceTypeDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1023 ResourceNameDirEntry
= LdrGetNextEntry(ResourceNameDir
, ResourceName
, Offset
);
1025 if ( ResourceNameDirEntry
!= NULL
) {
1027 ResourceLangDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceNameDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1029 ResourceLangDirEntry
= LdrGetNextEntry(ResourceLangDir
, (LPWSTR
)Language
, Offset
);
1030 if ( ResourceLangDirEntry
!= NULL
) {
1032 *ResourceDataEntry
= (IMAGE_RESOURCE_DATA_ENTRY
*)(ResourceLangDirEntry
->OffsetToData
+
1033 (ULONG
)ResourceTypeDir
);
1034 return STATUS_SUCCESS
;
1050 NTSTATUS
LdrAccessResource(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
*ResourceDataEntry
, void **Data
)
1052 PIMAGE_SECTION_HEADER Sections
;
1058 if ( ResourceDataEntry
== NULL
)
1064 Sections
= (PIMAGE_SECTION_HEADER
) SECHDROFFSET(Dll
->BaseAddress
);
1067 (i
< Dll
->Headers
->FileHeader
.NumberOfSections
);
1071 if (Sections
[i
].VirtualAddress
<= ResourceDataEntry
->OffsetToData
1072 && Sections
[i
].VirtualAddress
+ Sections
[i
].Misc
.VirtualSize
> ResourceDataEntry
->OffsetToData
)
1076 if ( i
== Dll
->Headers
->FileHeader
.NumberOfSections
) {
1081 *Data
= (void *)(((ULONG
)Dll
->BaseAddress
+ ResourceDataEntry
->OffsetToData
- (ULONG
)Sections
[i
].VirtualAddress
) +
1082 (ULONG
)Sections
[i
].PointerToRawData
);
1085 return STATUS_SUCCESS
;