[FREELDR] Insert DLLs into load order before what references them. (#1951)
[reactos.git] / boot / freeldr / freeldr / lib / peloader.c
1 /*
2 * PROJECT: FreeLoader
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.
6 *
7 * COPYRIGHT: Copyright 1998-2003 Brian Palmer <brianp@sginet.com>
8 * Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org>
9 *
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).
14 *
15 * This article was very handy during development:
16 * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/
17 */
18
19 /* INCLUDES ******************************************************************/
20
21 #include <freeldr.h>
22 #include <debug.h>
23
24 DBG_DEFAULT_CHANNEL(PELOADER);
25
26 /* PRIVATE FUNCTIONS *********************************************************/
27
28 /* DllName - physical, UnicodeString->Buffer - virtual */
29 static BOOLEAN
30 PeLdrpCompareDllName(
31 IN PCH DllName,
32 IN PUNICODE_STRING UnicodeName)
33 {
34 PWSTR Buffer;
35 SIZE_T i, Length;
36
37 /* First obvious check: for length of two names */
38 Length = strlen(DllName);
39
40 #if DBG
41 {
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);
48 }
49 #endif
50
51 if ((Length * sizeof(WCHAR)) > UnicodeName->Length)
52 return FALSE;
53
54 /* Store pointer to unicode string's buffer */
55 Buffer = VaToPa(UnicodeName->Buffer);
56
57 /* Loop character by character */
58 for (i = 0; i < Length; i++)
59 {
60 /* Compare two characters, uppercasing them */
61 if (toupper(*DllName) != toupper((CHAR)*Buffer))
62 return FALSE;
63
64 /* Move to the next character */
65 DllName++;
66 Buffer++;
67 }
68
69 /* Check, if strings either fully match, or match till the "." (w/o extension) */
70 if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.'))
71 {
72 /* Yes they do */
73 return TRUE;
74 }
75
76 /* Strings don't match, return FALSE */
77 return FALSE;
78 }
79
80 static BOOLEAN
81 PeLdrpLoadAndScanReferencedDll(
82 IN OUT PLIST_ENTRY ModuleListHead,
83 IN PCCH DirectoryPath,
84 IN PCH ImportName,
85 IN PLIST_ENTRY Parent OPTIONAL,
86 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry);
87
88 static BOOLEAN
89 PeLdrpBindImportName(
90 IN OUT PLIST_ENTRY ModuleListHead,
91 IN PVOID DllBase,
92 IN PVOID ImageBase,
93 IN PIMAGE_THUNK_DATA ThunkData,
94 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
95 IN ULONG ExportSize,
96 IN BOOLEAN ProcessForwards,
97 IN PCSTR DirectoryPath,
98 IN PLIST_ENTRY Parent)
99 {
100 ULONG Ordinal;
101 PULONG NameTable, FunctionTable;
102 PUSHORT OrdinalTable;
103 LONG High, Low, Middle, Result;
104 ULONG Hint;
105 PIMAGE_IMPORT_BY_NAME ImportData;
106 PCHAR ExportName, ForwarderName;
107 BOOLEAN Success;
108
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);
111
112 /* Check passed DllBase param */
113 if(DllBase == NULL)
114 {
115 WARN("DllBase == NULL!\n");
116 return FALSE;
117 }
118
119 /* Convert all non-critical pointers to PA from VA */
120 ThunkData = VaToPa(ThunkData);
121
122 /* Is the reference by ordinal? */
123 if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards)
124 {
125 /* Yes, calculate the ordinal */
126 Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base);
127 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
128 }
129 else
130 {
131 /* It's reference by name, we have to look it up in the export directory */
132 if (!ProcessForwards)
133 {
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);
139 }
140
141 /* Get the import name */
142 ImportData = VaToPa((PVOID)ThunkData->u1.AddressOfData);
143
144 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
145 NameTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames));
146 OrdinalTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals));
147
148 //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
149 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
150
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);
154
155 /* Get the export name from the hint */
156 ExportName = VaToPa(RVA(DllBase, NameTable[Hint]));
157
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))
162 {
163 Ordinal = OrdinalTable[Hint];
164 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
165 }
166 else
167 {
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. */
170
171 //TRACE("PeLdrpBindImportName() looking up the import name using binary search...\n");
172
173 /* Low boundary is set to 0, and high boundary to the maximum index */
174 Low = 0;
175 High = ExportDirectory->NumberOfNames - 1;
176
177 /* Perform a binary-search loop */
178 while (High >= Low)
179 {
180 /* Divide by 2 by shifting to the right once */
181 Middle = (Low + High) / 2;
182
183 /* Get the name from the name table */
184 ExportName = VaToPa(RVA(DllBase, NameTable[Middle]));
185
186 /* Compare the names */
187 Result = strcmp(ExportName, (PCHAR)ImportData->Name);
188
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])));
192
193 // TRACE("TE->u1.AOD %p, fulladdr %p\n",
194 // ThunkData->u1.AddressOfData,
195 // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );
196
197 /* Depending on result of strcmp, perform different actions */
198 if (Result > 0)
199 {
200 /* Adjust top boundary */
201 High = Middle - 1;
202 }
203 else if (Result < 0)
204 {
205 /* Adjust bottom boundary */
206 Low = Middle + 1;
207 }
208 else
209 {
210 /* Yay, found it! */
211 break;
212 }
213 }
214
215 /* If high boundary is less than low boundary, then no result found */
216 if (High < Low)
217 {
218 ERR("Did not find export '%s'!\n", (PCHAR)ImportData->Name);
219 return FALSE;
220 }
221
222 /* Everything alright, get the ordinal */
223 Ordinal = OrdinalTable[Middle];
224
225 //TRACE("PeLdrpBindImportName() found Ordinal %d\n", Ordinal);
226 }
227 }
228
229 /* Check ordinal number for validity! */
230 if (Ordinal >= ExportDirectory->NumberOfFunctions)
231 {
232 ERR("Ordinal number is invalid!\n");
233 return FALSE;
234 }
235
236 /* Get a pointer to the function table */
237 FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions));
238
239 /* Save a pointer to the function */
240 ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]);
241
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)))
246 {
247 PLDR_DATA_TABLE_ENTRY DataTableEntry;
248 CHAR ForwardDllName[255];
249 PIMAGE_EXPORT_DIRECTORY RefExportDirectory;
250 ULONG RefExportSize;
251
252 TRACE("PeLdrpBindImportName(): ForwarderName %s\n", ForwarderName);
253
254 /* Save the name of the forward dll */
255 RtlCopyMemory(ForwardDllName, ForwarderName, sizeof(ForwardDllName));
256
257 /* Strip out the symbol name */
258 *strrchr(ForwardDllName,'.') = '\0';
259
260 /* Check if the target image is already loaded */
261 if (!PeLdrCheckForLoadedDll(ModuleListHead, ForwardDllName, &DataTableEntry))
262 {
263 /* Check if the forward dll name has an extension */
264 if (strchr(ForwardDllName, '.') == NULL)
265 {
266 /* Name does not have an extension, append '.dll' */
267 strcat(ForwardDllName, ".dll");
268 }
269
270 /* Now let's try to load it! */
271 Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead,
272 DirectoryPath,
273 ForwardDllName,
274 Parent,
275 &DataTableEntry);
276 if (!Success)
277 {
278 ERR("PeLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n");
279 return Success;
280 }
281 }
282
283 /* Get pointer to the export directory of loaded DLL */
284 RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
285 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase),
286 TRUE,
287 IMAGE_DIRECTORY_ENTRY_EXPORT,
288 &RefExportSize);
289
290 /* Fail if it's NULL */
291 if (RefExportDirectory)
292 {
293 UCHAR Buffer[128];
294 IMAGE_THUNK_DATA RefThunkData;
295 PIMAGE_IMPORT_BY_NAME ImportByName;
296 PCHAR ImportName;
297
298 /* Get pointer to the import name */
299 ImportName = strrchr(ForwarderName, '.') + 1;
300
301 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
302 ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer;
303
304 /* Fill the name with the import name */
305 RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1);
306
307 /* Set Hint to 0 */
308 ImportByName->Hint = 0;
309
310 /* And finally point ThunkData's AddressOfData to that structure */
311 RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName;
312
313 /* And recursively call ourselves */
314 Success = PeLdrpBindImportName(ModuleListHead,
315 DataTableEntry->DllBase,
316 ImageBase,
317 &RefThunkData,
318 RefExportDirectory,
319 RefExportSize,
320 TRUE,
321 DirectoryPath,
322 Parent);
323
324 /* Fill out the ThunkData with data from RefThunkData */
325 ThunkData->u1 = RefThunkData.u1;
326
327 /* Return what we got from the recursive call */
328 return Success;
329 }
330 else
331 {
332 /* Fail if ExportDirectory is NULL */
333 return FALSE;
334 }
335 }
336
337 /* Success! */
338 return TRUE;
339 }
340
341 static BOOLEAN
342 PeLdrpLoadAndScanReferencedDll(
343 IN OUT PLIST_ENTRY ModuleListHead,
344 IN PCCH DirectoryPath,
345 IN PCH ImportName,
346 IN PLIST_ENTRY Parent OPTIONAL,
347 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
348 {
349 CHAR FullDllName[256];
350 BOOLEAN Success;
351 PVOID BasePA = NULL;
352
353 /* Prepare the full path to the file to be loaded */
354 strcpy(FullDllName, DirectoryPath);
355 strcat(FullDllName, ImportName);
356
357 TRACE("Loading referenced DLL: %s\n", FullDllName);
358
359 /* Load the image */
360 Success = PeLdrLoadImage(FullDllName, LoaderBootDriver, &BasePA);
361 if (!Success)
362 {
363 ERR("PeLdrLoadImage() failed\n");
364 return Success;
365 }
366
367 /* Allocate DTE for newly loaded DLL */
368 Success = PeLdrAllocateDataTableEntry(Parent ? Parent->Blink : ModuleListHead,
369 ImportName,
370 FullDllName,
371 BasePA,
372 DataTableEntry);
373 if (!Success)
374 {
375 ERR("PeLdrAllocateDataTableEntry() failed\n");
376 return Success;
377 }
378
379 (*DataTableEntry)->Flags |= LDRP_DRIVER_DEPENDENT_DLL;
380
381 /* Scan its dependencies too */
382 TRACE("PeLdrScanImportDescriptorTable() calling ourselves for %S\n",
383 VaToPa((*DataTableEntry)->BaseDllName.Buffer));
384 Success = PeLdrScanImportDescriptorTable(ModuleListHead, DirectoryPath, *DataTableEntry);
385 if (!Success)
386 {
387 ERR("PeLdrScanImportDescriptorTable() failed\n");
388 return Success;
389 }
390
391 return TRUE;
392 }
393
394 static BOOLEAN
395 PeLdrpScanImportAddressTable(
396 IN OUT PLIST_ENTRY ModuleListHead,
397 IN PVOID DllBase,
398 IN PVOID ImageBase,
399 IN PIMAGE_THUNK_DATA ThunkData,
400 IN PCSTR DirectoryPath,
401 IN PLIST_ENTRY Parent)
402 {
403 PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
404 BOOLEAN Success;
405 ULONG ExportSize;
406
407 TRACE("PeLdrpScanImportAddressTable(): DllBase 0x%X, "
408 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData);
409
410 /* Obtain the export table from the DLL's base */
411 if (DllBase == NULL)
412 {
413 ERR("Error, DllBase == NULL!\n");
414 return FALSE;
415 }
416 else
417 {
418 ExportDirectory =
419 (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase),
420 TRUE,
421 IMAGE_DIRECTORY_ENTRY_EXPORT,
422 &ExportSize);
423 }
424
425 TRACE("PeLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory);
426
427 /* If pointer to Export Directory is */
428 if (ExportDirectory == NULL)
429 {
430 ERR("DllBase=%p(%p)\n", DllBase, VaToPa(DllBase));
431 return FALSE;
432 }
433
434 /* Go through each entry in the thunk table and bind it */
435 while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0)
436 {
437 /* Bind it */
438 Success = PeLdrpBindImportName(ModuleListHead,
439 DllBase,
440 ImageBase,
441 ThunkData,
442 ExportDirectory,
443 ExportSize,
444 FALSE,
445 DirectoryPath,
446 Parent);
447
448 /* Move to the next entry */
449 ThunkData++;
450
451 /* Return error if binding was unsuccessful */
452 if (!Success)
453 return Success;
454 }
455
456 /* Return success */
457 return TRUE;
458 }
459
460
461 /* FUNCTIONS *****************************************************************/
462
463 /* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */
464 BOOLEAN
465 PeLdrCheckForLoadedDll(
466 IN OUT PLIST_ENTRY ModuleListHead,
467 IN PCH DllName,
468 OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry)
469 {
470 PLDR_DATA_TABLE_ENTRY DataTableEntry;
471 LIST_ENTRY *ModuleEntry;
472
473 TRACE("PeLdrCheckForLoadedDll: DllName %s\n", DllName);
474
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)
479 {
480 /* Get pointer to the current DTE */
481 DataTableEntry = CONTAINING_RECORD(ModuleEntry,
482 LDR_DATA_TABLE_ENTRY,
483 InLoadOrderLinks);
484
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));
488
489 /* Compare names */
490 if (PeLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName))
491 {
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);
497 return TRUE;
498 }
499
500 /* Go to the next entry */
501 ModuleEntry = ModuleEntry->Flink;
502 }
503
504 /* Nothing found */
505 return FALSE;
506 }
507
508 BOOLEAN
509 PeLdrScanImportDescriptorTable(
510 IN OUT PLIST_ENTRY ModuleListHead,
511 IN PCCH DirectoryPath,
512 IN PLDR_DATA_TABLE_ENTRY ScanDTE)
513 {
514 PLDR_DATA_TABLE_ENTRY DataTableEntry;
515 PIMAGE_IMPORT_DESCRIPTOR ImportTable;
516 ULONG ImportTableSize;
517 PCH ImportName;
518 BOOLEAN Success;
519
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);
523
524 #if DBG
525 {
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);
532 }
533 #endif
534
535 /* If image doesn't have any import directory - just return success */
536 if (ImportTable == NULL)
537 return TRUE;
538
539 /* Loop through all entries */
540 for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++)
541 {
542 /* Get pointer to the name */
543 ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name));
544 TRACE("PeLdrScanImportDescriptorTable(): Looking at %s\n", ImportName);
545
546 /* In case we get a reference to ourselves - just skip it */
547 if (PeLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName))
548 continue;
549
550 /* Load the DLL if it is not already loaded */
551 if (!PeLdrCheckForLoadedDll(ModuleListHead, ImportName, &DataTableEntry))
552 {
553 Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead,
554 DirectoryPath,
555 ImportName,
556 &ScanDTE->InLoadOrderLinks,
557 &DataTableEntry);
558 if (!Success)
559 {
560 ERR("PeLdrpLoadAndScanReferencedDll() failed\n");
561 return Success;
562 }
563 }
564
565 /* Scan its import address table */
566 Success = PeLdrpScanImportAddressTable(ModuleListHead,
567 DataTableEntry->DllBase,
568 ScanDTE->DllBase,
569 (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk),
570 DirectoryPath,
571 &ScanDTE->InLoadOrderLinks);
572
573 if (!Success)
574 {
575 ERR("PeLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n",
576 ImportName, DirectoryPath);
577 return Success;
578 }
579 }
580
581 return TRUE;
582 }
583
584 BOOLEAN
585 PeLdrAllocateDataTableEntry(
586 IN OUT PLIST_ENTRY ModuleListHead,
587 IN PCCH BaseDllName,
588 IN PCCH FullDllName,
589 IN PVOID BasePA,
590 OUT PLDR_DATA_TABLE_ENTRY *NewEntry)
591 {
592 PVOID BaseVA = PaToVa(BasePA);
593 PWSTR Buffer;
594 PLDR_DATA_TABLE_ENTRY DataTableEntry;
595 PIMAGE_NT_HEADERS NtHeaders;
596 USHORT Length;
597
598 TRACE("PeLdrAllocateDataTableEntry(, '%s', '%s', %p)\n",
599 BaseDllName, FullDllName, BasePA);
600
601 /* Allocate memory for a data table entry, zero-initialize it */
602 DataTableEntry = (PLDR_DATA_TABLE_ENTRY)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY),
603 TAG_WLDR_DTE);
604 if (DataTableEntry == NULL)
605 return FALSE;
606 RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY));
607
608 /* Get NT headers from the image */
609 NtHeaders = RtlImageNtHeader(BasePA);
610
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;
617
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);
622 if (Buffer == NULL)
623 {
624 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
625 return FALSE;
626 }
627 RtlZeroMemory(Buffer, Length);
628
629 DataTableEntry->BaseDllName.Length = Length;
630 DataTableEntry->BaseDllName.MaximumLength = Length;
631 DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer);
632 while (*BaseDllName != 0)
633 {
634 *Buffer++ = *BaseDllName++;
635 }
636
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);
641 if (Buffer == NULL)
642 {
643 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
644 return FALSE;
645 }
646 RtlZeroMemory(Buffer, Length);
647
648 DataTableEntry->FullDllName.Length = Length;
649 DataTableEntry->FullDllName.MaximumLength = Length;
650 DataTableEntry->FullDllName.Buffer = PaToVa(Buffer);
651 while (*FullDllName != 0)
652 {
653 *Buffer++ = *FullDllName++;
654 }
655
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;
660
661 /* Insert this DTE to a list in the LPB */
662 InsertTailList(ModuleListHead, &DataTableEntry->InLoadOrderLinks);
663 TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry,
664 DataTableEntry->BaseDllName.Length / 2,
665 VaToPa(DataTableEntry->BaseDllName.Buffer),
666 DataTableEntry->DllBase);
667
668 /* Save pointer to a newly allocated and initialized entry */
669 *NewEntry = DataTableEntry;
670
671 /* Return success */
672 return TRUE;
673 }
674
675 /*
676 * PeLdrLoadImage loads the specified image from the file (it doesn't
677 * perform any additional operations on the filename, just directly
678 * calls the file I/O routines), and relocates it so that it's ready
679 * to be used when paging is enabled.
680 * Addressing mode: physical
681 */
682 BOOLEAN
683 PeLdrLoadImage(
684 IN PCHAR FileName,
685 IN TYPE_OF_MEMORY MemoryType,
686 OUT PVOID *ImageBasePA)
687 {
688 ULONG FileId;
689 PVOID PhysicalBase;
690 PVOID VirtualBase = NULL;
691 UCHAR HeadersBuffer[SECTOR_SIZE * 2];
692 PIMAGE_NT_HEADERS NtHeaders;
693 PIMAGE_SECTION_HEADER SectionHeader;
694 ULONG VirtualSize, SizeOfRawData, NumberOfSections;
695 ARC_STATUS Status;
696 LARGE_INTEGER Position;
697 ULONG i, BytesRead;
698
699 TRACE("PeLdrLoadImage(%s, %ld, *)\n", FileName, MemoryType);
700
701 /* Open the image file */
702 Status = ArcOpen((PSTR)FileName, OpenReadOnly, &FileId);
703 if (Status != ESUCCESS)
704 {
705 WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName, Status);
706 return FALSE;
707 }
708
709 /* Load the first 2 sectors of the image so we can read the PE header */
710 Status = ArcRead(FileId, HeadersBuffer, SECTOR_SIZE * 2, &BytesRead);
711 if (Status != ESUCCESS)
712 {
713 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
714 UiMessageBox("Error reading from file.");
715 ArcClose(FileId);
716 return FALSE;
717 }
718
719 /* Now read the MZ header to get the offset to the PE Header */
720 NtHeaders = RtlImageNtHeader(HeadersBuffer);
721 if (!NtHeaders)
722 {
723 ERR("No NT header found in \"%s\"\n", FileName);
724 UiMessageBox("Error: No NT header found.");
725 ArcClose(FileId);
726 return FALSE;
727 }
728
729 /* Ensure this is executable image */
730 if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0))
731 {
732 ERR("Not an executable image \"%s\"\n", FileName);
733 UiMessageBox("Not an executable image.");
734 ArcClose(FileId);
735 return FALSE;
736 }
737
738 /* Store number of sections to read and a pointer to the first section */
739 NumberOfSections = NtHeaders->FileHeader.NumberOfSections;
740 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
741
742 /* Try to allocate this memory, if fails - allocate somewhere else */
743 PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage,
744 (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)),
745 MemoryType);
746
747 if (PhysicalBase == NULL)
748 {
749 /* It's ok, we don't panic - let's allocate again at any other "low" place */
750 PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType);
751
752 if (PhysicalBase == NULL)
753 {
754 ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders->OptionalHeader.SizeOfImage, FileName);
755 UiMessageBox("Failed to alloc pages for image.");
756 ArcClose(FileId);
757 return FALSE;
758 }
759 }
760
761 /* This is the real image base - in form of a virtual address */
762 VirtualBase = PaToVa(PhysicalBase);
763
764 TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase);
765
766 /* Set to 0 position and fully load the file image */
767 Position.QuadPart = 0;
768 Status = ArcSeek(FileId, &Position, SeekAbsolute);
769 if (Status != ESUCCESS)
770 {
771 ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName, Status);
772 UiMessageBox("Error seeking the start of a file.");
773 ArcClose(FileId);
774 return FALSE;
775 }
776
777 Status = ArcRead(FileId, PhysicalBase, NtHeaders->OptionalHeader.SizeOfHeaders, &BytesRead);
778 if (Status != ESUCCESS)
779 {
780 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
781 UiMessageBox("Error reading headers.");
782 ArcClose(FileId);
783 return FALSE;
784 }
785
786 /* Reload the NT Header */
787 NtHeaders = RtlImageNtHeader(PhysicalBase);
788
789 /* Load the first section */
790 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
791
792 /* Fill output parameters */
793 *ImageBasePA = PhysicalBase;
794
795 /* Walk through each section and read it (check/fix any possible
796 bad situations, if they arise) */
797 for (i = 0; i < NumberOfSections; i++)
798 {
799 VirtualSize = SectionHeader->Misc.VirtualSize;
800 SizeOfRawData = SectionHeader->SizeOfRawData;
801
802 /* Handle a case when VirtualSize equals 0 */
803 if (VirtualSize == 0)
804 VirtualSize = SizeOfRawData;
805
806 /* If PointerToRawData is 0, then force its size to be also 0 */
807 if (SectionHeader->PointerToRawData == 0)
808 {
809 SizeOfRawData = 0;
810 }
811 else
812 {
813 /* Cut the loaded size to the VirtualSize extents */
814 if (SizeOfRawData > VirtualSize)
815 SizeOfRawData = VirtualSize;
816 }
817
818 /* Actually read the section (if its size is not 0) */
819 if (SizeOfRawData != 0)
820 {
821 /* Seek to the correct position */
822 Position.LowPart = SectionHeader->PointerToRawData;
823 Status = ArcSeek(FileId, &Position, SeekAbsolute);
824
825 TRACE("SH->VA: 0x%X\n", SectionHeader->VirtualAddress);
826
827 /* Read this section from the file, size = SizeOfRawData */
828 Status = ArcRead(FileId, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress, SizeOfRawData, &BytesRead);
829 if (Status != ESUCCESS)
830 {
831 ERR("PeLdrLoadImage(): Error reading section from file!\n");
832 break;
833 }
834 }
835
836 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
837 if (SizeOfRawData < VirtualSize)
838 {
839 TRACE("PeLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize);
840 RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG_PTR)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData);
841 }
842
843 SectionHeader++;
844 }
845
846 /* We are done with the file - close it */
847 ArcClose(FileId);
848
849 /* If loading failed - return right now */
850 if (Status != ESUCCESS)
851 return FALSE;
852
853 /* Relocate the image, if it needs it */
854 if (NtHeaders->OptionalHeader.ImageBase != (ULONG_PTR)VirtualBase)
855 {
856 WARN("Relocating %p -> %p\n", NtHeaders->OptionalHeader.ImageBase, VirtualBase);
857 return (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase,
858 (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase,
859 "FreeLdr",
860 TRUE,
861 TRUE, /* in case of conflict still return success */
862 FALSE);
863 }
864
865 TRACE("PeLdrLoadImage() done, PA = %p\n", *ImageBasePA);
866 return TRUE;
867 }