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