3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Provides routines for loading PE files.
5 * (Deprecated remark) To be merged with arch/i386/loader.c in future.
7 * COPYRIGHT: Copyright 1998-2003 Brian Palmer <brianp@sginet.com>
8 * Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org>
10 * NOTES: The source code in this file is based on the work of respective
11 * authors of PE loading code in ReactOS and Brian Palmer and
12 * Alex Ionescu's arch/i386/loader.c, and my research project
13 * (creating a native EFI loader for Windows).
15 * This article was very handy during development:
16 * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/
19 /* INCLUDES ******************************************************************/
24 DBG_DEFAULT_CHANNEL(PELOADER
);
26 /* PRIVATE FUNCTIONS *********************************************************/
28 /* DllName - physical, UnicodeString->Buffer - virtual */
32 IN PUNICODE_STRING UnicodeName
)
37 /* First obvious check: for length of two names */
38 Length
= strlen(DllName
);
42 UNICODE_STRING UnicodeNamePA
;
43 UnicodeNamePA
.Length
= UnicodeName
->Length
;
44 UnicodeNamePA
.MaximumLength
= UnicodeName
->MaximumLength
;
45 UnicodeNamePA
.Buffer
= VaToPa(UnicodeName
->Buffer
);
46 TRACE("PeLdrpCompareDllName: %s and %wZ, Length = %d "
47 "UN->Length %d\n", DllName
, &UnicodeNamePA
, Length
, UnicodeName
->Length
);
51 if ((Length
* sizeof(WCHAR
)) > UnicodeName
->Length
)
54 /* Store pointer to unicode string's buffer */
55 Buffer
= VaToPa(UnicodeName
->Buffer
);
57 /* Loop character by character */
58 for (i
= 0; i
< Length
; i
++)
60 /* Compare two characters, uppercasing them */
61 if (toupper(*DllName
) != toupper((CHAR
)*Buffer
))
64 /* Move to the next character */
69 /* Check, if strings either fully match, or match till the "." (w/o extension) */
70 if ((UnicodeName
->Length
== Length
* sizeof(WCHAR
)) || (*Buffer
== L
'.'))
76 /* Strings don't match, return FALSE */
81 PeLdrpLoadAndScanReferencedDll(
82 IN OUT PLIST_ENTRY ModuleListHead
,
83 IN PCCH DirectoryPath
,
85 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
);
89 IN OUT PLIST_ENTRY ModuleListHead
,
92 IN PIMAGE_THUNK_DATA ThunkData
,
93 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory
,
95 IN BOOLEAN ProcessForwards
,
96 IN PCSTR DirectoryPath
)
99 PULONG NameTable
, FunctionTable
;
100 PUSHORT OrdinalTable
;
101 LONG High
, Low
, Middle
, Result
;
103 PIMAGE_IMPORT_BY_NAME ImportData
;
104 PCHAR ExportName
, ForwarderName
;
107 //TRACE("PeLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n",
108 // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards);
110 /* Check passed DllBase param */
113 WARN("DllBase == NULL!\n");
117 /* Convert all non-critical pointers to PA from VA */
118 ThunkData
= VaToPa(ThunkData
);
120 /* Is the reference by ordinal? */
121 if (IMAGE_SNAP_BY_ORDINAL(ThunkData
->u1
.Ordinal
) && !ProcessForwards
)
123 /* Yes, calculate the ordinal */
124 Ordinal
= (ULONG
)(IMAGE_ORDINAL(ThunkData
->u1
.Ordinal
) - (UINT32
)ExportDirectory
->Base
);
125 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
129 /* It's reference by name, we have to look it up in the export directory */
130 if (!ProcessForwards
)
132 /* AddressOfData in thunk entry will become a virtual address (from relative) */
133 //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData);
134 ThunkData
->u1
.AddressOfData
=
135 (ULONG_PTR
)RVA(ImageBase
, ThunkData
->u1
.AddressOfData
);
136 //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData);
139 /* Get the import name */
140 ImportData
= VaToPa((PVOID
)ThunkData
->u1
.AddressOfData
);
142 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
143 NameTable
= VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfNames
));
144 OrdinalTable
= VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfNameOrdinals
));
146 //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
147 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
149 /* Get the hint, convert it to a physical pointer */
150 Hint
= ((PIMAGE_IMPORT_BY_NAME
)VaToPa((PVOID
)ThunkData
->u1
.AddressOfData
))->Hint
;
151 //TRACE("HintIndex %d\n", Hint);
153 /* Get the export name from the hint */
154 ExportName
= VaToPa(RVA(DllBase
, NameTable
[Hint
]));
156 /* If Hint is less than total number of entries in the export directory,
157 and import name == export name, then we can just get it from the OrdinalTable */
158 if ((Hint
< ExportDirectory
->NumberOfNames
) &&
159 (strcmp(ExportName
, (PCHAR
)ImportData
->Name
) == 0))
161 Ordinal
= OrdinalTable
[Hint
];
162 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
166 /* It's not the easy way, we have to lookup import name in the name table.
167 Let's use a binary search for this task. */
169 //TRACE("PeLdrpBindImportName() looking up the import name using binary search...\n");
171 /* Low boundary is set to 0, and high boundary to the maximum index */
173 High
= ExportDirectory
->NumberOfNames
- 1;
175 /* Perform a binary-search loop */
178 /* Divide by 2 by shifting to the right once */
179 Middle
= (Low
+ High
) / 2;
181 /* Get the name from the name table */
182 ExportName
= VaToPa(RVA(DllBase
, NameTable
[Middle
]));
184 /* Compare the names */
185 Result
= strcmp(ExportName
, (PCHAR
)ImportData
->Name
);
187 // TRACE("Binary search: comparing Import '__', Export '%s'\n",
188 // VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),
189 // (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));
191 // TRACE("TE->u1.AOD %p, fulladdr %p\n",
192 // ThunkData->u1.AddressOfData,
193 // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );
195 /* Depending on result of strcmp, perform different actions */
198 /* Adjust top boundary */
203 /* Adjust bottom boundary */
213 /* If high boundary is less than low boundary, then no result found */
216 ERR("Did not find export '%s'!\n", (PCHAR
)ImportData
->Name
);
220 /* Everything alright, get the ordinal */
221 Ordinal
= OrdinalTable
[Middle
];
223 //TRACE("PeLdrpBindImportName() found Ordinal %d\n", Ordinal);
227 /* Check ordinal number for validity! */
228 if (Ordinal
>= ExportDirectory
->NumberOfFunctions
)
230 ERR("Ordinal number is invalid!\n");
234 /* Get a pointer to the function table */
235 FunctionTable
= (PULONG
)VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfFunctions
));
237 /* Save a pointer to the function */
238 ThunkData
->u1
.Function
= (ULONG_PTR
)RVA(DllBase
, FunctionTable
[Ordinal
]);
240 /* Is it a forwarder? (function pointer is within the export directory) */
241 ForwarderName
= (PCHAR
)VaToPa((PVOID
)ThunkData
->u1
.Function
);
242 if (((ULONG_PTR
)ForwarderName
> (ULONG_PTR
)ExportDirectory
) &&
243 ((ULONG_PTR
)ForwarderName
< ((ULONG_PTR
)ExportDirectory
+ ExportSize
)))
245 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
246 CHAR ForwardDllName
[255];
247 PIMAGE_EXPORT_DIRECTORY RefExportDirectory
;
250 TRACE("PeLdrpBindImportName(): ForwarderName %s\n", ForwarderName
);
252 /* Save the name of the forward dll */
253 RtlCopyMemory(ForwardDllName
, ForwarderName
, sizeof(ForwardDllName
));
255 /* Strip out the symbol name */
256 *strrchr(ForwardDllName
,'.') = '\0';
258 /* Check if the target image is already loaded */
259 if (!PeLdrCheckForLoadedDll(ModuleListHead
, ForwardDllName
, &DataTableEntry
))
261 /* Check if the forward dll name has an extension */
262 if (strchr(ForwardDllName
, '.') == NULL
)
264 /* Name does not have an extension, append '.dll' */
265 strcat(ForwardDllName
, ".dll");
268 /* Now let's try to load it! */
269 Success
= PeLdrpLoadAndScanReferencedDll(ModuleListHead
,
275 ERR("PeLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n");
280 /* Get pointer to the export directory of loaded DLL */
281 RefExportDirectory
= (PIMAGE_EXPORT_DIRECTORY
)
282 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry
->DllBase
),
284 IMAGE_DIRECTORY_ENTRY_EXPORT
,
287 /* Fail if it's NULL */
288 if (RefExportDirectory
)
291 IMAGE_THUNK_DATA RefThunkData
;
292 PIMAGE_IMPORT_BY_NAME ImportByName
;
295 /* Get pointer to the import name */
296 ImportName
= strrchr(ForwarderName
, '.') + 1;
298 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
299 ImportByName
= (PIMAGE_IMPORT_BY_NAME
)Buffer
;
301 /* Fill the name with the import name */
302 RtlCopyMemory(ImportByName
->Name
, ImportName
, strlen(ImportName
)+1);
305 ImportByName
->Hint
= 0;
307 /* And finally point ThunkData's AddressOfData to that structure */
308 RefThunkData
.u1
.AddressOfData
= (ULONG_PTR
)ImportByName
;
310 /* And recursively call ourselves */
311 Success
= PeLdrpBindImportName(ModuleListHead
,
312 DataTableEntry
->DllBase
,
320 /* Fill out the ThunkData with data from RefThunkData */
321 ThunkData
->u1
= RefThunkData
.u1
;
323 /* Return what we got from the recursive call */
328 /* Fail if ExportDirectory is NULL */
338 PeLdrpLoadAndScanReferencedDll(
339 IN OUT PLIST_ENTRY ModuleListHead
,
340 IN PCCH DirectoryPath
,
342 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
)
344 CHAR FullDllName
[256];
348 /* Prepare the full path to the file to be loaded */
349 strcpy(FullDllName
, DirectoryPath
);
350 strcat(FullDllName
, ImportName
);
352 TRACE("Loading referenced DLL: %s\n", FullDllName
);
355 Success
= PeLdrLoadImage(FullDllName
, LoaderBootDriver
, &BasePA
);
358 ERR("PeLdrLoadImage() failed\n");
362 /* Allocate DTE for newly loaded DLL */
363 Success
= PeLdrAllocateDataTableEntry(ModuleListHead
,
370 ERR("PeLdrAllocateDataTableEntry() failed\n");
374 (*DataTableEntry
)->Flags
|= LDRP_DRIVER_DEPENDENT_DLL
;
376 /* Scan its dependencies too */
377 TRACE("PeLdrScanImportDescriptorTable() calling ourselves for %S\n",
378 VaToPa((*DataTableEntry
)->BaseDllName
.Buffer
));
379 Success
= PeLdrScanImportDescriptorTable(ModuleListHead
, DirectoryPath
, *DataTableEntry
);
382 ERR("PeLdrScanImportDescriptorTable() failed\n");
390 PeLdrpScanImportAddressTable(
391 IN OUT PLIST_ENTRY ModuleListHead
,
394 IN PIMAGE_THUNK_DATA ThunkData
,
395 IN PCSTR DirectoryPath
)
397 PIMAGE_EXPORT_DIRECTORY ExportDirectory
= NULL
;
401 TRACE("PeLdrpScanImportAddressTable(): DllBase 0x%X, "
402 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase
, ImageBase
, ThunkData
);
404 /* Obtain the export table from the DLL's base */
407 ERR("Error, DllBase == NULL!\n");
413 (PIMAGE_EXPORT_DIRECTORY
)RtlImageDirectoryEntryToData(VaToPa(DllBase
),
415 IMAGE_DIRECTORY_ENTRY_EXPORT
,
419 TRACE("PeLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory
);
421 /* If pointer to Export Directory is */
422 if (ExportDirectory
== NULL
)
424 ERR("DllBase=%p(%p)\n", DllBase
, VaToPa(DllBase
));
428 /* Go through each entry in the thunk table and bind it */
429 while (((PIMAGE_THUNK_DATA
)VaToPa(ThunkData
))->u1
.AddressOfData
!= 0)
432 Success
= PeLdrpBindImportName(ModuleListHead
,
441 /* Move to the next entry */
444 /* Return error if binding was unsuccessful */
454 /* FUNCTIONS *****************************************************************/
456 /* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */
458 PeLdrCheckForLoadedDll(
459 IN OUT PLIST_ENTRY ModuleListHead
,
461 OUT PLDR_DATA_TABLE_ENTRY
*LoadedEntry
)
463 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
464 LIST_ENTRY
*ModuleEntry
;
466 TRACE("PeLdrCheckForLoadedDll: DllName %s\n", DllName
);
468 /* Just go through each entry in the LoadOrderList and compare loaded module's
469 name with a given name */
470 ModuleEntry
= ModuleListHead
->Flink
;
471 while (ModuleEntry
!= ModuleListHead
)
473 /* Get pointer to the current DTE */
474 DataTableEntry
= CONTAINING_RECORD(ModuleEntry
,
475 LDR_DATA_TABLE_ENTRY
,
478 TRACE("PeLdrCheckForLoadedDll: DTE %p, EP %p, base %p name '%.*ws'\n",
479 DataTableEntry
, DataTableEntry
->EntryPoint
, DataTableEntry
->DllBase
,
480 DataTableEntry
->BaseDllName
.Length
/ 2, VaToPa(DataTableEntry
->BaseDllName
.Buffer
));
483 if (PeLdrpCompareDllName(DllName
, &DataTableEntry
->BaseDllName
))
485 /* Yes, found it, report pointer to the loaded module's DTE
486 to the caller and increase load count for it */
487 *LoadedEntry
= DataTableEntry
;
488 DataTableEntry
->LoadCount
++;
489 TRACE("PeLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry
);
493 /* Go to the next entry */
494 ModuleEntry
= ModuleEntry
->Flink
;
502 PeLdrScanImportDescriptorTable(
503 IN OUT PLIST_ENTRY ModuleListHead
,
504 IN PCCH DirectoryPath
,
505 IN PLDR_DATA_TABLE_ENTRY ScanDTE
)
507 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
508 PIMAGE_IMPORT_DESCRIPTOR ImportTable
;
509 ULONG ImportTableSize
;
513 /* Get a pointer to the import table of this image */
514 ImportTable
= (PIMAGE_IMPORT_DESCRIPTOR
)RtlImageDirectoryEntryToData(VaToPa(ScanDTE
->DllBase
),
515 TRUE
, IMAGE_DIRECTORY_ENTRY_IMPORT
, &ImportTableSize
);
519 UNICODE_STRING BaseName
;
520 BaseName
.Buffer
= VaToPa(ScanDTE
->BaseDllName
.Buffer
);
521 BaseName
.MaximumLength
= ScanDTE
->BaseDllName
.MaximumLength
;
522 BaseName
.Length
= ScanDTE
->BaseDllName
.Length
;
523 TRACE("PeLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n",
524 &BaseName
, ImportTable
);
528 /* If image doesn't have any import directory - just return success */
529 if (ImportTable
== NULL
)
532 /* Loop through all entries */
533 for (;(ImportTable
->Name
!= 0) && (ImportTable
->FirstThunk
!= 0);ImportTable
++)
535 /* Get pointer to the name */
536 ImportName
= (PCH
)VaToPa(RVA(ScanDTE
->DllBase
, ImportTable
->Name
));
537 TRACE("PeLdrScanImportDescriptorTable(): Looking at %s\n", ImportName
);
539 /* In case we get a reference to ourselves - just skip it */
540 if (PeLdrpCompareDllName(ImportName
, &ScanDTE
->BaseDllName
))
543 /* Load the DLL if it is not already loaded */
544 if (!PeLdrCheckForLoadedDll(ModuleListHead
, ImportName
, &DataTableEntry
))
546 Success
= PeLdrpLoadAndScanReferencedDll(ModuleListHead
,
552 ERR("PeLdrpLoadAndScanReferencedDll() failed\n");
557 /* Scan its import address table */
558 Success
= PeLdrpScanImportAddressTable(ModuleListHead
,
559 DataTableEntry
->DllBase
,
561 (PIMAGE_THUNK_DATA
)RVA(ScanDTE
->DllBase
, ImportTable
->FirstThunk
),
566 ERR("PeLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n",
567 ImportName
, DirectoryPath
);
576 PeLdrAllocateDataTableEntry(
577 IN OUT PLIST_ENTRY ModuleListHead
,
581 OUT PLDR_DATA_TABLE_ENTRY
*NewEntry
)
583 PVOID BaseVA
= PaToVa(BasePA
);
585 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
586 PIMAGE_NT_HEADERS NtHeaders
;
589 TRACE("PeLdrAllocateDataTableEntry(, '%s', '%s', %p)\n",
590 BaseDllName
, FullDllName
, BasePA
);
592 /* Allocate memory for a data table entry, zero-initialize it */
593 DataTableEntry
= (PLDR_DATA_TABLE_ENTRY
)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY
),
595 if (DataTableEntry
== NULL
)
597 RtlZeroMemory(DataTableEntry
, sizeof(LDR_DATA_TABLE_ENTRY
));
599 /* Get NT headers from the image */
600 NtHeaders
= RtlImageNtHeader(BasePA
);
602 /* Initialize corresponding fields of DTE based on NT headers value */
603 DataTableEntry
->DllBase
= BaseVA
;
604 DataTableEntry
->SizeOfImage
= NtHeaders
->OptionalHeader
.SizeOfImage
;
605 DataTableEntry
->EntryPoint
= RVA(BaseVA
, NtHeaders
->OptionalHeader
.AddressOfEntryPoint
);
606 DataTableEntry
->SectionPointer
= 0;
607 DataTableEntry
->CheckSum
= NtHeaders
->OptionalHeader
.CheckSum
;
609 /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName
610 by simple conversion - copying each character */
611 Length
= (USHORT
)(strlen(BaseDllName
) * sizeof(WCHAR
));
612 Buffer
= (PWSTR
)FrLdrHeapAlloc(Length
, TAG_WLDR_NAME
);
615 FrLdrHeapFree(DataTableEntry
, TAG_WLDR_DTE
);
618 RtlZeroMemory(Buffer
, Length
);
620 DataTableEntry
->BaseDllName
.Length
= Length
;
621 DataTableEntry
->BaseDllName
.MaximumLength
= Length
;
622 DataTableEntry
->BaseDllName
.Buffer
= PaToVa(Buffer
);
623 while (*BaseDllName
!= 0)
625 *Buffer
++ = *BaseDllName
++;
628 /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName
629 using the same method */
630 Length
= (USHORT
)(strlen(FullDllName
) * sizeof(WCHAR
));
631 Buffer
= (PWSTR
)FrLdrHeapAlloc(Length
, TAG_WLDR_NAME
);
634 FrLdrHeapFree(DataTableEntry
, TAG_WLDR_DTE
);
637 RtlZeroMemory(Buffer
, Length
);
639 DataTableEntry
->FullDllName
.Length
= Length
;
640 DataTableEntry
->FullDllName
.MaximumLength
= Length
;
641 DataTableEntry
->FullDllName
.Buffer
= PaToVa(Buffer
);
642 while (*FullDllName
!= 0)
644 *Buffer
++ = *FullDllName
++;
647 /* Initialize what's left - LoadCount which is 1, and set Flags so that
648 we know this entry is processed */
649 DataTableEntry
->Flags
= LDRP_ENTRY_PROCESSED
;
650 DataTableEntry
->LoadCount
= 1;
652 /* Insert this DTE to a list in the LPB */
653 InsertTailList(ModuleListHead
, &DataTableEntry
->InLoadOrderLinks
);
654 TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry
,
655 DataTableEntry
->BaseDllName
.Length
/ 2,
656 VaToPa(DataTableEntry
->BaseDllName
.Buffer
),
657 DataTableEntry
->DllBase
);
659 /* Save pointer to a newly allocated and initialized entry */
660 *NewEntry
= DataTableEntry
;
667 * PeLdrLoadImage loads the specified image from the file (it doesn't
668 * perform any additional operations on the filename, just directly
669 * calls the file I/O routines), and relocates it so that it's ready
670 * to be used when paging is enabled.
671 * Addressing mode: physical
676 IN TYPE_OF_MEMORY MemoryType
,
677 OUT PVOID
*ImageBasePA
)
681 PVOID VirtualBase
= NULL
;
682 UCHAR HeadersBuffer
[SECTOR_SIZE
* 2];
683 PIMAGE_NT_HEADERS NtHeaders
;
684 PIMAGE_SECTION_HEADER SectionHeader
;
685 ULONG VirtualSize
, SizeOfRawData
, NumberOfSections
;
687 LARGE_INTEGER Position
;
690 TRACE("PeLdrLoadImage(%s, %ld, *)\n", FileName
, MemoryType
);
692 /* Open the image file */
693 Status
= ArcOpen((PSTR
)FileName
, OpenReadOnly
, &FileId
);
694 if (Status
!= ESUCCESS
)
696 WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName
, Status
);
700 /* Load the first 2 sectors of the image so we can read the PE header */
701 Status
= ArcRead(FileId
, HeadersBuffer
, SECTOR_SIZE
* 2, &BytesRead
);
702 if (Status
!= ESUCCESS
)
704 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName
, Status
);
705 UiMessageBox("Error reading from file.");
710 /* Now read the MZ header to get the offset to the PE Header */
711 NtHeaders
= RtlImageNtHeader(HeadersBuffer
);
714 ERR("No NT header found in \"%s\"\n", FileName
);
715 UiMessageBox("Error: No NT header found.");
720 /* Ensure this is executable image */
721 if (((NtHeaders
->FileHeader
.Characteristics
& IMAGE_FILE_EXECUTABLE_IMAGE
) == 0))
723 ERR("Not an executable image \"%s\"\n", FileName
);
724 UiMessageBox("Not an executable image.");
729 /* Store number of sections to read and a pointer to the first section */
730 NumberOfSections
= NtHeaders
->FileHeader
.NumberOfSections
;
731 SectionHeader
= IMAGE_FIRST_SECTION(NtHeaders
);
733 /* Try to allocate this memory, if fails - allocate somewhere else */
734 PhysicalBase
= MmAllocateMemoryAtAddress(NtHeaders
->OptionalHeader
.SizeOfImage
,
735 (PVOID
)((ULONG
)NtHeaders
->OptionalHeader
.ImageBase
& (KSEG0_BASE
- 1)),
738 if (PhysicalBase
== NULL
)
740 /* It's ok, we don't panic - let's allocate again at any other "low" place */
741 PhysicalBase
= MmAllocateMemoryWithType(NtHeaders
->OptionalHeader
.SizeOfImage
, MemoryType
);
743 if (PhysicalBase
== NULL
)
745 ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders
->OptionalHeader
.SizeOfImage
, FileName
);
746 UiMessageBox("Failed to alloc pages for image.");
752 /* This is the real image base - in form of a virtual address */
753 VirtualBase
= PaToVa(PhysicalBase
);
755 TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase
, VirtualBase
);
757 /* Set to 0 position and fully load the file image */
758 Position
.QuadPart
= 0;
759 Status
= ArcSeek(FileId
, &Position
, SeekAbsolute
);
760 if (Status
!= ESUCCESS
)
762 ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName
, Status
);
763 UiMessageBox("Error seeking the start of a file.");
768 Status
= ArcRead(FileId
, PhysicalBase
, NtHeaders
->OptionalHeader
.SizeOfHeaders
, &BytesRead
);
769 if (Status
!= ESUCCESS
)
771 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName
, Status
);
772 UiMessageBox("Error reading headers.");
777 /* Reload the NT Header */
778 NtHeaders
= RtlImageNtHeader(PhysicalBase
);
780 /* Load the first section */
781 SectionHeader
= IMAGE_FIRST_SECTION(NtHeaders
);
783 /* Fill output parameters */
784 *ImageBasePA
= PhysicalBase
;
786 /* Walk through each section and read it (check/fix any possible
787 bad situations, if they arise) */
788 for (i
= 0; i
< NumberOfSections
; i
++)
790 VirtualSize
= SectionHeader
->Misc
.VirtualSize
;
791 SizeOfRawData
= SectionHeader
->SizeOfRawData
;
793 /* Handle a case when VirtualSize equals 0 */
794 if (VirtualSize
== 0)
795 VirtualSize
= SizeOfRawData
;
797 /* If PointerToRawData is 0, then force its size to be also 0 */
798 if (SectionHeader
->PointerToRawData
== 0)
804 /* Cut the loaded size to the VirtualSize extents */
805 if (SizeOfRawData
> VirtualSize
)
806 SizeOfRawData
= VirtualSize
;
809 /* Actually read the section (if its size is not 0) */
810 if (SizeOfRawData
!= 0)
812 /* Seek to the correct position */
813 Position
.LowPart
= SectionHeader
->PointerToRawData
;
814 Status
= ArcSeek(FileId
, &Position
, SeekAbsolute
);
816 TRACE("SH->VA: 0x%X\n", SectionHeader
->VirtualAddress
);
818 /* Read this section from the file, size = SizeOfRawData */
819 Status
= ArcRead(FileId
, (PUCHAR
)PhysicalBase
+ SectionHeader
->VirtualAddress
, SizeOfRawData
, &BytesRead
);
820 if (Status
!= ESUCCESS
)
822 ERR("PeLdrLoadImage(): Error reading section from file!\n");
827 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
828 if (SizeOfRawData
< VirtualSize
)
830 TRACE("PeLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData
, VirtualSize
);
831 RtlZeroMemory((PVOID
)(SectionHeader
->VirtualAddress
+ (ULONG_PTR
)PhysicalBase
+ SizeOfRawData
), VirtualSize
- SizeOfRawData
);
837 /* We are done with the file - close it */
840 /* If loading failed - return right now */
841 if (Status
!= ESUCCESS
)
844 /* Relocate the image, if it needs it */
845 if (NtHeaders
->OptionalHeader
.ImageBase
!= (ULONG_PTR
)VirtualBase
)
847 WARN("Relocating %p -> %p\n", NtHeaders
->OptionalHeader
.ImageBase
, VirtualBase
);
848 return (BOOLEAN
)LdrRelocateImageWithBias(PhysicalBase
,
849 (ULONG_PTR
)VirtualBase
- (ULONG_PTR
)PhysicalBase
,
852 TRUE
, /* in case of conflict still return success */
856 TRACE("PeLdrLoadImage() done, PA = %p\n", *ImageBasePA
);