[FREELDR] Set LDRP_DRIVER_DEPENDENT_DLL where necessary. (#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 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry);
86
87 static BOOLEAN
88 PeLdrpBindImportName(
89 IN OUT PLIST_ENTRY ModuleListHead,
90 IN PVOID DllBase,
91 IN PVOID ImageBase,
92 IN PIMAGE_THUNK_DATA ThunkData,
93 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
94 IN ULONG ExportSize,
95 IN BOOLEAN ProcessForwards,
96 IN PCSTR DirectoryPath)
97 {
98 ULONG Ordinal;
99 PULONG NameTable, FunctionTable;
100 PUSHORT OrdinalTable;
101 LONG High, Low, Middle, Result;
102 ULONG Hint;
103 PIMAGE_IMPORT_BY_NAME ImportData;
104 PCHAR ExportName, ForwarderName;
105 BOOLEAN Success;
106
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);
109
110 /* Check passed DllBase param */
111 if(DllBase == NULL)
112 {
113 WARN("DllBase == NULL!\n");
114 return FALSE;
115 }
116
117 /* Convert all non-critical pointers to PA from VA */
118 ThunkData = VaToPa(ThunkData);
119
120 /* Is the reference by ordinal? */
121 if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards)
122 {
123 /* Yes, calculate the ordinal */
124 Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base);
125 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
126 }
127 else
128 {
129 /* It's reference by name, we have to look it up in the export directory */
130 if (!ProcessForwards)
131 {
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);
137 }
138
139 /* Get the import name */
140 ImportData = VaToPa((PVOID)ThunkData->u1.AddressOfData);
141
142 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
143 NameTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames));
144 OrdinalTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals));
145
146 //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
147 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
148
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);
152
153 /* Get the export name from the hint */
154 ExportName = VaToPa(RVA(DllBase, NameTable[Hint]));
155
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))
160 {
161 Ordinal = OrdinalTable[Hint];
162 //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal);
163 }
164 else
165 {
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. */
168
169 //TRACE("PeLdrpBindImportName() looking up the import name using binary search...\n");
170
171 /* Low boundary is set to 0, and high boundary to the maximum index */
172 Low = 0;
173 High = ExportDirectory->NumberOfNames - 1;
174
175 /* Perform a binary-search loop */
176 while (High >= Low)
177 {
178 /* Divide by 2 by shifting to the right once */
179 Middle = (Low + High) / 2;
180
181 /* Get the name from the name table */
182 ExportName = VaToPa(RVA(DllBase, NameTable[Middle]));
183
184 /* Compare the names */
185 Result = strcmp(ExportName, (PCHAR)ImportData->Name);
186
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])));
190
191 // TRACE("TE->u1.AOD %p, fulladdr %p\n",
192 // ThunkData->u1.AddressOfData,
193 // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );
194
195 /* Depending on result of strcmp, perform different actions */
196 if (Result > 0)
197 {
198 /* Adjust top boundary */
199 High = Middle - 1;
200 }
201 else if (Result < 0)
202 {
203 /* Adjust bottom boundary */
204 Low = Middle + 1;
205 }
206 else
207 {
208 /* Yay, found it! */
209 break;
210 }
211 }
212
213 /* If high boundary is less than low boundary, then no result found */
214 if (High < Low)
215 {
216 ERR("Did not find export '%s'!\n", (PCHAR)ImportData->Name);
217 return FALSE;
218 }
219
220 /* Everything alright, get the ordinal */
221 Ordinal = OrdinalTable[Middle];
222
223 //TRACE("PeLdrpBindImportName() found Ordinal %d\n", Ordinal);
224 }
225 }
226
227 /* Check ordinal number for validity! */
228 if (Ordinal >= ExportDirectory->NumberOfFunctions)
229 {
230 ERR("Ordinal number is invalid!\n");
231 return FALSE;
232 }
233
234 /* Get a pointer to the function table */
235 FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions));
236
237 /* Save a pointer to the function */
238 ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]);
239
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)))
244 {
245 PLDR_DATA_TABLE_ENTRY DataTableEntry;
246 CHAR ForwardDllName[255];
247 PIMAGE_EXPORT_DIRECTORY RefExportDirectory;
248 ULONG RefExportSize;
249
250 TRACE("PeLdrpBindImportName(): ForwarderName %s\n", ForwarderName);
251
252 /* Save the name of the forward dll */
253 RtlCopyMemory(ForwardDllName, ForwarderName, sizeof(ForwardDllName));
254
255 /* Strip out the symbol name */
256 *strrchr(ForwardDllName,'.') = '\0';
257
258 /* Check if the target image is already loaded */
259 if (!PeLdrCheckForLoadedDll(ModuleListHead, ForwardDllName, &DataTableEntry))
260 {
261 /* Check if the forward dll name has an extension */
262 if (strchr(ForwardDllName, '.') == NULL)
263 {
264 /* Name does not have an extension, append '.dll' */
265 strcat(ForwardDllName, ".dll");
266 }
267
268 /* Now let's try to load it! */
269 Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead,
270 DirectoryPath,
271 ForwardDllName,
272 &DataTableEntry);
273 if (!Success)
274 {
275 ERR("PeLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n");
276 return Success;
277 }
278 }
279
280 /* Get pointer to the export directory of loaded DLL */
281 RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
282 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase),
283 TRUE,
284 IMAGE_DIRECTORY_ENTRY_EXPORT,
285 &RefExportSize);
286
287 /* Fail if it's NULL */
288 if (RefExportDirectory)
289 {
290 UCHAR Buffer[128];
291 IMAGE_THUNK_DATA RefThunkData;
292 PIMAGE_IMPORT_BY_NAME ImportByName;
293 PCHAR ImportName;
294
295 /* Get pointer to the import name */
296 ImportName = strrchr(ForwarderName, '.') + 1;
297
298 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
299 ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer;
300
301 /* Fill the name with the import name */
302 RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1);
303
304 /* Set Hint to 0 */
305 ImportByName->Hint = 0;
306
307 /* And finally point ThunkData's AddressOfData to that structure */
308 RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName;
309
310 /* And recursively call ourselves */
311 Success = PeLdrpBindImportName(ModuleListHead,
312 DataTableEntry->DllBase,
313 ImageBase,
314 &RefThunkData,
315 RefExportDirectory,
316 RefExportSize,
317 TRUE,
318 DirectoryPath);
319
320 /* Fill out the ThunkData with data from RefThunkData */
321 ThunkData->u1 = RefThunkData.u1;
322
323 /* Return what we got from the recursive call */
324 return Success;
325 }
326 else
327 {
328 /* Fail if ExportDirectory is NULL */
329 return FALSE;
330 }
331 }
332
333 /* Success! */
334 return TRUE;
335 }
336
337 static BOOLEAN
338 PeLdrpLoadAndScanReferencedDll(
339 IN OUT PLIST_ENTRY ModuleListHead,
340 IN PCCH DirectoryPath,
341 IN PCH ImportName,
342 OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
343 {
344 CHAR FullDllName[256];
345 BOOLEAN Success;
346 PVOID BasePA = NULL;
347
348 /* Prepare the full path to the file to be loaded */
349 strcpy(FullDllName, DirectoryPath);
350 strcat(FullDllName, ImportName);
351
352 TRACE("Loading referenced DLL: %s\n", FullDllName);
353
354 /* Load the image */
355 Success = PeLdrLoadImage(FullDllName, LoaderBootDriver, &BasePA);
356 if (!Success)
357 {
358 ERR("PeLdrLoadImage() failed\n");
359 return Success;
360 }
361
362 /* Allocate DTE for newly loaded DLL */
363 Success = PeLdrAllocateDataTableEntry(ModuleListHead,
364 ImportName,
365 FullDllName,
366 BasePA,
367 DataTableEntry);
368 if (!Success)
369 {
370 ERR("PeLdrAllocateDataTableEntry() failed\n");
371 return Success;
372 }
373
374 (*DataTableEntry)->Flags |= LDRP_DRIVER_DEPENDENT_DLL;
375
376 /* Scan its dependencies too */
377 TRACE("PeLdrScanImportDescriptorTable() calling ourselves for %S\n",
378 VaToPa((*DataTableEntry)->BaseDllName.Buffer));
379 Success = PeLdrScanImportDescriptorTable(ModuleListHead, DirectoryPath, *DataTableEntry);
380 if (!Success)
381 {
382 ERR("PeLdrScanImportDescriptorTable() failed\n");
383 return Success;
384 }
385
386 return TRUE;
387 }
388
389 static BOOLEAN
390 PeLdrpScanImportAddressTable(
391 IN OUT PLIST_ENTRY ModuleListHead,
392 IN PVOID DllBase,
393 IN PVOID ImageBase,
394 IN PIMAGE_THUNK_DATA ThunkData,
395 IN PCSTR DirectoryPath)
396 {
397 PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
398 BOOLEAN Success;
399 ULONG ExportSize;
400
401 TRACE("PeLdrpScanImportAddressTable(): DllBase 0x%X, "
402 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData);
403
404 /* Obtain the export table from the DLL's base */
405 if (DllBase == NULL)
406 {
407 ERR("Error, DllBase == NULL!\n");
408 return FALSE;
409 }
410 else
411 {
412 ExportDirectory =
413 (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase),
414 TRUE,
415 IMAGE_DIRECTORY_ENTRY_EXPORT,
416 &ExportSize);
417 }
418
419 TRACE("PeLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory);
420
421 /* If pointer to Export Directory is */
422 if (ExportDirectory == NULL)
423 {
424 ERR("DllBase=%p(%p)\n", DllBase, VaToPa(DllBase));
425 return FALSE;
426 }
427
428 /* Go through each entry in the thunk table and bind it */
429 while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0)
430 {
431 /* Bind it */
432 Success = PeLdrpBindImportName(ModuleListHead,
433 DllBase,
434 ImageBase,
435 ThunkData,
436 ExportDirectory,
437 ExportSize,
438 FALSE,
439 DirectoryPath);
440
441 /* Move to the next entry */
442 ThunkData++;
443
444 /* Return error if binding was unsuccessful */
445 if (!Success)
446 return Success;
447 }
448
449 /* Return success */
450 return TRUE;
451 }
452
453
454 /* FUNCTIONS *****************************************************************/
455
456 /* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */
457 BOOLEAN
458 PeLdrCheckForLoadedDll(
459 IN OUT PLIST_ENTRY ModuleListHead,
460 IN PCH DllName,
461 OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry)
462 {
463 PLDR_DATA_TABLE_ENTRY DataTableEntry;
464 LIST_ENTRY *ModuleEntry;
465
466 TRACE("PeLdrCheckForLoadedDll: DllName %s\n", DllName);
467
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)
472 {
473 /* Get pointer to the current DTE */
474 DataTableEntry = CONTAINING_RECORD(ModuleEntry,
475 LDR_DATA_TABLE_ENTRY,
476 InLoadOrderLinks);
477
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));
481
482 /* Compare names */
483 if (PeLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName))
484 {
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);
490 return TRUE;
491 }
492
493 /* Go to the next entry */
494 ModuleEntry = ModuleEntry->Flink;
495 }
496
497 /* Nothing found */
498 return FALSE;
499 }
500
501 BOOLEAN
502 PeLdrScanImportDescriptorTable(
503 IN OUT PLIST_ENTRY ModuleListHead,
504 IN PCCH DirectoryPath,
505 IN PLDR_DATA_TABLE_ENTRY ScanDTE)
506 {
507 PLDR_DATA_TABLE_ENTRY DataTableEntry;
508 PIMAGE_IMPORT_DESCRIPTOR ImportTable;
509 ULONG ImportTableSize;
510 PCH ImportName;
511 BOOLEAN Success;
512
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);
516
517 #if DBG
518 {
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);
525 }
526 #endif
527
528 /* If image doesn't have any import directory - just return success */
529 if (ImportTable == NULL)
530 return TRUE;
531
532 /* Loop through all entries */
533 for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++)
534 {
535 /* Get pointer to the name */
536 ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name));
537 TRACE("PeLdrScanImportDescriptorTable(): Looking at %s\n", ImportName);
538
539 /* In case we get a reference to ourselves - just skip it */
540 if (PeLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName))
541 continue;
542
543 /* Load the DLL if it is not already loaded */
544 if (!PeLdrCheckForLoadedDll(ModuleListHead, ImportName, &DataTableEntry))
545 {
546 Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead,
547 DirectoryPath,
548 ImportName,
549 &DataTableEntry);
550 if (!Success)
551 {
552 ERR("PeLdrpLoadAndScanReferencedDll() failed\n");
553 return Success;
554 }
555 }
556
557 /* Scan its import address table */
558 Success = PeLdrpScanImportAddressTable(ModuleListHead,
559 DataTableEntry->DllBase,
560 ScanDTE->DllBase,
561 (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk),
562 DirectoryPath);
563
564 if (!Success)
565 {
566 ERR("PeLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n",
567 ImportName, DirectoryPath);
568 return Success;
569 }
570 }
571
572 return TRUE;
573 }
574
575 BOOLEAN
576 PeLdrAllocateDataTableEntry(
577 IN OUT PLIST_ENTRY ModuleListHead,
578 IN PCCH BaseDllName,
579 IN PCCH FullDllName,
580 IN PVOID BasePA,
581 OUT PLDR_DATA_TABLE_ENTRY *NewEntry)
582 {
583 PVOID BaseVA = PaToVa(BasePA);
584 PWSTR Buffer;
585 PLDR_DATA_TABLE_ENTRY DataTableEntry;
586 PIMAGE_NT_HEADERS NtHeaders;
587 USHORT Length;
588
589 TRACE("PeLdrAllocateDataTableEntry(, '%s', '%s', %p)\n",
590 BaseDllName, FullDllName, BasePA);
591
592 /* Allocate memory for a data table entry, zero-initialize it */
593 DataTableEntry = (PLDR_DATA_TABLE_ENTRY)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY),
594 TAG_WLDR_DTE);
595 if (DataTableEntry == NULL)
596 return FALSE;
597 RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY));
598
599 /* Get NT headers from the image */
600 NtHeaders = RtlImageNtHeader(BasePA);
601
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;
608
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);
613 if (Buffer == NULL)
614 {
615 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
616 return FALSE;
617 }
618 RtlZeroMemory(Buffer, Length);
619
620 DataTableEntry->BaseDllName.Length = Length;
621 DataTableEntry->BaseDllName.MaximumLength = Length;
622 DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer);
623 while (*BaseDllName != 0)
624 {
625 *Buffer++ = *BaseDllName++;
626 }
627
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);
632 if (Buffer == NULL)
633 {
634 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
635 return FALSE;
636 }
637 RtlZeroMemory(Buffer, Length);
638
639 DataTableEntry->FullDllName.Length = Length;
640 DataTableEntry->FullDllName.MaximumLength = Length;
641 DataTableEntry->FullDllName.Buffer = PaToVa(Buffer);
642 while (*FullDllName != 0)
643 {
644 *Buffer++ = *FullDllName++;
645 }
646
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;
651
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);
658
659 /* Save pointer to a newly allocated and initialized entry */
660 *NewEntry = DataTableEntry;
661
662 /* Return success */
663 return TRUE;
664 }
665
666 /*
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
672 */
673 BOOLEAN
674 PeLdrLoadImage(
675 IN PCHAR FileName,
676 IN TYPE_OF_MEMORY MemoryType,
677 OUT PVOID *ImageBasePA)
678 {
679 ULONG FileId;
680 PVOID PhysicalBase;
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;
686 ARC_STATUS Status;
687 LARGE_INTEGER Position;
688 ULONG i, BytesRead;
689
690 TRACE("PeLdrLoadImage(%s, %ld, *)\n", FileName, MemoryType);
691
692 /* Open the image file */
693 Status = ArcOpen((PSTR)FileName, OpenReadOnly, &FileId);
694 if (Status != ESUCCESS)
695 {
696 WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName, Status);
697 return FALSE;
698 }
699
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)
703 {
704 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
705 UiMessageBox("Error reading from file.");
706 ArcClose(FileId);
707 return FALSE;
708 }
709
710 /* Now read the MZ header to get the offset to the PE Header */
711 NtHeaders = RtlImageNtHeader(HeadersBuffer);
712 if (!NtHeaders)
713 {
714 ERR("No NT header found in \"%s\"\n", FileName);
715 UiMessageBox("Error: No NT header found.");
716 ArcClose(FileId);
717 return FALSE;
718 }
719
720 /* Ensure this is executable image */
721 if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0))
722 {
723 ERR("Not an executable image \"%s\"\n", FileName);
724 UiMessageBox("Not an executable image.");
725 ArcClose(FileId);
726 return FALSE;
727 }
728
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);
732
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)),
736 MemoryType);
737
738 if (PhysicalBase == NULL)
739 {
740 /* It's ok, we don't panic - let's allocate again at any other "low" place */
741 PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType);
742
743 if (PhysicalBase == NULL)
744 {
745 ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders->OptionalHeader.SizeOfImage, FileName);
746 UiMessageBox("Failed to alloc pages for image.");
747 ArcClose(FileId);
748 return FALSE;
749 }
750 }
751
752 /* This is the real image base - in form of a virtual address */
753 VirtualBase = PaToVa(PhysicalBase);
754
755 TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase);
756
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)
761 {
762 ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName, Status);
763 UiMessageBox("Error seeking the start of a file.");
764 ArcClose(FileId);
765 return FALSE;
766 }
767
768 Status = ArcRead(FileId, PhysicalBase, NtHeaders->OptionalHeader.SizeOfHeaders, &BytesRead);
769 if (Status != ESUCCESS)
770 {
771 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
772 UiMessageBox("Error reading headers.");
773 ArcClose(FileId);
774 return FALSE;
775 }
776
777 /* Reload the NT Header */
778 NtHeaders = RtlImageNtHeader(PhysicalBase);
779
780 /* Load the first section */
781 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
782
783 /* Fill output parameters */
784 *ImageBasePA = PhysicalBase;
785
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++)
789 {
790 VirtualSize = SectionHeader->Misc.VirtualSize;
791 SizeOfRawData = SectionHeader->SizeOfRawData;
792
793 /* Handle a case when VirtualSize equals 0 */
794 if (VirtualSize == 0)
795 VirtualSize = SizeOfRawData;
796
797 /* If PointerToRawData is 0, then force its size to be also 0 */
798 if (SectionHeader->PointerToRawData == 0)
799 {
800 SizeOfRawData = 0;
801 }
802 else
803 {
804 /* Cut the loaded size to the VirtualSize extents */
805 if (SizeOfRawData > VirtualSize)
806 SizeOfRawData = VirtualSize;
807 }
808
809 /* Actually read the section (if its size is not 0) */
810 if (SizeOfRawData != 0)
811 {
812 /* Seek to the correct position */
813 Position.LowPart = SectionHeader->PointerToRawData;
814 Status = ArcSeek(FileId, &Position, SeekAbsolute);
815
816 TRACE("SH->VA: 0x%X\n", SectionHeader->VirtualAddress);
817
818 /* Read this section from the file, size = SizeOfRawData */
819 Status = ArcRead(FileId, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress, SizeOfRawData, &BytesRead);
820 if (Status != ESUCCESS)
821 {
822 ERR("PeLdrLoadImage(): Error reading section from file!\n");
823 break;
824 }
825 }
826
827 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
828 if (SizeOfRawData < VirtualSize)
829 {
830 TRACE("PeLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize);
831 RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG_PTR)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData);
832 }
833
834 SectionHeader++;
835 }
836
837 /* We are done with the file - close it */
838 ArcClose(FileId);
839
840 /* If loading failed - return right now */
841 if (Status != ESUCCESS)
842 return FALSE;
843
844 /* Relocate the image, if it needs it */
845 if (NtHeaders->OptionalHeader.ImageBase != (ULONG_PTR)VirtualBase)
846 {
847 WARN("Relocating %p -> %p\n", NtHeaders->OptionalHeader.ImageBase, VirtualBase);
848 return (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase,
849 (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase,
850 "FreeLdr",
851 TRUE,
852 TRUE, /* in case of conflict still return success */
853 FALSE);
854 }
855
856 TRACE("PeLdrLoadImage() done, PA = %p\n", *ImageBasePA);
857 return TRUE;
858 }