some __stdcall fixes in ntoskrnl and ntdll
[reactos.git] / reactos / lib / ntdll / ldr / utils.c
1 /* $Id: utils.c,v 1.10 1999/08/29 06:59:04 ea 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 #define WIN32_NO_PEHDR
15 #include <windows.h>
16 #include <ddk/ntddk.h>
17 #include <pe.h>
18 #include <string.h>
19 #include <internal/string.h>
20 #include <wchar.h>
21 #include <ntdll/ldr.h>
22
23 #ifdef DBG_NTDLL_LDR_UTILS
24 #define NDEBUG
25 #endif
26 #include <ntdll/ntdll.h>
27
28 /* FUNCTIONS *****************************************************************/
29
30
31 /* Type for a DLL's entry point */
32 typedef
33 WINBOOL
34 STDCALL
35 (* PDLLMAIN_FUNC) (
36 HANDLE hInst,
37 ULONG ul_reason_for_call,
38 LPVOID lpReserved
39 );
40
41
42 /**********************************************************************
43 * NAME
44 * LdrLoadDll
45 *
46 * DESCRIPTION
47 *
48 * ARGUMENTS
49 *
50 * RETURN VALUE
51 *
52 * REVISIONS
53 *
54 * NOTE
55 *
56 */
57 static
58 NTSTATUS
59 LdrLoadDll (
60 PDLL * Dll,
61 PCHAR Name
62 )
63 {
64 char fqname [255] = "\\??\\C:\\reactos\\system32\\";
65 ANSI_STRING AnsiString;
66 UNICODE_STRING UnicodeString;
67 OBJECT_ATTRIBUTES FileObjectAttributes;
68 char BlockBuffer [1024];
69 PIMAGE_DOS_HEADER DosHeader;
70 NTSTATUS Status;
71 PIMAGE_NT_HEADERS NTHeaders;
72 PEPFUNC DllStartupAddr;
73 ULONG ImageSize;
74 ULONG InitialViewSize;
75 PVOID ImageBase;
76 HANDLE FileHandle;
77 HANDLE SectionHandle;
78 PDLLMAIN_FUNC Entrypoint;
79
80
81 DPRINT("LdrLoadDll(Base %x, Name \"%s\")\n", Dll, Name);
82
83 /*
84 * Build the DLL's absolute name
85 */
86 strcat(fqname, Name);
87
88 DPRINT("fqname \"%s\"\n", fqname);
89 /*
90 * Open the DLL's image file.
91 */
92 RtlInitAnsiString(
93 & AnsiString,
94 fqname
95 );
96 RtlAnsiStringToUnicodeString(
97 & UnicodeString,
98 & AnsiString,
99 TRUE
100 );
101
102 InitializeObjectAttributes(
103 & FileObjectAttributes,
104 & UnicodeString,
105 0,
106 NULL,
107 NULL
108 );
109
110 DPRINT("Opening dll \"%s\"\n", fqname);
111
112 Status = ZwOpenFile(
113 & FileHandle,
114 FILE_ALL_ACCESS,
115 & FileObjectAttributes,
116 NULL,
117 0,
118 0
119 );
120 if (!NT_SUCCESS(Status))
121 {
122 DPRINT("Dll open failed: Status = 0x%08x\n", Status);
123 return Status;
124 }
125 Status = ZwReadFile(
126 FileHandle,
127 0,
128 0,
129 0,
130 0,
131 BlockBuffer,
132 sizeof BlockBuffer,
133 0,
134 0
135 );
136 if (!NT_SUCCESS(Status))
137 {
138 DPRINT("Dll header read failed: Status = 0x%08x\n", Status);
139 ZwClose(FileHandle);
140 return Status;
141 }
142 /*
143 * Overlay DOS and NT headers structures to the
144 * buffer with DLL's header raw data.
145 */
146 DosHeader = (PIMAGE_DOS_HEADER) BlockBuffer;
147 NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew);
148 /*
149 * Check it is a PE image file.
150 */
151 if ( (DosHeader->e_magic != IMAGE_DOS_MAGIC)
152 || (DosHeader->e_lfanew == 0L)
153 // || (*(PULONG)((PUCHAR)BlockBuffer + DosHeader->e_lfanew) != IMAGE_PE_MAGIC)
154 || (*(PULONG)(NTHeaders) != IMAGE_PE_MAGIC)
155 )
156 {
157 DPRINT("NTDLL format invalid\n");
158 ZwClose(FileHandle);
159
160 return STATUS_UNSUCCESSFUL;
161 }
162
163 // NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew);
164 ImageBase = (PVOID) NTHeaders->OptionalHeader.ImageBase;
165 ImageSize = NTHeaders->OptionalHeader.SizeOfImage;
166
167 DPRINT("ImageBase 0x%08x\n", ImageBase);
168
169 DllStartupAddr =
170 (PEPFUNC) (
171 ImageBase
172 + NTHeaders->OptionalHeader.AddressOfEntryPoint
173 );
174 /*
175 * Create a section for NTDLL.
176 */
177 Status = ZwCreateSection(
178 & SectionHandle,
179 SECTION_ALL_ACCESS,
180 NULL,
181 NULL,
182 PAGE_READWRITE,
183 MEM_COMMIT,
184 FileHandle
185 );
186 if (!NT_SUCCESS(Status))
187 {
188 DPRINT("NTDLL create section failed: Status = 0x%08x\n", Status);
189 ZwClose(FileHandle);
190 return Status;
191 }
192 /*
193 * Map the NTDLL into the process.
194 */
195 InitialViewSize =
196 DosHeader->e_lfanew
197 + sizeof (IMAGE_NT_HEADERS)
198 + sizeof (IMAGE_SECTION_HEADER) * NTHeaders->FileHeader.NumberOfSections;
199 Status = ZwMapViewOfSection(
200 SectionHandle,
201 NtCurrentProcess(),
202 (PVOID *) & ImageBase,
203 0,
204 InitialViewSize,
205 NULL,
206 & InitialViewSize,
207 0,
208 MEM_COMMIT,
209 PAGE_READWRITE
210 );
211 if (!NT_SUCCESS(Status))
212 {
213 DPRINT("NTDLL.LDR: map view of section failed ");
214 ZwClose(FileHandle);
215
216 return Status;
217 }
218 ZwClose(FileHandle);
219
220 (*Dll) = RtlAllocateHeap(
221 RtlGetProcessHeap(),
222 0,
223 sizeof (DLL)
224 );
225 (*Dll)->Headers = NTHeaders;
226 (*Dll)->BaseAddress = (PVOID)ImageBase;
227 (*Dll)->Next = LdrDllListHead.Next;
228 (*Dll)->Prev = & LdrDllListHead;
229 LdrDllListHead.Next->Prev = (*Dll);
230 LdrDllListHead.Next = (*Dll);
231
232 Entrypoint =
233 (PDLLMAIN_FUNC) LdrPEStartup(
234 ImageBase,
235 SectionHandle
236 );
237 if (Entrypoint != NULL)
238 {
239 DPRINT("Calling entry point at 0x%08x\n", Entrypoint);
240 if (FALSE == Entrypoint(
241 ImageBase,
242 DLL_PROCESS_ATTACH,
243 NULL
244 ))
245 {
246 DPRINT("NTDLL.LDR: DLL \"%s\" failed to initialize\n", fqname);
247 /* FIXME: should clean up and fail */
248 }
249 else
250 {
251 DPRINT("NTDLL.LDR: DLL \"%s\" initialized successfully\n", fqname);
252 }
253 }
254 else
255 {
256 DPRINT("NTDLL.LDR: Entrypoint is NULL for \"%s\"\n", fqname);
257 }
258
259 return STATUS_SUCCESS;
260 }
261
262
263 /**********************************************************************
264 * NAME LOCAL
265 * LdrFindDll
266 *
267 * DESCRIPTION
268 *
269 * ARGUMENTS
270 *
271 * RETURN VALUE
272 *
273 * REVISIONS
274 *
275 * NOTE
276 *
277 */
278 static
279 NTSTATUS
280 LdrFindDll (
281 PDLL * Dll,
282 PCHAR Name
283 )
284 {
285 PIMAGE_EXPORT_DIRECTORY ExportDir;
286 DLL * current;
287 PIMAGE_OPTIONAL_HEADER OptionalHeader;
288
289
290 DPRINT("NTDLL.LdrFindDll(Name %s)\n", Name);
291
292 current = & LdrDllListHead;
293 do
294 {
295 OptionalHeader = & current->Headers->OptionalHeader;
296 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
297 OptionalHeader->DataDirectory[
298 IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
299 ExportDir = (PIMAGE_EXPORT_DIRECTORY)
300 ((ULONG)ExportDir + (ULONG)current->BaseAddress);
301
302 DPRINT("Scanning %x %x %x\n",
303 ExportDir->Name,
304 current->BaseAddress,
305 (ExportDir->Name + current->BaseAddress)
306 );
307 DPRINT("Scanning %s\n",
308 ExportDir->Name + current->BaseAddress
309 );
310 if (strcmp(ExportDir->Name + current->BaseAddress, Name) == 0)
311 {
312 *Dll = current;
313 return STATUS_SUCCESS;
314 }
315
316 current = current->Next;
317
318 } while (current != & LdrDllListHead);
319
320 dprintf("Failed to find dll %s\n",Name);
321
322 return (LdrLoadDll(Dll, Name));
323 }
324
325
326 /**********************************************************************
327 * NAME
328 * LdrMapSections
329 *
330 * DESCRIPTION
331 *
332 * ARGUMENTS
333 *
334 * RETURN VALUE
335 *
336 * REVISIONS
337 *
338 * NOTE
339 *
340 */
341 NTSTATUS
342 LdrMapSections (
343 HANDLE ProcessHandle,
344 PVOID ImageBase,
345 HANDLE SectionHandle,
346 PIMAGE_NT_HEADERS NTHeaders
347 )
348 {
349 ULONG i;
350 NTSTATUS Status;
351
352
353 for ( i = 0;
354 (i < NTHeaders->FileHeader.NumberOfSections);
355 i++
356 )
357 {
358 PIMAGE_SECTION_HEADER Sections;
359 LARGE_INTEGER Offset;
360 ULONG Base;
361
362 Sections = (PIMAGE_SECTION_HEADER) SECHDROFFSET(ImageBase);
363 Base = (ULONG) (Sections[i].VirtualAddress + ImageBase);
364 Offset.u.LowPart = Sections[i].PointerToRawData;
365 Offset.u.HighPart = 0;
366 Status = ZwMapViewOfSection(
367 SectionHandle,
368 ProcessHandle,
369 (PVOID *) & Base,
370 0,
371 Sections[i].Misc.VirtualSize,
372 & Offset,
373 (PULONG) & Sections[i].Misc.VirtualSize,
374 0,
375 MEM_COMMIT,
376 PAGE_READWRITE
377 );
378 if (!NT_SUCCESS(Status))
379 {
380 return Status;
381 }
382 }
383 return STATUS_SUCCESS;
384 }
385
386
387 /**********************************************************************
388 * NAME LOCAL
389 * LdrGetExportByOrdinal
390 *
391 * DESCRIPTION
392 *
393 * ARGUMENTS
394 *
395 * RETURN VALUE
396 *
397 * REVISIONS
398 *
399 * NOTE
400 *
401 */
402 static
403 PVOID
404 LdrGetExportByOrdinal (
405 PDLL Module,
406 ULONG Ordinal
407 )
408 {
409 PIMAGE_EXPORT_DIRECTORY ExportDir;
410 PDWORD * ExFunctions;
411 USHORT * ExOrdinals;
412
413 ExportDir = (
414 Module->BaseAddress
415 + (Module->Headers->OptionalHeader
416 .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
417 .VirtualAddress
418 )
419 );
420
421 ExOrdinals = (USHORT *)
422 RVA(
423 Module->BaseAddress,
424 ExportDir->AddressOfNameOrdinals
425 );
426 ExFunctions = (PDWORD *)
427 RVA(
428 Module->BaseAddress,
429 ExportDir->AddressOfFunctions
430 );
431 dprintf(
432 "LdrGetExportByOrdinal(Ordinal %d) = %x\n",
433 Ordinal,
434 ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]]
435 );
436 return(ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]]);
437 }
438
439
440 /**********************************************************************
441 * NAME LOCAL
442 * LdrGetExportByName
443 *
444 * DESCRIPTION
445 *
446 * ARGUMENTS
447 *
448 * RETURN VALUE
449 *
450 * REVISIONS
451 *
452 * NOTE
453 *
454 */
455 static
456 PVOID
457 LdrGetExportByName (
458 PDLL Module,
459 PUCHAR SymbolName
460 )
461 {
462 PIMAGE_EXPORT_DIRECTORY ExportDir;
463 PDWORD * ExFunctions;
464 PDWORD * ExNames;
465 USHORT * ExOrdinals;
466 ULONG i;
467 PVOID ExName;
468 ULONG Ordinal;
469
470 DPRINT(
471 "LdrFindExport(Module %x, SymbolName %s)\n",
472 Module,
473 SymbolName
474 );
475
476 ExportDir = (
477 Module->BaseAddress
478 + (Module->Headers->OptionalHeader
479 .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
480 .VirtualAddress
481 )
482 );
483 /*
484 * Get header pointers
485 */
486 ExNames = (PDWORD *)
487 RVA(
488 Module->BaseAddress,
489 ExportDir->AddressOfNames
490 );
491 ExOrdinals = (USHORT *)
492 RVA(
493 Module->BaseAddress,
494 ExportDir->AddressOfNameOrdinals
495 );
496 ExFunctions = (PDWORD *)
497 RVA(
498 Module->BaseAddress,
499 ExportDir->AddressOfFunctions
500 );
501 for ( i = 0;
502 ( i < ExportDir->NumberOfFunctions);
503 i++
504 )
505 {
506 ExName = RVA(
507 Module->BaseAddress,
508 ExNames[i]
509 );
510 DPRINT(
511 "Comparing '%s' '%s'\n",
512 ExName,
513 SymbolName
514 );
515 if (strcmp(ExName,SymbolName) == 0)
516 {
517 Ordinal = ExOrdinals[i];
518 return(RVA(Module->BaseAddress, ExFunctions[Ordinal]));
519 }
520 }
521
522 dprintf("LdrGetExportByName() = failed to find %s\n",SymbolName);
523
524 return NULL;
525 }
526
527
528 /**********************************************************************
529 * NAME LOCAL
530 * LdrPerformRelocations
531 *
532 * DESCRIPTION
533 * Relocate a DLL's memory image.
534 *
535 * ARGUMENTS
536 *
537 * RETURN VALUE
538 *
539 * REVISIONS
540 *
541 * NOTE
542 *
543 */
544 static
545 NTSTATUS
546 LdrPerformRelocations (
547 PIMAGE_NT_HEADERS NTHeaders,
548 PVOID ImageBase
549 )
550 {
551 USHORT NumberOfEntries;
552 PUSHORT pValue16;
553 ULONG RelocationRVA;
554 ULONG Delta32;
555 ULONG Offset;
556 PULONG pValue32;
557 PRELOCATION_DIRECTORY RelocationDir;
558 PRELOCATION_ENTRY RelocationBlock;
559 int i;
560
561
562 RelocationRVA =
563 NTHeaders->OptionalHeader
564 .DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
565 .VirtualAddress;
566 if (RelocationRVA)
567 {
568 RelocationDir = (PRELOCATION_DIRECTORY)
569 ((PCHAR)ImageBase + RelocationRVA);
570
571 while (RelocationDir->SizeOfBlock)
572 {
573 Delta32 = (unsigned long) (
574 ImageBase
575 - NTHeaders->OptionalHeader.ImageBase
576 );
577 RelocationBlock = (PRELOCATION_ENTRY) (
578 RelocationRVA
579 + ImageBase
580 + sizeof (RELOCATION_DIRECTORY)
581 );
582 NumberOfEntries = (
583 RelocationDir->SizeOfBlock
584 - sizeof (RELOCATION_DIRECTORY)
585 )
586 / sizeof (RELOCATION_ENTRY);
587
588 for ( i = 0;
589 (i < NumberOfEntries);
590 i++
591 )
592 {
593 Offset = (
594 RelocationBlock[i].TypeOffset
595 & 0xfff
596 )
597 + RelocationDir->VirtualAddress;
598 /*
599 * What kind of relocations should we perform
600 * for the current entry?
601 */
602 switch (RelocationBlock[i].TypeOffset >> 12)
603 {
604 case TYPE_RELOC_ABSOLUTE:
605 break;
606
607 case TYPE_RELOC_HIGH:
608 pValue16 = (PUSHORT) (ImageBase + Offset);
609 *pValue16 += Delta32 >> 16;
610 break;
611
612 case TYPE_RELOC_LOW:
613 pValue16 = (PUSHORT)(ImageBase + Offset);
614 *pValue16 += Delta32 & 0xffff;
615 break;
616
617 case TYPE_RELOC_HIGHLOW:
618 pValue32 = (PULONG) (ImageBase + Offset);
619 *pValue32 += Delta32;
620 break;
621
622 case TYPE_RELOC_HIGHADJ:
623 /* FIXME: do the highadjust fixup */
624 DPRINT(
625 "TYPE_RELOC_HIGHADJ fixup not implemented"
626 ", sorry\n"
627 );
628 return(STATUS_UNSUCCESSFUL);
629
630 default:
631 DPRINT("unexpected fixup type\n");
632 return STATUS_UNSUCCESSFUL;
633 }
634 }
635 RelocationRVA += RelocationDir->SizeOfBlock;
636 RelocationDir = (PRELOCATION_DIRECTORY) (
637 ImageBase
638 + RelocationRVA
639 );
640 }
641 }
642 return STATUS_SUCCESS;
643 }
644
645
646 /**********************************************************************
647 * NAME LOCAL
648 * LdrFixupImports
649 *
650 * DESCRIPTION
651 * Compute the entry point for every symbol the DLL imports
652 * from other modules.
653 *
654 * ARGUMENTS
655 *
656 * RETURN VALUE
657 *
658 * REVISIONS
659 *
660 * NOTE
661 *
662 */
663 static
664 NTSTATUS
665 LdrFixupImports (
666 PIMAGE_NT_HEADERS NTHeaders,
667 PVOID ImageBase
668 )
669 {
670 PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory;
671 ULONG Ordinal;
672 PDLL Module;
673 NTSTATUS Status;
674
675
676 /*
677 * Process each import module.
678 */
679 ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY) (
680 ImageBase
681 + NTHeaders->OptionalHeader
682 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
683 .VirtualAddress
684 );
685 while (ImportModuleDirectory->dwRVAModuleName)
686 {
687 PVOID * ImportAddressList;
688 PULONG FunctionNameList;
689 DWORD pName;
690 PWORD pHint;
691
692 Status = LdrFindDll(
693 & Module,
694 (PCHAR) (
695 ImageBase
696 + ImportModuleDirectory->dwRVAModuleName
697 )
698 );
699 if (!NT_SUCCESS(Status))
700 {
701 return Status;
702 }
703 /*
704 * Get the import address list.
705 */
706 ImportAddressList = (PVOID *) (
707 NTHeaders->OptionalHeader.ImageBase
708 + ImportModuleDirectory->dwRVAFunctionAddressList
709 );
710 /*
711 * Get the list of functions to import.
712 */
713 if (ImportModuleDirectory->dwRVAFunctionNameList != 0)
714 {
715 FunctionNameList = (PULONG) (
716 ImageBase
717 + ImportModuleDirectory->dwRVAFunctionNameList
718 );
719 }
720 else
721 {
722 FunctionNameList = (PULONG) (
723 ImageBase
724 + ImportModuleDirectory->dwRVAFunctionAddressList
725 );
726 }
727 /*
728 * Walk through function list and fixup addresses.
729 */
730 while (*FunctionNameList != 0L)
731 {
732 if ((*FunctionNameList) & 0x80000000)
733 {
734 Ordinal = (*FunctionNameList) & 0x7fffffff;
735 *ImportAddressList =
736 LdrGetExportByOrdinal(
737 Module,
738 Ordinal
739 );
740 }
741 else
742 {
743 pName = (DWORD) (
744 ImageBase
745 + *FunctionNameList
746 + 2
747 );
748 pHint = (PWORD) (
749 ImageBase
750 + *FunctionNameList
751 );
752
753 *ImportAddressList =
754 LdrGetExportByName(
755 Module,
756 (PUCHAR) pName
757 );
758 if ((*ImportAddressList) == NULL)
759 {
760 return STATUS_UNSUCCESSFUL;
761 }
762 }
763 ImportAddressList++;
764 FunctionNameList++;
765 }
766 ImportModuleDirectory++;
767 }
768 return STATUS_SUCCESS;
769 }
770
771
772 /**********************************************************************
773 * NAME
774 * LdrPEStartup
775 *
776 * DESCRIPTION
777 * 1. Map the DLL's sections into memory.
778 * 2. Relocate, if needed the DLL.
779 * 3. Fixup any imported symbol.
780 * 4. Compute the DLL's entry point.
781 *
782 * ARGUMENTS
783 * ImageBase
784 * Address at which the DLL's image
785 * is loaded.
786 *
787 * SectionHandle
788 * Handle of the section that contains
789 * the DLL's image.
790 *
791 * RETURN VALUE
792 * NULL on error; otherwise the entry point
793 * to call for initializing the DLL.
794 *
795 * REVISIONS
796 *
797 * NOTE
798 *
799 */
800 PEPFUNC
801 LdrPEStartup (
802 PVOID ImageBase,
803 HANDLE SectionHandle
804 )
805 {
806 NTSTATUS Status;
807 PEPFUNC EntryPoint;
808 PIMAGE_DOS_HEADER DosHeader;
809 PIMAGE_NT_HEADERS NTHeaders;
810
811
812 /*
813 * Overlay DOS and WNT headers structures
814 * to the DLL's image.
815 */
816 DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
817 NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew);
818 /*
819 * Initialize image sections.
820 */
821 LdrMapSections(
822 NtCurrentProcess(),
823 ImageBase,
824 SectionHandle,
825 NTHeaders
826 );
827 /*
828 * If the base address is different from the
829 * one the DLL is actually loaded, perform any
830 * relocation.
831 */
832 if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
833 {
834 Status = LdrPerformRelocations(
835 NTHeaders,
836 ImageBase
837 );
838 if (!NT_SUCCESS(Status))
839 {
840 dprintf("LdrPerformRelocations() failed\n");
841 return NULL;
842 }
843 }
844 /*
845 * If the DLL's imports symbols from other
846 * modules, fixup the imported calls entry points.
847 */
848 if (NTHeaders->OptionalHeader
849 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
850 .VirtualAddress != 0)
851 {
852 Status = LdrFixupImports(
853 NTHeaders,
854 ImageBase
855 );
856 if (!NT_SUCCESS(Status))
857 {
858 dprintf("LdrFixupImports() failed\n");
859 return NULL;
860 }
861 }
862 /*
863 * Compute the DLL's entry point's address.
864 */
865 EntryPoint = (PEPFUNC) (
866 ImageBase
867 + NTHeaders->OptionalHeader.AddressOfEntryPoint
868 );
869 dprintf("LdrPEStartup() = %x\n",EntryPoint);
870 return EntryPoint;
871 }
872
873
874
875 /* EOF */