1 /* $Id: utils.c,v 1.18 1999/12/09 19:14:45 ekohl 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 /**********************************************************************
372 HANDLE ProcessHandle
,
374 HANDLE SectionHandle
,
375 PIMAGE_NT_HEADERS NTHeaders
383 (i
< NTHeaders
->FileHeader
.NumberOfSections
);
387 PIMAGE_SECTION_HEADER Sections
;
388 LARGE_INTEGER Offset
;
391 Sections
= (PIMAGE_SECTION_HEADER
) SECHDROFFSET(ImageBase
);
392 Base
= (ULONG
) (Sections
[i
].VirtualAddress
+ ImageBase
);
393 Offset
.u
.LowPart
= Sections
[i
].PointerToRawData
;
394 Offset
.u
.HighPart
= 0;
395 Status
= ZwMapViewOfSection(
400 Sections
[i
].Misc
.VirtualSize
,
402 (PULONG
) & Sections
[i
].Misc
.VirtualSize
,
407 if (!NT_SUCCESS(Status
))
412 return STATUS_SUCCESS
;
416 /**********************************************************************
418 * LdrGetExportByOrdinal
433 LdrGetExportByOrdinal (
438 PIMAGE_EXPORT_DIRECTORY ExportDir
;
439 PDWORD
* ExFunctions
;
444 + (Module
->Headers
->OptionalHeader
445 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
]
450 ExOrdinals
= (USHORT
*)
453 ExportDir
->AddressOfNameOrdinals
455 ExFunctions
= (PDWORD
*)
458 ExportDir
->AddressOfFunctions
461 "LdrGetExportByOrdinal(Ordinal %d) = %x\n",
463 ExFunctions
[ExOrdinals
[Ordinal
- ExportDir
->Base
]]
465 return(ExFunctions
[ExOrdinals
[Ordinal
- ExportDir
->Base
]]);
469 /**********************************************************************
491 PIMAGE_EXPORT_DIRECTORY ExportDir
;
492 PDWORD
* ExFunctions
;
500 // "LdrFindExport(Module %x, SymbolName %s)\n",
507 + (Module
->Headers
->OptionalHeader
508 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
]
513 * Get header pointers
518 ExportDir
->AddressOfNames
520 ExOrdinals
= (USHORT
*)
523 ExportDir
->AddressOfNameOrdinals
525 ExFunctions
= (PDWORD
*)
528 ExportDir
->AddressOfFunctions
531 ( i
< ExportDir
->NumberOfFunctions
);
540 // "Comparing '%s' '%s'\n",
544 if (strcmp(ExName
,SymbolName
) == 0)
546 Ordinal
= ExOrdinals
[i
];
547 return(RVA(Module
->BaseAddress
, ExFunctions
[Ordinal
]));
551 dprintf("LdrGetExportByName() = failed to find %s\n",SymbolName
);
557 /**********************************************************************
559 * LdrPerformRelocations
562 * Relocate a DLL's memory image.
575 LdrPerformRelocations (
576 PIMAGE_NT_HEADERS NTHeaders
,
580 USHORT NumberOfEntries
;
586 PRELOCATION_DIRECTORY RelocationDir
;
587 PRELOCATION_ENTRY RelocationBlock
;
592 NTHeaders
->OptionalHeader
593 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_BASERELOC
]
597 RelocationDir
= (PRELOCATION_DIRECTORY
)
598 ((PCHAR
)ImageBase
+ RelocationRVA
);
600 while (RelocationDir
->SizeOfBlock
)
602 Delta32
= (unsigned long) (
604 - NTHeaders
->OptionalHeader
.ImageBase
606 RelocationBlock
= (PRELOCATION_ENTRY
) (
609 + sizeof (RELOCATION_DIRECTORY
)
612 RelocationDir
->SizeOfBlock
613 - sizeof (RELOCATION_DIRECTORY
)
615 / sizeof (RELOCATION_ENTRY
);
618 (i
< NumberOfEntries
);
623 RelocationBlock
[i
].TypeOffset
626 + RelocationDir
->VirtualAddress
;
628 * What kind of relocations should we perform
629 * for the current entry?
631 switch (RelocationBlock
[i
].TypeOffset
>> 12)
633 case TYPE_RELOC_ABSOLUTE
:
636 case TYPE_RELOC_HIGH
:
637 pValue16
= (PUSHORT
) (ImageBase
+ Offset
);
638 *pValue16
+= Delta32
>> 16;
642 pValue16
= (PUSHORT
)(ImageBase
+ Offset
);
643 *pValue16
+= Delta32
& 0xffff;
646 case TYPE_RELOC_HIGHLOW
:
647 pValue32
= (PULONG
) (ImageBase
+ Offset
);
648 *pValue32
+= Delta32
;
651 case TYPE_RELOC_HIGHADJ
:
652 /* FIXME: do the highadjust fixup */
654 "TYPE_RELOC_HIGHADJ fixup not implemented"
657 return(STATUS_UNSUCCESSFUL
);
660 DPRINT("unexpected fixup type\n");
661 return STATUS_UNSUCCESSFUL
;
664 RelocationRVA
+= RelocationDir
->SizeOfBlock
;
665 RelocationDir
= (PRELOCATION_DIRECTORY
) (
671 return STATUS_SUCCESS
;
675 /**********************************************************************
680 * Compute the entry point for every symbol the DLL imports
681 * from other modules.
692 static NTSTATUS
LdrFixupImports(PIMAGE_NT_HEADERS NTHeaders
,
695 PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory
;
702 * Process each import module.
704 ImportModuleDirectory
= (PIMAGE_IMPORT_MODULE_DIRECTORY
)(
705 ImageBase
+ NTHeaders
->OptionalHeader
706 .DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
709 while (ImportModuleDirectory
->dwRVAModuleName
)
711 PVOID
* ImportAddressList
;
712 PULONG FunctionNameList
;
716 DPRINT("ImportModule->Directory->dwRVAModuleName %s\n",
717 (PCHAR
)(ImageBase
+ ImportModuleDirectory
->dwRVAModuleName
));
719 Status
= LdrLoadDll(&Module
,
721 +ImportModuleDirectory
->dwRVAModuleName
));
722 if (!NT_SUCCESS(Status
))
727 * Get the import address list.
729 ImportAddressList
= (PVOID
*)(NTHeaders
->OptionalHeader
.ImageBase
730 + ImportModuleDirectory
->dwRVAFunctionAddressList
);
733 * Get the list of functions to import.
735 if (ImportModuleDirectory
->dwRVAFunctionNameList
!= 0)
737 FunctionNameList
= (PULONG
) (
739 + ImportModuleDirectory
->dwRVAFunctionNameList
744 FunctionNameList
= (PULONG
) (
746 + ImportModuleDirectory
->dwRVAFunctionAddressList
750 * Walk through function list and fixup addresses.
752 while (*FunctionNameList
!= 0L)
754 if ((*FunctionNameList
) & 0x80000000)
756 Ordinal
= (*FunctionNameList
) & 0x7fffffff;
758 LdrGetExportByOrdinal(
780 if ((*ImportAddressList
) == NULL
)
782 dprintf("Failed to import %s\n", pName
);
783 return STATUS_UNSUCCESSFUL
;
789 ImportModuleDirectory
++;
791 return STATUS_SUCCESS
;
795 /**********************************************************************
800 * 1. Map the DLL's sections into memory.
801 * 2. Relocate, if needed the DLL.
802 * 3. Fixup any imported symbol.
803 * 4. Compute the DLL's entry point.
807 * Address at which the DLL's image
811 * Handle of the section that contains
815 * NULL on error; otherwise the entry point
816 * to call for initializing the DLL.
823 PEPFUNC
LdrPEStartup (PVOID ImageBase
,
824 HANDLE SectionHandle
)
828 PIMAGE_DOS_HEADER DosHeader
;
829 PIMAGE_NT_HEADERS NTHeaders
;
833 * Overlay DOS and WNT headers structures
834 * to the DLL's image.
836 DosHeader
= (PIMAGE_DOS_HEADER
) ImageBase
;
837 NTHeaders
= (PIMAGE_NT_HEADERS
) (ImageBase
+ DosHeader
->e_lfanew
);
840 * Initialize image sections.
842 LdrMapSections(NtCurrentProcess(),
848 * If the base address is different from the
849 * one the DLL is actually loaded, perform any
852 if (ImageBase
!= (PVOID
) NTHeaders
->OptionalHeader
.ImageBase
)
854 Status
= LdrPerformRelocations(NTHeaders
, ImageBase
);
855 if (!NT_SUCCESS(Status
))
857 dprintf("LdrPerformRelocations() failed\n");
863 * If the DLL's imports symbols from other
864 * modules, fixup the imported calls entry points.
866 if (NTHeaders
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
]
867 .VirtualAddress
!= 0)
869 DPRINT("About to fixup imports\n");
870 Status
= LdrFixupImports(NTHeaders
, ImageBase
);
871 if (!NT_SUCCESS(Status
))
873 dprintf("LdrFixupImports() failed\n");
879 * Compute the DLL's entry point's address.
881 EntryPoint
= (PEPFUNC
) (ImageBase
882 + NTHeaders
->OptionalHeader
.AddressOfEntryPoint
);
883 DPRINT("LdrPEStartup() = %x\n",EntryPoint
);
887 NTSTATUS
LdrUnloadDll(PDLL Dll
)
889 PDLLMAIN_FUNC Entrypoint
;
892 if ( Dll
== NULL
|| Dll
== &LdrDllListHead
)
896 if ( Dll
->ReferenceCount
> 1 )
898 Dll
->ReferenceCount
--;
899 return STATUS_SUCCESS
;
902 if (( Dll
->Headers
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
) == IMAGE_FILE_DLL
) {
904 Entrypoint
= (PDLLMAIN_FUNC
) LdrPEStartup(Dll
->BaseAddress
,
906 if (Entrypoint
!= NULL
)
908 DPRINT("Calling entry point at 0x%08x\n", Entrypoint
);
909 if (FALSE
== Entrypoint(Dll
,
913 DPRINT("NTDLL.LDR: DLL failed to detach\n");
918 DPRINT("NTDLL.LDR: DLL detached successfully\n");
923 DPRINT("NTDLL.LDR: Entrypoint is NULL for \n");
927 Status
= ZwUnmapViewOfSection(NtCurrentProcess(),
930 ZwClose(Dll
->SectionHandle
);
935 static IMAGE_RESOURCE_DIRECTORY_ENTRY
* LdrGetNextEntry(IMAGE_RESOURCE_DIRECTORY
*ResourceDir
, LPCWSTR ResourceName
, ULONG Offset
)
939 WORD NumberOfNamedEntries
;
940 WORD NumberOfIdEntries
;
944 if ( (((ULONG
)ResourceDir
) & 0xF0000000) != 0 ) {
945 return (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)NULL
;
949 NumberOfIdEntries
= ResourceDir
->NumberOfIdEntries
;
950 NumberOfNamedEntries
= ResourceDir
->NumberOfNamedEntries
;
951 if ( ( NumberOfIdEntries
+ NumberOfNamedEntries
) == 0) {
952 return &ResourceDir
->DirectoryEntries
[0];
955 if ( HIWORD(ResourceName
) != 0 ) {
956 Length
= wcslen(ResourceName
);
957 Entries
= ResourceDir
->NumberOfNamedEntries
;
959 IMAGE_RESOURCE_DIR_STRING_U
*DirString
;
962 DirString
= (IMAGE_RESOURCE_DIR_STRING_U
*)(((ULONG
)ResourceDir
->DirectoryEntries
[Entries
].Name
& (~0xF0000000)) + Offset
);
964 if ( DirString
->Length
== Length
&& wcscmp(DirString
->NameString
, ResourceName
) == 0 ) {
965 return &ResourceDir
->DirectoryEntries
[Entries
];
967 } while (Entries
> 0);
970 Entries
= ResourceDir
->NumberOfIdEntries
+ ResourceDir
->NumberOfNamedEntries
;
974 if ( (LPWSTR
)ResourceDir
->DirectoryEntries
[Entries
].Name
== ResourceName
) {
975 return &ResourceDir
->DirectoryEntries
[Entries
];
977 } while (Entries
> ResourceDir
->NumberOfNamedEntries
);
989 NTSTATUS
LdrFindResource_U(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
**ResourceDataEntry
,LPCWSTR ResourceName
, ULONG ResourceType
,ULONG Language
)
991 IMAGE_RESOURCE_DIRECTORY
*ResourceTypeDir
;
992 IMAGE_RESOURCE_DIRECTORY
*ResourceNameDir
;
993 IMAGE_RESOURCE_DIRECTORY
*ResourceLangDir
;
995 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceTypeDirEntry
;
996 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceNameDirEntry
;
997 IMAGE_RESOURCE_DIRECTORY_ENTRY
*ResourceLangDirEntry
;
999 PIMAGE_OPTIONAL_HEADER OptionalHeader
;
1005 OptionalHeader
= & Dll
->Headers
->OptionalHeader
;
1006 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1007 OptionalHeader
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
;
1008 ResourceTypeDir
= (PIMAGE_RESOURCE_DIRECTORY
)
1009 ((ULONG
)ResourceTypeDir
+ (ULONG
)Dll
->BaseAddress
);
1012 Offset
= (ULONG
)ResourceTypeDir
;
1014 ResourceTypeDirEntry
= LdrGetNextEntry(ResourceTypeDir
, (LPWSTR
)ResourceType
, Offset
);
1016 if ( ResourceTypeDirEntry
!= NULL
) {
1017 ResourceNameDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceTypeDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1019 ResourceNameDirEntry
= LdrGetNextEntry(ResourceNameDir
, ResourceName
, Offset
);
1021 if ( ResourceNameDirEntry
!= NULL
) {
1023 ResourceLangDir
= (IMAGE_RESOURCE_DIRECTORY
*)((ResourceNameDirEntry
->OffsetToData
& (~0xF0000000)) + Offset
);
1025 ResourceLangDirEntry
= LdrGetNextEntry(ResourceLangDir
, (LPWSTR
)Language
, Offset
);
1026 if ( ResourceLangDirEntry
!= NULL
) {
1028 *ResourceDataEntry
= (IMAGE_RESOURCE_DATA_ENTRY
*)(ResourceLangDirEntry
->OffsetToData
+
1029 (ULONG
)ResourceTypeDir
);
1030 return STATUS_SUCCESS
;
1046 NTSTATUS
LdrAccessResource(DLL
*Dll
, IMAGE_RESOURCE_DATA_ENTRY
*ResourceDataEntry
, void **Data
)
1048 PIMAGE_SECTION_HEADER Sections
;
1054 if ( ResourceDataEntry
== NULL
)
1060 Sections
= (PIMAGE_SECTION_HEADER
) SECHDROFFSET(Dll
->BaseAddress
);
1063 (i
< Dll
->Headers
->FileHeader
.NumberOfSections
);
1067 if (Sections
[i
].VirtualAddress
<= ResourceDataEntry
->OffsetToData
1068 && Sections
[i
].VirtualAddress
+ Sections
[i
].Misc
.VirtualSize
> ResourceDataEntry
->OffsetToData
)
1072 if ( i
== Dll
->Headers
->FileHeader
.NumberOfSections
) {
1077 *Data
= (void *)(((ULONG
)Dll
->BaseAddress
+ ResourceDataEntry
->OffsetToData
- (ULONG
)Sections
[i
].VirtualAddress
) +
1078 (ULONG
)Sections
[i
].PointerToRawData
);
1081 return STATUS_SUCCESS
;