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