1 /* $Id: utils.c,v 1.21 1999/12/20 02:14:37 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.
574 static NTSTATUS
LdrPerformRelocations (PIMAGE_NT_HEADERS NTHeaders
,
577 USHORT NumberOfEntries
;
583 PRELOCATION_DIRECTORY RelocationDir
;
584 PRELOCATION_ENTRY RelocationBlock
;
588 RelocationRVA
= NTHeaders
->OptionalHeader
589 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
]
594 RelocationDir
= (PRELOCATION_DIRECTORY
)
595 ((PCHAR
)ImageBase
+ RelocationRVA
);
597 while (RelocationDir
->SizeOfBlock
)
599 Delta32
= (ULONG
)(ImageBase
-
600 NTHeaders
->OptionalHeader
.ImageBase
);
601 RelocationBlock
= (PRELOCATION_ENTRY
) (
604 + sizeof (RELOCATION_DIRECTORY
)
607 RelocationDir
->SizeOfBlock
608 - sizeof (RELOCATION_DIRECTORY
)
610 / sizeof (RELOCATION_ENTRY
);
613 (i
< NumberOfEntries
);
618 RelocationBlock
[i
].TypeOffset
621 + RelocationDir
->VirtualAddress
;
623 * What kind of relocations should we perform
624 * for the current entry?
626 switch (RelocationBlock
[i
].TypeOffset
>> 12)
628 case TYPE_RELOC_ABSOLUTE
:
631 case TYPE_RELOC_HIGH
:
632 pValue16
= (PUSHORT
) (ImageBase
+ Offset
);
633 *pValue16
+= Delta32
>> 16;
637 pValue16
= (PUSHORT
)(ImageBase
+ Offset
);
638 *pValue16
+= Delta32
& 0xffff;
641 case TYPE_RELOC_HIGHLOW
:
642 pValue32
= (PULONG
) (ImageBase
+ Offset
);
643 *pValue32
+= Delta32
;
646 case TYPE_RELOC_HIGHADJ
:
647 /* FIXME: do the highadjust fixup */
649 "TYPE_RELOC_HIGHADJ fixup not implemented"
652 return(STATUS_UNSUCCESSFUL
);
655 DPRINT("unexpected fixup type\n");
656 return STATUS_UNSUCCESSFUL
;
659 RelocationRVA
+= RelocationDir
->SizeOfBlock
;
660 RelocationDir
= (PRELOCATION_DIRECTORY
) (
666 return STATUS_SUCCESS
;
670 /**********************************************************************
675 * Compute the entry point for every symbol the DLL imports
676 * from other modules.
687 static NTSTATUS
LdrFixupImports(PIMAGE_NT_HEADERS NTHeaders
,
690 PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory
;
695 DPRINT("LdrFixupImports(NTHeaders %x, ImageBase %x)\n", NTHeaders
,
699 * Process each import module.
701 ImportModuleDirectory
= (PIMAGE_IMPORT_MODULE_DIRECTORY
)(
702 ImageBase
+ NTHeaders
->OptionalHeader
703 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
705 DPRINT1("ImportModuleDirectory %x\n", ImportModuleDirectory
);
706 DPRINT("ImportModuleDirectory %x\n", ImportModuleDirectory
);
708 while (ImportModuleDirectory
->dwRVAModuleName
)
710 PVOID
* ImportAddressList
;
711 PULONG FunctionNameList
;
715 DPRINT("ImportModule->Directory->dwRVAModuleName %s\n",
716 (PCHAR
)(ImageBase
+ ImportModuleDirectory
->dwRVAModuleName
));
718 Status
= LdrLoadDll(&Module
,
720 +ImportModuleDirectory
->dwRVAModuleName
));
721 if (!NT_SUCCESS(Status
))
726 * Get the import address list.
728 ImportAddressList
= (PVOID
*)(NTHeaders
->OptionalHeader
.ImageBase
729 + ImportModuleDirectory
->dwRVAFunctionAddressList
);
732 * Get the list of functions to import.
734 if (ImportModuleDirectory
->dwRVAFunctionNameList
!= 0)
736 FunctionNameList
= (PULONG
) (
738 + ImportModuleDirectory
->dwRVAFunctionNameList
743 FunctionNameList
= (PULONG
) (
745 + ImportModuleDirectory
->dwRVAFunctionAddressList
749 * Walk through function list and fixup addresses.
751 while (*FunctionNameList
!= 0L)
753 if ((*FunctionNameList
) & 0x80000000)
755 Ordinal
= (*FunctionNameList
) & 0x7fffffff;
757 LdrGetExportByOrdinal(
779 if ((*ImportAddressList
) == NULL
)
781 dprintf("Failed to import %s\n", pName
);
782 return STATUS_UNSUCCESSFUL
;
788 ImportModuleDirectory
++;
790 return STATUS_SUCCESS
;
794 /**********************************************************************
799 * 1. Map the DLL's sections into memory.
800 * 2. Relocate, if needed the DLL.
801 * 3. Fixup any imported symbol.
802 * 4. Compute the DLL's entry point.
806 * Address at which the DLL's image
810 * Handle of the section that contains
814 * NULL on error; otherwise the entry point
815 * to call for initializing the DLL.
822 PEPFUNC
LdrPEStartup (PVOID ImageBase
,
823 HANDLE SectionHandle
)
827 PIMAGE_DOS_HEADER DosHeader
;
828 PIMAGE_NT_HEADERS NTHeaders
;
832 * Overlay DOS and WNT headers structures
833 * to the DLL's image.
835 DosHeader
= (PIMAGE_DOS_HEADER
) ImageBase
;
836 NTHeaders
= (PIMAGE_NT_HEADERS
) (ImageBase
+ DosHeader
->e_lfanew
);
839 * Initialize image sections.
841 LdrMapSections(NtCurrentProcess(),
847 * If the base address is different from the
848 * one the DLL is actually loaded, perform any
851 if (ImageBase
!= (PVOID
) NTHeaders
->OptionalHeader
.ImageBase
)
853 Status
= LdrPerformRelocations(NTHeaders
, ImageBase
);
854 if (!NT_SUCCESS(Status
))
856 dprintf("LdrPerformRelocations() failed\n");
862 * If the DLL's imports symbols from other
863 * modules, fixup the imported calls entry points.
865 if (NTHeaders
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
866 .VirtualAddress
!= 0)
868 DPRINT("About to fixup imports\n");
869 Status
= LdrFixupImports(NTHeaders
, ImageBase
);
870 if (!NT_SUCCESS(Status
))
872 dprintf("LdrFixupImports() failed\n");
878 * Compute the DLL's entry point's address.
880 EntryPoint
= (PEPFUNC
) (ImageBase
881 + NTHeaders
->OptionalHeader
.AddressOfEntryPoint
);
882 DPRINT("LdrPEStartup() = %x\n",EntryPoint
);
886 NTSTATUS
LdrUnloadDll(PDLL Dll
)
888 PDLLMAIN_FUNC Entrypoint
;
891 if ( Dll
== NULL
|| Dll
== &LdrDllListHead
)
895 if ( Dll
->ReferenceCount
> 1 )
897 Dll
->ReferenceCount
--;
898 return STATUS_SUCCESS
;
901 if (( Dll
->Headers
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
) == IMAGE_FILE_DLL
) {
903 Entrypoint
= (PDLLMAIN_FUNC
) LdrPEStartup(Dll
->BaseAddress
,
905 if (Entrypoint
!= NULL
)
907 DPRINT("Calling entry point at 0x%08x\n", Entrypoint
);
908 if (FALSE
== Entrypoint(Dll
,
912 DPRINT("NTDLL.LDR: DLL failed to detach\n");
917 DPRINT("NTDLL.LDR: DLL detached successfully\n");
922 DPRINT("NTDLL.LDR: Entrypoint is NULL for \n");
926 Status
= ZwUnmapViewOfSection(NtCurrentProcess(),
929 ZwClose(Dll
->SectionHandle
);
934 static IMAGE_RESOURCE_DIRECTORY_ENTRY
* LdrGetNextEntry(IMAGE_RESOURCE_DIRECTORY
*ResourceDir
, LPCWSTR ResourceName
, ULONG Offset
)
938 WORD NumberOfNamedEntries
;
939 WORD NumberOfIdEntries
;
943 if ( (((ULONG
)ResourceDir
) & 0xF0000000) != 0 ) {
944 return (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)NULL
;
948 NumberOfIdEntries
= ResourceDir
->NumberOfIdEntries
;
949 NumberOfNamedEntries
= ResourceDir
->NumberOfNamedEntries
;
950 if ( ( NumberOfIdEntries
+ NumberOfNamedEntries
) == 0) {
951 return &ResourceDir
->DirectoryEntries
[0];
954 if ( HIWORD(ResourceName
) != 0 ) {
955 Length
= wcslen(ResourceName
);
956 Entries
= ResourceDir
->NumberOfNamedEntries
;
958 IMAGE_RESOURCE_DIR_STRING_U
*DirString
;
961 DirString
= (IMAGE_RESOURCE_DIR_STRING_U
*)(((ULONG
)ResourceDir
->DirectoryEntries
[Entries
].Name
& (~0xF0000000)) + Offset
);
963 if ( DirString
->Length
== Length
&& wcscmp(DirString
->NameString
, ResourceName
) == 0 ) {
964 return &ResourceDir
->DirectoryEntries
[Entries
];
966 } while (Entries
> 0);
969 Entries
= ResourceDir
->NumberOfIdEntries
+ ResourceDir
->NumberOfNamedEntries
;
973 if ( (LPWSTR
)ResourceDir
->DirectoryEntries
[Entries
].Name
== ResourceName
) {
974 return &ResourceDir
->DirectoryEntries
[Entries
];
976 } while (Entries
> ResourceDir
->NumberOfNamedEntries
);
988 NTSTATUS
LdrFindResource_U(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
**ResourceDataEntry
,LPCWSTR ResourceName
, ULONG ResourceType
,ULONG Language
)
990 IMAGE_RESOURCE_DIRECTORY
*ResourceTypeDir
;
991 IMAGE_RESOURCE_DIRECTORY
*ResourceNameDir
;
992 IMAGE_RESOURCE_DIRECTORY
*ResourceLangDir
;
994 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceTypeDirEntry
;
995 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceNameDirEntry
;
996 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceLangDirEntry
;
998 PIMAGE_OPTIONAL_HEADER OptionalHeader
;
1004 OptionalHeader
= & Dll
->Headers
->OptionalHeader
;
1005 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1006 OptionalHeader
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
;
1007 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1008 ((ULONG
)ResourceTypeDir
+ (ULONG
)Dll
->BaseAddress
);
1011 Offset
= (ULONG
)ResourceTypeDir
;
1013 ResourceTypeDirEntry
= LdrGetNextEntry(ResourceTypeDir
, (LPWSTR
)ResourceType
, Offset
);
1015 if ( ResourceTypeDirEntry
!= NULL
) {
1016 ResourceNameDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceTypeDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1018 ResourceNameDirEntry
= LdrGetNextEntry(ResourceNameDir
, ResourceName
, Offset
);
1020 if ( ResourceNameDirEntry
!= NULL
) {
1022 ResourceLangDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceNameDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1024 ResourceLangDirEntry
= LdrGetNextEntry(ResourceLangDir
, (LPWSTR
)Language
, Offset
);
1025 if ( ResourceLangDirEntry
!= NULL
) {
1027 *ResourceDataEntry
= (IMAGE_RESOURCE_DATA_ENTRY
*)(ResourceLangDirEntry
->OffsetToData
+
1028 (ULONG
)ResourceTypeDir
);
1029 return STATUS_SUCCESS
;
1045 NTSTATUS
LdrAccessResource(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
*ResourceDataEntry
, void **Data
)
1047 PIMAGE_SECTION_HEADER Sections
;
1053 if ( ResourceDataEntry
== NULL
)
1059 Sections
= (PIMAGE_SECTION_HEADER
) SECHDROFFSET(Dll
->BaseAddress
);
1062 (i
< Dll
->Headers
->FileHeader
.NumberOfSections
);
1066 if (Sections
[i
].VirtualAddress
<= ResourceDataEntry
->OffsetToData
1067 && Sections
[i
].VirtualAddress
+ Sections
[i
].Misc
.VirtualSize
> ResourceDataEntry
->OffsetToData
)
1071 if ( i
== Dll
->Headers
->FileHeader
.NumberOfSections
) {
1076 *Data
= (void *)(((ULONG
)Dll
->BaseAddress
+ ResourceDataEntry
->OffsetToData
- (ULONG
)Sections
[i
].VirtualAddress
) +
1077 (ULONG
)Sections
[i
].PointerToRawData
);
1080 return STATUS_SUCCESS
;