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 IN PLIST_ENTRY Parent OPTIONAL
,
86 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
);
90 IN OUT PLIST_ENTRY ModuleListHead
,
93 IN PIMAGE_THUNK_DATA ThunkData
,
94 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory
,
96 IN BOOLEAN ProcessForwards
,
97 IN PCSTR DirectoryPath
,
98 IN PLIST_ENTRY Parent
)
101 PULONG NameTable
, FunctionTable
;
102 PUSHORT OrdinalTable
;
103 LONG High
, Low
, Middle
, Result
;
105 PIMAGE_IMPORT_BY_NAME ImportData
;
106 PCHAR ExportName
, ForwarderName
;
109 //TRACE("PeLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n",
110 // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards);
112 /* Check passed DllBase param */
115 WARN("DllBase == NULL!\n");
119 /* Convert all non-critical pointers to PA from VA */
120 ThunkData
= VaToPa(ThunkData
);
122 /* Is the reference by ordinal? */
123 if (IMAGE_SNAP_BY_ORDINAL(ThunkData
->u1
.Ordinal
) && !ProcessForwards
)
125 /* Yes, calculate the ordinal */
126 Ordinal
= (ULONG
)(IMAGE_ORDINAL(ThunkData
->u1
.Ordinal
) - (UINT32
)ExportDirectory
->Base
);
127 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
131 /* It's reference by name, we have to look it up in the export directory */
132 if (!ProcessForwards
)
134 /* AddressOfData in thunk entry will become a virtual address (from relative) */
135 //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData);
136 ThunkData
->u1
.AddressOfData
=
137 (ULONG_PTR
)RVA(ImageBase
, ThunkData
->u1
.AddressOfData
);
138 //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData);
141 /* Get the import name */
142 ImportData
= VaToPa((PVOID
)ThunkData
->u1
.AddressOfData
);
144 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
145 NameTable
= VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfNames
));
146 OrdinalTable
= VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfNameOrdinals
));
148 //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
149 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
151 /* Get the hint, convert it to a physical pointer */
152 Hint
= ((PIMAGE_IMPORT_BY_NAME
)VaToPa((PVOID
)ThunkData
->u1
.AddressOfData
))->Hint
;
153 //TRACE("HintIndex %d\n", Hint);
155 /* Get the export name from the hint */
156 ExportName
= VaToPa(RVA(DllBase
, NameTable
[Hint
]));
158 /* If Hint is less than total number of entries in the export directory,
159 and import name == export name, then we can just get it from the OrdinalTable */
160 if ((Hint
< ExportDirectory
->NumberOfNames
) &&
161 (strcmp(ExportName
, (PCHAR
)ImportData
->Name
) == 0))
163 Ordinal
= OrdinalTable
[Hint
];
164 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
168 /* It's not the easy way, we have to lookup import name in the name table.
169 Let's use a binary search for this task. */
171 //TRACE("PeLdrpBindImportName() looking up the import name using binary search...\n");
173 /* Low boundary is set to 0, and high boundary to the maximum index */
175 High
= ExportDirectory
->NumberOfNames
- 1;
177 /* Perform a binary-search loop */
180 /* Divide by 2 by shifting to the right once */
181 Middle
= (Low
+ High
) / 2;
183 /* Get the name from the name table */
184 ExportName
= VaToPa(RVA(DllBase
, NameTable
[Middle
]));
186 /* Compare the names */
187 Result
= strcmp(ExportName
, (PCHAR
)ImportData
->Name
);
189 // TRACE("Binary search: comparing Import '__', Export '%s'\n",
190 // VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),
191 // (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));
193 // TRACE("TE->u1.AOD %p, fulladdr %p\n",
194 // ThunkData->u1.AddressOfData,
195 // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );
197 /* Depending on result of strcmp, perform different actions */
200 /* Adjust top boundary */
205 /* Adjust bottom boundary */
215 /* If high boundary is less than low boundary, then no result found */
218 ERR("Did not find export '%s'!\n", (PCHAR
)ImportData
->Name
);
222 /* Everything alright, get the ordinal */
223 Ordinal
= OrdinalTable
[Middle
];
225 //TRACE("PeLdrpBindImportName() found Ordinal %d\n", Ordinal);
229 /* Check ordinal number for validity! */
230 if (Ordinal
>= ExportDirectory
->NumberOfFunctions
)
232 ERR("Ordinal number is invalid!\n");
236 /* Get a pointer to the function table */
237 FunctionTable
= (PULONG
)VaToPa(RVA(DllBase
, ExportDirectory
->AddressOfFunctions
));
239 /* Save a pointer to the function */
240 ThunkData
->u1
.Function
= (ULONG_PTR
)RVA(DllBase
, FunctionTable
[Ordinal
]);
242 /* Is it a forwarder? (function pointer is within the export directory) */
243 ForwarderName
= (PCHAR
)VaToPa((PVOID
)ThunkData
->u1
.Function
);
244 if (((ULONG_PTR
)ForwarderName
> (ULONG_PTR
)ExportDirectory
) &&
245 ((ULONG_PTR
)ForwarderName
< ((ULONG_PTR
)ExportDirectory
+ ExportSize
)))
247 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
248 CHAR ForwardDllName
[255];
249 PIMAGE_EXPORT_DIRECTORY RefExportDirectory
;
252 TRACE("PeLdrpBindImportName(): ForwarderName %s\n", ForwarderName
);
254 /* Save the name of the forward dll */
255 RtlCopyMemory(ForwardDllName
, ForwarderName
, sizeof(ForwardDllName
));
257 /* Strip out the symbol name */
258 *strrchr(ForwardDllName
,'.') = '\0';
260 /* Check if the target image is already loaded */
261 if (!PeLdrCheckForLoadedDll(ModuleListHead
, ForwardDllName
, &DataTableEntry
))
263 /* Check if the forward dll name has an extension */
264 if (strchr(ForwardDllName
, '.') == NULL
)
266 /* Name does not have an extension, append '.dll' */
267 strcat(ForwardDllName
, ".dll");
270 /* Now let's try to load it! */
271 Success
= PeLdrpLoadAndScanReferencedDll(ModuleListHead
,
278 ERR("PeLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n");
283 /* Get pointer to the export directory of loaded DLL */
284 RefExportDirectory
= (PIMAGE_EXPORT_DIRECTORY
)
285 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry
->DllBase
),
287 IMAGE_DIRECTORY_ENTRY_EXPORT
,
290 /* Fail if it's NULL */
291 if (RefExportDirectory
)
294 IMAGE_THUNK_DATA RefThunkData
;
295 PIMAGE_IMPORT_BY_NAME ImportByName
;
298 /* Get pointer to the import name */
299 ImportName
= strrchr(ForwarderName
, '.') + 1;
301 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
302 ImportByName
= (PIMAGE_IMPORT_BY_NAME
)Buffer
;
304 /* Fill the name with the import name */
305 RtlCopyMemory(ImportByName
->Name
, ImportName
, strlen(ImportName
)+1);
308 ImportByName
->Hint
= 0;
310 /* And finally point ThunkData's AddressOfData to that structure */
311 RefThunkData
.u1
.AddressOfData
= (ULONG_PTR
)ImportByName
;
313 /* And recursively call ourselves */
314 Success
= PeLdrpBindImportName(ModuleListHead
,
315 DataTableEntry
->DllBase
,
324 /* Fill out the ThunkData with data from RefThunkData */
325 ThunkData
->u1
= RefThunkData
.u1
;
327 /* Return what we got from the recursive call */
332 /* Fail if ExportDirectory is NULL */
342 PeLdrpLoadAndScanReferencedDll(
343 IN OUT PLIST_ENTRY ModuleListHead
,
344 IN PCCH DirectoryPath
,
346 IN PLIST_ENTRY Parent OPTIONAL
,
347 OUT PLDR_DATA_TABLE_ENTRY
*DataTableEntry
)
349 CHAR FullDllName
[256];
353 /* Prepare the full path to the file to be loaded */
354 strcpy(FullDllName
, DirectoryPath
);
355 strcat(FullDllName
, ImportName
);
357 TRACE("Loading referenced DLL: %s\n", FullDllName
);
360 Success
= PeLdrLoadImage(FullDllName
, LoaderBootDriver
, &BasePA
);
363 ERR("PeLdrLoadImage() failed\n");
367 /* Allocate DTE for newly loaded DLL */
368 Success
= PeLdrAllocateDataTableEntry(Parent
? Parent
->Blink
: ModuleListHead
,
375 ERR("PeLdrAllocateDataTableEntry() failed\n");
379 (*DataTableEntry
)->Flags
|= LDRP_DRIVER_DEPENDENT_DLL
;
381 /* Scan its dependencies too */
382 TRACE("PeLdrScanImportDescriptorTable() calling ourselves for %S\n",
383 VaToPa((*DataTableEntry
)->BaseDllName
.Buffer
));
384 Success
= PeLdrScanImportDescriptorTable(ModuleListHead
, DirectoryPath
, *DataTableEntry
);
387 ERR("PeLdrScanImportDescriptorTable() failed\n");
395 PeLdrpScanImportAddressTable(
396 IN OUT PLIST_ENTRY ModuleListHead
,
399 IN PIMAGE_THUNK_DATA ThunkData
,
400 IN PCSTR DirectoryPath
,
401 IN PLIST_ENTRY Parent
)
403 PIMAGE_EXPORT_DIRECTORY ExportDirectory
= NULL
;
407 TRACE("PeLdrpScanImportAddressTable(): DllBase 0x%X, "
408 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase
, ImageBase
, ThunkData
);
410 /* Obtain the export table from the DLL's base */
413 ERR("Error, DllBase == NULL!\n");
419 (PIMAGE_EXPORT_DIRECTORY
)RtlImageDirectoryEntryToData(VaToPa(DllBase
),
421 IMAGE_DIRECTORY_ENTRY_EXPORT
,
425 TRACE("PeLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory
);
427 /* If pointer to Export Directory is */
428 if (ExportDirectory
== NULL
)
430 ERR("DllBase=%p(%p)\n", DllBase
, VaToPa(DllBase
));
434 /* Go through each entry in the thunk table and bind it */
435 while (((PIMAGE_THUNK_DATA
)VaToPa(ThunkData
))->u1
.AddressOfData
!= 0)
438 Success
= PeLdrpBindImportName(ModuleListHead
,
448 /* Move to the next entry */
451 /* Return error if binding was unsuccessful */
461 /* FUNCTIONS *****************************************************************/
463 /* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */
465 PeLdrCheckForLoadedDll(
466 IN OUT PLIST_ENTRY ModuleListHead
,
468 OUT PLDR_DATA_TABLE_ENTRY
*LoadedEntry
)
470 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
471 LIST_ENTRY
*ModuleEntry
;
473 TRACE("PeLdrCheckForLoadedDll: DllName %s\n", DllName
);
475 /* Just go through each entry in the LoadOrderList and compare loaded module's
476 name with a given name */
477 ModuleEntry
= ModuleListHead
->Flink
;
478 while (ModuleEntry
!= ModuleListHead
)
480 /* Get pointer to the current DTE */
481 DataTableEntry
= CONTAINING_RECORD(ModuleEntry
,
482 LDR_DATA_TABLE_ENTRY
,
485 TRACE("PeLdrCheckForLoadedDll: DTE %p, EP %p, base %p name '%.*ws'\n",
486 DataTableEntry
, DataTableEntry
->EntryPoint
, DataTableEntry
->DllBase
,
487 DataTableEntry
->BaseDllName
.Length
/ 2, VaToPa(DataTableEntry
->BaseDllName
.Buffer
));
490 if (PeLdrpCompareDllName(DllName
, &DataTableEntry
->BaseDllName
))
492 /* Yes, found it, report pointer to the loaded module's DTE
493 to the caller and increase load count for it */
494 *LoadedEntry
= DataTableEntry
;
495 DataTableEntry
->LoadCount
++;
496 TRACE("PeLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry
);
500 /* Go to the next entry */
501 ModuleEntry
= ModuleEntry
->Flink
;
509 PeLdrScanImportDescriptorTable(
510 IN OUT PLIST_ENTRY ModuleListHead
,
511 IN PCCH DirectoryPath
,
512 IN PLDR_DATA_TABLE_ENTRY ScanDTE
)
514 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
515 PIMAGE_IMPORT_DESCRIPTOR ImportTable
;
516 ULONG ImportTableSize
;
520 /* Get a pointer to the import table of this image */
521 ImportTable
= (PIMAGE_IMPORT_DESCRIPTOR
)RtlImageDirectoryEntryToData(VaToPa(ScanDTE
->DllBase
),
522 TRUE
, IMAGE_DIRECTORY_ENTRY_IMPORT
, &ImportTableSize
);
526 UNICODE_STRING BaseName
;
527 BaseName
.Buffer
= VaToPa(ScanDTE
->BaseDllName
.Buffer
);
528 BaseName
.MaximumLength
= ScanDTE
->BaseDllName
.MaximumLength
;
529 BaseName
.Length
= ScanDTE
->BaseDllName
.Length
;
530 TRACE("PeLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n",
531 &BaseName
, ImportTable
);
535 /* If image doesn't have any import directory - just return success */
536 if (ImportTable
== NULL
)
539 /* Loop through all entries */
540 for (;(ImportTable
->Name
!= 0) && (ImportTable
->FirstThunk
!= 0);ImportTable
++)
542 /* Get pointer to the name */
543 ImportName
= (PCH
)VaToPa(RVA(ScanDTE
->DllBase
, ImportTable
->Name
));
544 TRACE("PeLdrScanImportDescriptorTable(): Looking at %s\n", ImportName
);
546 /* In case we get a reference to ourselves - just skip it */
547 if (PeLdrpCompareDllName(ImportName
, &ScanDTE
->BaseDllName
))
550 /* Load the DLL if it is not already loaded */
551 if (!PeLdrCheckForLoadedDll(ModuleListHead
, ImportName
, &DataTableEntry
))
553 Success
= PeLdrpLoadAndScanReferencedDll(ModuleListHead
,
556 &ScanDTE
->InLoadOrderLinks
,
560 ERR("PeLdrpLoadAndScanReferencedDll() failed\n");
565 /* Scan its import address table */
566 Success
= PeLdrpScanImportAddressTable(ModuleListHead
,
567 DataTableEntry
->DllBase
,
569 (PIMAGE_THUNK_DATA
)RVA(ScanDTE
->DllBase
, ImportTable
->FirstThunk
),
571 &ScanDTE
->InLoadOrderLinks
);
575 ERR("PeLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n",
576 ImportName
, DirectoryPath
);
585 PeLdrAllocateDataTableEntry(
586 IN OUT PLIST_ENTRY ModuleListHead
,
590 OUT PLDR_DATA_TABLE_ENTRY
*NewEntry
)
592 PVOID BaseVA
= PaToVa(BasePA
);
594 PLDR_DATA_TABLE_ENTRY DataTableEntry
;
595 PIMAGE_NT_HEADERS NtHeaders
;
598 TRACE("PeLdrAllocateDataTableEntry(, '%s', '%s', %p)\n",
599 BaseDllName
, FullDllName
, BasePA
);
601 /* Allocate memory for a data table entry, zero-initialize it */
602 DataTableEntry
= (PLDR_DATA_TABLE_ENTRY
)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY
),
604 if (DataTableEntry
== NULL
)
606 RtlZeroMemory(DataTableEntry
, sizeof(LDR_DATA_TABLE_ENTRY
));
608 /* Get NT headers from the image */
609 NtHeaders
= RtlImageNtHeader(BasePA
);
611 /* Initialize corresponding fields of DTE based on NT headers value */
612 DataTableEntry
->DllBase
= BaseVA
;
613 DataTableEntry
->SizeOfImage
= NtHeaders
->OptionalHeader
.SizeOfImage
;
614 DataTableEntry
->EntryPoint
= RVA(BaseVA
, NtHeaders
->OptionalHeader
.AddressOfEntryPoint
);
615 DataTableEntry
->SectionPointer
= 0;
616 DataTableEntry
->CheckSum
= NtHeaders
->OptionalHeader
.CheckSum
;
618 /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName
619 by simple conversion - copying each character */
620 Length
= (USHORT
)(strlen(BaseDllName
) * sizeof(WCHAR
));
621 Buffer
= (PWSTR
)FrLdrHeapAlloc(Length
, TAG_WLDR_NAME
);
624 FrLdrHeapFree(DataTableEntry
, TAG_WLDR_DTE
);
627 RtlZeroMemory(Buffer
, Length
);
629 DataTableEntry
->BaseDllName
.Length
= Length
;
630 DataTableEntry
->BaseDllName
.MaximumLength
= Length
;
631 DataTableEntry
->BaseDllName
.Buffer
= PaToVa(Buffer
);
632 while (*BaseDllName
!= 0)
634 *Buffer
++ = *BaseDllName
++;
637 /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName
638 using the same method */
639 Length
= (USHORT
)(strlen(FullDllName
) * sizeof(WCHAR
));
640 Buffer
= (PWSTR
)FrLdrHeapAlloc(Length
, TAG_WLDR_NAME
);
643 FrLdrHeapFree(DataTableEntry
, TAG_WLDR_DTE
);
646 RtlZeroMemory(Buffer
, Length
);
648 DataTableEntry
->FullDllName
.Length
= Length
;
649 DataTableEntry
->FullDllName
.MaximumLength
= Length
;
650 DataTableEntry
->FullDllName
.Buffer
= PaToVa(Buffer
);
651 while (*FullDllName
!= 0)
653 *Buffer
++ = *FullDllName
++;
656 /* Initialize what's left - LoadCount which is 1, and set Flags so that
657 we know this entry is processed */
658 DataTableEntry
->Flags
= LDRP_ENTRY_PROCESSED
;
659 DataTableEntry
->LoadCount
= 1;
661 /* Honour the FORCE_INTEGRITY flag */
662 if (NtHeaders
->OptionalHeader
.DllCharacteristics
& IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
)
665 * On Vista and above, the LDRP_IMAGE_INTEGRITY_FORCED flag must be set
666 * if IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY is set in the image header.
667 * This is done after the image has been loaded and the digital signature
668 * check has passed successfully. (We do not do it yet!)
670 * Several OS functionality depend on the presence of this flag.
671 * For example, when using Object-Manager callbacks the latter will call
672 * MmVerifyCallbackFunction() to verify whether the flag is present.
673 * If not callbacks will not work.
674 * (See Windows Internals Part 1, 6th edition, p. 176.)
676 DataTableEntry
->Flags
|= LDRP_IMAGE_INTEGRITY_FORCED
;
679 /* Insert this DTE to a list in the LPB */
680 InsertTailList(ModuleListHead
, &DataTableEntry
->InLoadOrderLinks
);
681 TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry
,
682 DataTableEntry
->BaseDllName
.Length
/ 2,
683 VaToPa(DataTableEntry
->BaseDllName
.Buffer
),
684 DataTableEntry
->DllBase
);
686 /* Save pointer to a newly allocated and initialized entry */
687 *NewEntry
= DataTableEntry
;
694 * PeLdrLoadImage loads the specified image from the file (it doesn't
695 * perform any additional operations on the filename, just directly
696 * calls the file I/O routines), and relocates it so that it's ready
697 * to be used when paging is enabled.
698 * Addressing mode: physical
703 IN TYPE_OF_MEMORY MemoryType
,
704 OUT PVOID
*ImageBasePA
)
708 PVOID VirtualBase
= NULL
;
709 UCHAR HeadersBuffer
[SECTOR_SIZE
* 2];
710 PIMAGE_NT_HEADERS NtHeaders
;
711 PIMAGE_SECTION_HEADER SectionHeader
;
712 ULONG VirtualSize
, SizeOfRawData
, NumberOfSections
;
714 LARGE_INTEGER Position
;
717 TRACE("PeLdrLoadImage(%s, %ld, *)\n", FileName
, MemoryType
);
719 /* Open the image file */
720 Status
= ArcOpen((PSTR
)FileName
, OpenReadOnly
, &FileId
);
721 if (Status
!= ESUCCESS
)
723 WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName
, Status
);
727 /* Load the first 2 sectors of the image so we can read the PE header */
728 Status
= ArcRead(FileId
, HeadersBuffer
, SECTOR_SIZE
* 2, &BytesRead
);
729 if (Status
!= ESUCCESS
)
731 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName
, Status
);
732 UiMessageBox("Error reading from file.");
737 /* Now read the MZ header to get the offset to the PE Header */
738 NtHeaders
= RtlImageNtHeader(HeadersBuffer
);
741 ERR("No NT header found in \"%s\"\n", FileName
);
742 UiMessageBox("Error: No NT header found.");
747 /* Ensure this is executable image */
748 if (((NtHeaders
->FileHeader
.Characteristics
& IMAGE_FILE_EXECUTABLE_IMAGE
) == 0))
750 ERR("Not an executable image \"%s\"\n", FileName
);
751 UiMessageBox("Not an executable image.");
756 /* Store number of sections to read and a pointer to the first section */
757 NumberOfSections
= NtHeaders
->FileHeader
.NumberOfSections
;
758 SectionHeader
= IMAGE_FIRST_SECTION(NtHeaders
);
760 /* Try to allocate this memory, if fails - allocate somewhere else */
761 PhysicalBase
= MmAllocateMemoryAtAddress(NtHeaders
->OptionalHeader
.SizeOfImage
,
762 (PVOID
)((ULONG
)NtHeaders
->OptionalHeader
.ImageBase
& (KSEG0_BASE
- 1)),
765 if (PhysicalBase
== NULL
)
767 /* It's ok, we don't panic - let's allocate again at any other "low" place */
768 PhysicalBase
= MmAllocateMemoryWithType(NtHeaders
->OptionalHeader
.SizeOfImage
, MemoryType
);
770 if (PhysicalBase
== NULL
)
772 ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders
->OptionalHeader
.SizeOfImage
, FileName
);
773 UiMessageBox("Failed to alloc pages for image.");
779 /* This is the real image base - in form of a virtual address */
780 VirtualBase
= PaToVa(PhysicalBase
);
782 TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase
, VirtualBase
);
784 /* Set to 0 position and fully load the file image */
785 Position
.QuadPart
= 0;
786 Status
= ArcSeek(FileId
, &Position
, SeekAbsolute
);
787 if (Status
!= ESUCCESS
)
789 ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName
, Status
);
790 UiMessageBox("Error seeking the start of a file.");
795 Status
= ArcRead(FileId
, PhysicalBase
, NtHeaders
->OptionalHeader
.SizeOfHeaders
, &BytesRead
);
796 if (Status
!= ESUCCESS
)
798 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName
, Status
);
799 UiMessageBox("Error reading headers.");
805 * On Vista and above, a digital signature check is performed when the image
806 * has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag set in its header.
807 * (We of course do not perform this check yet!)
810 /* Reload the NT Header */
811 NtHeaders
= RtlImageNtHeader(PhysicalBase
);
813 /* Load the first section */
814 SectionHeader
= IMAGE_FIRST_SECTION(NtHeaders
);
816 /* Fill output parameters */
817 *ImageBasePA
= PhysicalBase
;
819 /* Walk through each section and read it (check/fix any possible
820 bad situations, if they arise) */
821 for (i
= 0; i
< NumberOfSections
; i
++)
823 VirtualSize
= SectionHeader
->Misc
.VirtualSize
;
824 SizeOfRawData
= SectionHeader
->SizeOfRawData
;
826 /* Handle a case when VirtualSize equals 0 */
827 if (VirtualSize
== 0)
828 VirtualSize
= SizeOfRawData
;
830 /* If PointerToRawData is 0, then force its size to be also 0 */
831 if (SectionHeader
->PointerToRawData
== 0)
837 /* Cut the loaded size to the VirtualSize extents */
838 if (SizeOfRawData
> VirtualSize
)
839 SizeOfRawData
= VirtualSize
;
842 /* Actually read the section (if its size is not 0) */
843 if (SizeOfRawData
!= 0)
845 /* Seek to the correct position */
846 Position
.LowPart
= SectionHeader
->PointerToRawData
;
847 Status
= ArcSeek(FileId
, &Position
, SeekAbsolute
);
849 TRACE("SH->VA: 0x%X\n", SectionHeader
->VirtualAddress
);
851 /* Read this section from the file, size = SizeOfRawData */
852 Status
= ArcRead(FileId
, (PUCHAR
)PhysicalBase
+ SectionHeader
->VirtualAddress
, SizeOfRawData
, &BytesRead
);
853 if (Status
!= ESUCCESS
)
855 ERR("PeLdrLoadImage(): Error reading section from file!\n");
860 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
861 if (SizeOfRawData
< VirtualSize
)
863 TRACE("PeLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData
, VirtualSize
);
864 RtlZeroMemory((PVOID
)(SectionHeader
->VirtualAddress
+ (ULONG_PTR
)PhysicalBase
+ SizeOfRawData
), VirtualSize
- SizeOfRawData
);
870 /* We are done with the file - close it */
873 /* If loading failed - return right now */
874 if (Status
!= ESUCCESS
)
877 /* Relocate the image, if it needs it */
878 if (NtHeaders
->OptionalHeader
.ImageBase
!= (ULONG_PTR
)VirtualBase
)
880 WARN("Relocating %p -> %p\n", NtHeaders
->OptionalHeader
.ImageBase
, VirtualBase
);
881 return (BOOLEAN
)LdrRelocateImageWithBias(PhysicalBase
,
882 (ULONG_PTR
)VirtualBase
- (ULONG_PTR
)PhysicalBase
,
885 TRUE
, /* in case of conflict still return success */
889 TRACE("PeLdrLoadImage() done, PA = %p\n", *ImageBasePA
);