- Move some Mm routines from pe.c to sysldr.c where they belong.
[reactos.git] / reactos / ntoskrnl / mm / sysldr.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/mm/sysldr.c
5 * PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 LIST_ENTRY PsLoadedModuleList;
18 KSPIN_LOCK PsLoadedModuleSpinLock;
19 PVOID PsNtosImageBase;
20 KMUTANT MmSystemLoadLock;
21
22 /* FUNCTIONS *****************************************************************/
23
24 VOID
25 NTAPI
26 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
27 IN PVOID OldBase,
28 IN PVOID NewBase,
29 IN ULONG Size)
30 {
31 ULONG_PTR OldBaseTop, Delta;
32 PLDR_DATA_TABLE_ENTRY LdrEntry;
33 PLIST_ENTRY NextEntry;
34 ULONG ImportSize;
35 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
36 PULONG ImageThunk;
37
38 /* Calculate the top and delta */
39 OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
40 Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
41
42 /* Loop the loader block */
43 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
44 NextEntry != &LoaderBlock->LoadOrderListHead;
45 NextEntry = NextEntry->Flink)
46 {
47 /* Get the loader entry */
48 LdrEntry = CONTAINING_RECORD(NextEntry,
49 LDR_DATA_TABLE_ENTRY,
50 InLoadOrderLinks);
51
52 /* Get the import table */
53 ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
54 TRUE,
55 IMAGE_DIRECTORY_ENTRY_IMPORT,
56 &ImportSize);
57 if (!ImportDescriptor) continue;
58
59 /* Make sure we have an IAT */
60 DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
61 while ((ImportDescriptor->Name) &&
62 (ImportDescriptor->OriginalFirstThunk))
63 {
64 /* Get the image thunk */
65 ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
66 ImportDescriptor->FirstThunk);
67 while (*ImageThunk)
68 {
69 /* Check if it's within this module */
70 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
71 {
72 /* Relocate it */
73 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
74 ImageThunk, *ImageThunk, *ImageThunk + Delta);
75 *ImageThunk += Delta;
76 }
77
78 /* Go to the next thunk */
79 ImageThunk++;
80 }
81
82 /* Go to the next import */
83 ImportDescriptor++;
84 }
85 }
86 }
87
88 NTSTATUS
89 NTAPI
90 MiSnapThunk(IN PVOID DllBase,
91 IN PVOID ImageBase,
92 IN PIMAGE_THUNK_DATA Name,
93 IN PIMAGE_THUNK_DATA Address,
94 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
95 IN ULONG ExportSize,
96 IN BOOLEAN SnapForwarder,
97 OUT PCHAR *MissingApi)
98 {
99 BOOLEAN IsOrdinal;
100 USHORT Ordinal;
101 PULONG NameTable;
102 PUSHORT OrdinalTable;
103 PIMAGE_IMPORT_BY_NAME NameImport;
104 USHORT Hint;
105 ULONG Low = 0, Mid = 0, High;
106 LONG Ret;
107 NTSTATUS Status;
108 PCHAR MissingForwarder;
109 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
110 PULONG ExportTable;
111 ANSI_STRING DllName;
112 UNICODE_STRING ForwarderName;
113 PLIST_ENTRY NextEntry;
114 PLDR_DATA_TABLE_ENTRY LdrEntry;
115 ULONG ForwardExportSize;
116 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
117 PIMAGE_IMPORT_BY_NAME ForwardName;
118 ULONG ForwardLength;
119 IMAGE_THUNK_DATA ForwardThunk;
120 PAGED_CODE();
121
122 /* Check if this is an ordinal */
123 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
124 if ((IsOrdinal) && !(SnapForwarder))
125 {
126 /* Get the ordinal number and set it as missing */
127 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
128 ExportDirectory->Base);
129 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
130 }
131 else
132 {
133 /* Get the VA if we don't have to snap */
134 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
135 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
136
137 /* Copy the procedure name */
138 strncpy(*MissingApi,
139 (PCHAR)&NameImport->Name[0],
140 MAXIMUM_FILENAME_LENGTH - 1);
141
142 /* Setup name tables */
143 DPRINT("Import name: %s\n", NameImport->Name);
144 NameTable = (PULONG)((ULONG_PTR)DllBase +
145 ExportDirectory->AddressOfNames);
146 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
147 ExportDirectory->AddressOfNameOrdinals);
148
149 /* Get the hint and check if it's valid */
150 Hint = NameImport->Hint;
151 if ((Hint < ExportDirectory->NumberOfNames) &&
152 !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
153 {
154 /* We have a match, get the ordinal number from here */
155 Ordinal = OrdinalTable[Hint];
156 }
157 else
158 {
159 /* Do a binary search */
160 High = ExportDirectory->NumberOfNames - 1;
161 while (High >= Low)
162 {
163 /* Get new middle value */
164 Mid = (Low + High) >> 1;
165
166 /* Compare name */
167 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
168 if (Ret < 0)
169 {
170 /* Update high */
171 High = Mid - 1;
172 }
173 else if (Ret > 0)
174 {
175 /* Update low */
176 Low = Mid + 1;
177 }
178 else
179 {
180 /* We got it */
181 break;
182 }
183 }
184
185 /* Check if we couldn't find it */
186 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
187
188 /* Otherwise, this is the ordinal */
189 Ordinal = OrdinalTable[Mid];
190 }
191 }
192
193 /* Check if the ordinal is invalid */
194 if (Ordinal >= ExportDirectory->NumberOfFunctions)
195 {
196 /* Fail */
197 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
198 }
199 else
200 {
201 /* In case the forwarder is missing */
202 MissingForwarder = NameBuffer;
203
204 /* Resolve the address and write it */
205 ExportTable = (PULONG)((ULONG_PTR)DllBase +
206 ExportDirectory->AddressOfFunctions);
207 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
208
209 /* Assume success from now on */
210 Status = STATUS_SUCCESS;
211
212 /* Check if the function is actually a forwarder */
213 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
214 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
215 {
216 /* Now assume failure in case the forwarder doesn't exist */
217 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
218
219 /* Build the forwarder name */
220 DllName.Buffer = (PCHAR)Address->u1.Function;
221 DllName.Length = strchr(DllName.Buffer, '.') -
222 DllName.Buffer +
223 sizeof(ANSI_NULL);
224 DllName.MaximumLength = DllName.Length;
225
226 /* Convert it */
227 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
228 &DllName,
229 TRUE)))
230 {
231 /* We failed, just return an error */
232 return Status;
233 }
234
235 /* Loop the module list */
236 NextEntry = PsLoadedModuleList.Flink;
237 while (NextEntry != &PsLoadedModuleList)
238 {
239 /* Get the loader entry */
240 LdrEntry = CONTAINING_RECORD(NextEntry,
241 LDR_DATA_TABLE_ENTRY,
242 InLoadOrderLinks);
243
244 /* Check if it matches */
245 if (RtlPrefixString((PSTRING)&ForwarderName,
246 (PSTRING)&LdrEntry->BaseDllName,
247 TRUE))
248 {
249 /* Get the forwarder export directory */
250 ForwardExportDirectory =
251 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
252 TRUE,
253 IMAGE_DIRECTORY_ENTRY_EXPORT,
254 &ForwardExportSize);
255 if (!ForwardExportDirectory) break;
256
257 /* Allocate a name entry */
258 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
259 sizeof(ANSI_NULL);
260 ForwardName = ExAllocatePoolWithTag(PagedPool,
261 sizeof(*ForwardName) +
262 ForwardLength,
263 TAG_LDR_WSTR);
264 if (!ForwardName) break;
265
266 /* Copy the data */
267 RtlCopyMemory(&ForwardName->Name[0],
268 DllName.Buffer + DllName.Length,
269 ForwardLength);
270 ForwardName->Hint = 0;
271
272 /* Set the new address */
273 *(PULONG)&ForwardThunk.u1.AddressOfData = (ULONG)ForwardName;
274
275 /* Snap the forwarder */
276 Status = MiSnapThunk(LdrEntry->DllBase,
277 ImageBase,
278 &ForwardThunk,
279 &ForwardThunk,
280 ForwardExportDirectory,
281 ForwardExportSize,
282 TRUE,
283 &MissingForwarder);
284
285 /* Free the forwarder name and set the thunk */
286 ExFreePool(ForwardName);
287 Address->u1 = ForwardThunk.u1;
288 break;
289 }
290
291 /* Go to the next entry */
292 NextEntry = NextEntry->Flink;
293 }
294
295 /* Free the name */
296 RtlFreeUnicodeString(&ForwarderName);
297 }
298 }
299
300 /* Return status */
301 return Status;
302 }
303
304 NTSTATUS
305 NTAPI
306 MiResolveImageReferences(IN PVOID ImageBase,
307 IN PUNICODE_STRING ImageFileDirectory,
308 IN PUNICODE_STRING NamePrefix OPTIONAL,
309 OUT PCHAR *MissingApi,
310 OUT PWCHAR *MissingDriver,
311 OUT PLOAD_IMPORTS *LoadImports)
312 {
313 PCHAR MissingApiBuffer = *MissingApi, ImportName;
314 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
315 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
316 PLOAD_IMPORTS LoadedImports;
317 ULONG GdiLink, NormalLink, i;
318 BOOLEAN ReferenceNeeded, Loaded;
319 ANSI_STRING TempString;
320 UNICODE_STRING NameString, DllName;
321 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
322 PVOID ImportBase, DllBase;
323 PLIST_ENTRY NextEntry;
324 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
325 NTSTATUS Status;
326 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
327 PAGED_CODE();
328 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
329 __FUNCTION__, ImageBase, ImageFileDirectory);
330
331 /* Assume no imports */
332 *LoadImports = (PVOID)-2;
333
334 /* Get the import descriptor */
335 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
336 TRUE,
337 IMAGE_DIRECTORY_ENTRY_IMPORT,
338 &ImportSize);
339 if (!ImportDescriptor) return STATUS_SUCCESS;
340
341 /* Loop all imports to count them */
342 for (CurrentImport = ImportDescriptor;
343 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
344 CurrentImport++)
345 {
346 /* One more */
347 ImportCount++;
348 }
349
350 /* Make sure we have non-zero imports */
351 if (ImportCount)
352 {
353 /* Calculate and allocate the list we'll need */
354 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
355 LoadedImports = ExAllocatePoolWithTag(PagedPool,
356 LoadedImportsSize,
357 TAG_LDR_WSTR);
358 if (LoadedImports)
359 {
360 /* Zero it and set the count */
361 RtlZeroMemory(LoadedImports, LoadedImportsSize);
362 LoadedImports->Count = ImportCount;
363 }
364 }
365 else
366 {
367 /* No table */
368 LoadedImports = NULL;
369 }
370
371 /* Reset the import count and loop descriptors again */
372 ImportCount = GdiLink = NormalLink = 0;
373 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
374 {
375 /* Get the name */
376 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
377
378 /* Check if this is a GDI driver */
379 GdiLink = GdiLink |
380 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
381
382 /* We can also allow dxapi */
383 NormalLink = NormalLink |
384 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
385 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
386
387 /* Check if this is a valid GDI driver */
388 if ((GdiLink) && (NormalLink))
389 {
390 /* It's not, it's importing stuff it shouldn't be! */
391 DPRINT1("Invalid driver!\n");
392 //MiDereferenceImports(LoadedImports);
393 if (LoadedImports) ExFreePool(LoadedImports);
394 return STATUS_PROCEDURE_NOT_FOUND;
395 }
396
397 /* Check if this is a "core" import, which doesn't get referenced */
398 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
399 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
400 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
401 {
402 /* Don't reference this */
403 ReferenceNeeded = FALSE;
404 }
405 else
406 {
407 /* Reference these modules */
408 ReferenceNeeded = TRUE;
409 }
410
411 /* Now setup a unicode string for the import */
412 RtlInitAnsiString(&TempString, ImportName);
413 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
414 if (!NT_SUCCESS(Status))
415 {
416 /* Failed */
417 //MiDereferenceImports(LoadedImports);
418 if (LoadedImports) ExFreePool(LoadedImports);
419 return Status;
420 }
421
422 /* We don't support name prefixes yet */
423 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
424
425 /* Remember that we haven't loaded the import at this point */
426 CheckDllState:
427 Loaded = FALSE;
428 ImportBase = NULL;
429
430 /* Loop the driver list */
431 NextEntry = PsLoadedModuleList.Flink;
432 while (NextEntry != &PsLoadedModuleList)
433 {
434 /* Get the loader entry and compare the name */
435 LdrEntry = CONTAINING_RECORD(NextEntry,
436 LDR_DATA_TABLE_ENTRY,
437 InLoadOrderLinks);
438 if (RtlEqualUnicodeString(&NameString,
439 &LdrEntry->BaseDllName,
440 TRUE))
441 {
442 /* Get the base address */
443 ImportBase = LdrEntry->DllBase;
444
445 /* Check if we haven't loaded yet, and we need references */
446 if (!(Loaded) && (ReferenceNeeded))
447 {
448 /* Make sure we're not already loading */
449 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
450 {
451 /* Increase the load count */
452 LdrEntry->LoadCount++;
453 }
454 }
455
456 /* Done, break out */
457 break;
458 }
459
460 /* Go to the next entry */
461 NextEntry = NextEntry->Flink;
462 }
463
464 /* Check if we haven't loaded the import yet */
465 if (!ImportBase)
466 {
467 /* Setup the import DLL name */
468 DllName.MaximumLength = NameString.Length +
469 ImageFileDirectory->Length +
470 sizeof(UNICODE_NULL);
471 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
472 DllName.MaximumLength,
473 TAG_LDR_WSTR);
474 if (DllName.Buffer)
475 {
476 /* Setup the base length and copy it */
477 DllName.Length = ImageFileDirectory->Length;
478 RtlCopyMemory(DllName.Buffer,
479 ImageFileDirectory->Buffer,
480 ImageFileDirectory->Length);
481
482 /* Now add the import name and null-terminate it */
483 RtlAppendStringToString((PSTRING)&DllName,
484 (PSTRING)&NameString);
485 DllName.Buffer[(DllName.MaximumLength - 1) / 2] = UNICODE_NULL;
486
487 /* Load the image */
488 Status = MmLoadSystemImage(&DllName,
489 NamePrefix,
490 NULL,
491 0,
492 (PVOID)&DllEntry,
493 &DllBase);
494 if (NT_SUCCESS(Status))
495 {
496 /* We can free the DLL Name */
497 ExFreePool(DllName.Buffer);
498 }
499 else
500 {
501 /* Fill out the information for the error */
502 DPRINT1("Failed to import: %S\n", DllName.Buffer);
503 *MissingDriver = DllName.Buffer;
504 *(PULONG)MissingDriver |= 1;
505 *MissingApi = NULL;
506 }
507 }
508 else
509 {
510 /* We're out of resources */
511 Status = STATUS_INSUFFICIENT_RESOURCES;
512 }
513
514 /* Check if we're OK until now */
515 if (NT_SUCCESS(Status))
516 {
517 /* We're now loaded */
518 Loaded = TRUE;
519
520 /* Sanity check */
521 ASSERT(DllBase = DllEntry->DllBase);
522
523 /* Call the initialization routines */
524 #if 0
525 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
526 if (!NT_SUCCESS(Status))
527 {
528 /* We failed, unload the image */
529 MmUnloadSystemImage(DllEntry);
530 while (TRUE);
531 Loaded = FALSE;
532 }
533 #endif
534 }
535
536 /* Check if we failed by here */
537 if (!NT_SUCCESS(Status))
538 {
539 /* Cleanup and return */
540 DPRINT1("Failed loading import\n");
541 RtlFreeUnicodeString(&NameString);
542 //MiDereferenceImports(LoadedImports);
543 if (LoadedImports) ExFreePool(LoadedImports);
544 return Status;
545 }
546
547 /* Loop again to make sure that everything is OK */
548 goto CheckDllState;
549 }
550
551 /* Check if we're support to reference this import */
552 if ((ReferenceNeeded) && (LoadedImports))
553 {
554 /* Make sure we're not already loading */
555 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
556 {
557 /* Add the entry */
558 LoadedImports->Entry[ImportCount] = LdrEntry;
559 ImportCount++;
560 }
561 }
562
563 /* Free the import name */
564 RtlFreeUnicodeString(&NameString);
565
566 /* Set the missing driver name and get the export directory */
567 *MissingDriver = LdrEntry->BaseDllName.Buffer;
568 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
569 TRUE,
570 IMAGE_DIRECTORY_ENTRY_EXPORT,
571 &ExportSize);
572 if (!ExportDirectory)
573 {
574 /* Cleanup and return */
575 DPRINT1("Invalid driver: %wZ\n", &LdrEntry->BaseDllName);
576 //MiDereferenceImports(LoadedImports);
577 if (LoadedImports) ExFreePool(LoadedImports);
578 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
579 }
580
581 /* Make sure we have an IAT */
582 if (ImportDescriptor->OriginalFirstThunk)
583 {
584 /* Get the first thunks */
585 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
586 ImportDescriptor->OriginalFirstThunk);
587 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
588 ImportDescriptor->FirstThunk);
589
590 /* Loop the IAT */
591 while (OrigThunk->u1.AddressOfData)
592 {
593 /* Snap thunk */
594 Status = MiSnapThunk(ImportBase,
595 ImageBase,
596 OrigThunk++,
597 FirstThunk++,
598 ExportDirectory,
599 ExportSize,
600 FALSE,
601 MissingApi);
602 if (!NT_SUCCESS(Status))
603 {
604 /* Cleanup and return */
605 //MiDereferenceImports(LoadedImports);
606 if (LoadedImports) ExFreePool(LoadedImports);
607 return Status;
608 }
609
610 /* Reset the buffer */
611 *MissingApi = MissingApiBuffer;
612 }
613 }
614
615 /* Go to the next import */
616 ImportDescriptor++;
617 }
618
619 /* Check if we have an import list */
620 if (LoadedImports)
621 {
622 /* Reset the count again, and loop entries*/
623 ImportCount = 0;
624 for (i = 0; i < LoadedImports->Count; i++)
625 {
626 if (LoadedImports->Entry[i])
627 {
628 /* Got an entry, OR it with 1 in case it's the single entry */
629 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
630 ImportCount++;
631 }
632 }
633
634 /* Check if we had no imports */
635 if (!ImportCount)
636 {
637 /* Free the list and set it to no imports */
638 ExFreePool(LoadedImports);
639 LoadedImports = (PVOID)-2;
640 }
641 else if (ImportCount == 1)
642 {
643 /* Just one entry, we can free the table and only use our entry */
644 ExFreePool(LoadedImports);
645 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
646 }
647 else if (ImportCount != LoadedImports->Count)
648 {
649 /* FIXME: Can this happen? */
650 DPRINT1("Unhandled scenario\n");
651 while (TRUE);
652 }
653
654 /* Return the list */
655 *LoadImports = LoadedImports;
656 }
657
658 /* Return success */
659 return STATUS_SUCCESS;
660 }
661
662 VOID
663 NTAPI
664 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
665 {
666 PLIST_ENTRY NextEntry;
667 ULONG i = 0;
668 PIMAGE_NT_HEADERS NtHeader;
669 PLDR_DATA_TABLE_ENTRY LdrEntry;
670 PIMAGE_FILE_HEADER FileHeader;
671 BOOLEAN ValidRelocs;
672 PIMAGE_DATA_DIRECTORY DataDirectory;
673 PVOID DllBase, NewImageAddress;
674 NTSTATUS Status;
675 ULONG DriverSize = 0, Size;
676 PIMAGE_SECTION_HEADER Section;
677
678 /* Loop driver list */
679 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
680 NextEntry != &LoaderBlock->LoadOrderListHead;
681 NextEntry = NextEntry->Flink)
682 {
683 /* Get the loader entry and NT header */
684 LdrEntry = CONTAINING_RECORD(NextEntry,
685 LDR_DATA_TABLE_ENTRY,
686 InLoadOrderLinks);
687 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
688
689 /* Debug info */
690 DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
691 LdrEntry->DllBase,
692 (ULONG_PTR)LdrEntry->DllBase+ LdrEntry->SizeOfImage,
693 &LdrEntry->FullDllName);
694
695 /* Skip kernel and HAL */
696 /* ROS HACK: Skip BOOTVID/KDCOM too */
697 i++;
698 if (i <= 4) continue;
699
700 /* Skip non-drivers */
701 if (!NtHeader) continue;
702
703 #if 1 // Disable for FreeLDR 2.5
704 /* Get header pointers */
705 Section = IMAGE_FIRST_SECTION(NtHeader);
706
707 /* Determine the size of the module */
708 for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
709 {
710 /* Skip this section if we're not supposed to load it */
711 if (!(Section[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
712 {
713 /* Add the size of this section into the total size */
714 Size = Section[i].VirtualAddress + Section[i].Misc.VirtualSize;
715 DriverSize = max(DriverSize, Size);
716 }
717 }
718
719 /* Round up the driver size to section alignment */
720 DriverSize = ROUND_UP(DriverSize, NtHeader->OptionalHeader.SectionAlignment);
721 #endif
722
723 /* Get the file header and make sure we can relocate */
724 FileHeader = &NtHeader->FileHeader;
725 if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
726 if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
727 IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
728
729 /* Everything made sense until now, check the relocation section too */
730 DataDirectory = &NtHeader->OptionalHeader.
731 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
732 if (!DataDirectory->VirtualAddress)
733 {
734 /* We don't really have relocations */
735 ValidRelocs = FALSE;
736 }
737 else
738 {
739 /* Make sure the size is valid */
740 if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
741 LdrEntry->SizeOfImage)
742 {
743 /* They're not, skip */
744 continue;
745 }
746
747 /* We have relocations */
748 ValidRelocs = TRUE;
749 }
750
751 /* Remember the original address */
752 DllBase = LdrEntry->DllBase;
753
754 /* Allocate a virtual section for the module */
755 NewImageAddress = MmAllocateSection(DriverSize, NULL);
756 if (!NewImageAddress)
757 {
758 /* Shouldn't happen */
759 DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
760 while (TRUE);
761 }
762
763 /* Sanity check */
764 DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
765 ASSERT(ExpInitializationPhase == 0);
766
767 #if 0 // Enable for FreeLDR 2.5
768 /* Now copy the entire driver over */
769 RtlCopyMemory(NewImageAddress, DllBase, DriverSize);
770 #else
771 /* Copy headers over */
772 RtlCopyMemory(NewImageAddress,
773 DllBase,
774 NtHeader->OptionalHeader.SizeOfHeaders);
775
776 /* Copy image sections into virtual section */
777 for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
778 {
779 /* Get the size of this section and check if it's valid and on-disk */
780 Size = Section[i].VirtualAddress + Section[i].Misc.VirtualSize;
781 if ((Size <= DriverSize) && (Section[i].SizeOfRawData))
782 {
783 /* Copy the data from the disk to the image */
784 RtlCopyMemory((PVOID)((ULONG_PTR)NewImageAddress +
785 Section[i].VirtualAddress),
786 (PVOID)((ULONG_PTR)DllBase +
787 Section[i].PointerToRawData),
788 Section[i].Misc.VirtualSize >
789 Section[i].SizeOfRawData ?
790 Section[i].SizeOfRawData :
791 Section[i].Misc.VirtualSize);
792 }
793 }
794 #endif
795
796 /* Sanity check */
797 ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
798
799 /* Set the image base to the old address */
800 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
801
802 /* Check if we had relocations */
803 if (ValidRelocs)
804 {
805 /* Relocate the image */
806 Status = LdrRelocateImageWithBias(NewImageAddress,
807 0,
808 "SYSLDR",
809 STATUS_SUCCESS,
810 STATUS_CONFLICTING_ADDRESSES,
811 STATUS_INVALID_IMAGE_FORMAT);
812 if (!NT_SUCCESS(Status))
813 {
814 /* This shouldn't happen */
815 DPRINT1("Relocations failed!\n");
816 while (TRUE);
817 }
818 }
819
820 /* Update the loader entry */
821 LdrEntry->DllBase = NewImageAddress;
822
823 /* Update the thunks */
824 DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
825 MiUpdateThunks(LoaderBlock,
826 DllBase,
827 NewImageAddress,
828 LdrEntry->SizeOfImage);
829
830 /* Update the loader entry */
831 LdrEntry->Flags |= 0x01000000;
832 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
833 NtHeader->OptionalHeader.AddressOfEntryPoint);
834 LdrEntry->SizeOfImage = DriverSize;
835 }
836 }
837
838 BOOLEAN
839 NTAPI
840 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
841 {
842 PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
843 PLIST_ENTRY ListHead, NextEntry;
844 ULONG EntrySize;
845
846 /* Setup the loaded module list and lock */
847 KeInitializeSpinLock(&PsLoadedModuleSpinLock);
848 InitializeListHead(&PsLoadedModuleList);
849
850 /* Get loop variables and the kernel entry */
851 ListHead = &LoaderBlock->LoadOrderListHead;
852 NextEntry = ListHead->Flink;
853 LdrEntry = CONTAINING_RECORD(NextEntry,
854 LDR_DATA_TABLE_ENTRY,
855 InLoadOrderLinks);
856 PsNtosImageBase = LdrEntry->DllBase;
857
858 /* Loop the loader block */
859 while (NextEntry != ListHead)
860 {
861 /* Get the loader entry */
862 LdrEntry = CONTAINING_RECORD(NextEntry,
863 LDR_DATA_TABLE_ENTRY,
864 InLoadOrderLinks);
865
866 /* FIXME: ROS HACK. Make sure this is a driver */
867 if (!RtlImageNtHeader(LdrEntry->DllBase))
868 {
869 /* Skip this entry */
870 NextEntry= NextEntry->Flink;
871 continue;
872 }
873
874 /* Calculate the size we'll need and allocate a copy */
875 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
876 LdrEntry->BaseDllName.MaximumLength +
877 sizeof(UNICODE_NULL);
878 NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
879 if (!NewEntry) return FALSE;
880
881 /* Copy the entry over */
882 *NewEntry = *LdrEntry;
883
884 /* Allocate the name */
885 NewEntry->FullDllName.Buffer =
886 ExAllocatePoolWithTag(PagedPool,
887 LdrEntry->FullDllName.MaximumLength +
888 sizeof(UNICODE_NULL),
889 TAG_LDR_WSTR);
890 if (!NewEntry->FullDllName.Buffer) return FALSE;
891
892 /* Set the base name */
893 NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
894
895 /* Copy the full and base name */
896 RtlCopyMemory(NewEntry->FullDllName.Buffer,
897 LdrEntry->FullDllName.Buffer,
898 LdrEntry->FullDllName.MaximumLength);
899 RtlCopyMemory(NewEntry->BaseDllName.Buffer,
900 LdrEntry->BaseDllName.Buffer,
901 LdrEntry->BaseDllName.MaximumLength);
902
903 /* Null-terminate the base name */
904 NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
905 sizeof(WCHAR)] = UNICODE_NULL;
906
907 /* Insert the entry into the list */
908 InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
909 NextEntry = NextEntry->Flink;
910 }
911
912 /* Build the import lists for the boot drivers */
913 //MiBuildImportsForBootDrivers();
914
915 /* We're done */
916 return TRUE;
917 }
918
919 BOOLEAN
920 NTAPI
921 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
922 {
923 PIMAGE_NT_HEADERS NtHeader;
924 PAGED_CODE();
925
926 /* Get NT Headers */
927 NtHeader = RtlImageNtHeader(BaseAddress);
928 if (NtHeader)
929 {
930 /* Check if this image is only safe for UP while we have 2+ CPUs */
931 if ((KeNumberProcessors > 1) &&
932 (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
933 {
934 /* Fail */
935 return FALSE;
936 }
937 }
938
939 /* Otherwise, it's safe */
940 return TRUE;
941 }
942
943 NTSTATUS
944 NTAPI
945 MmCheckSystemImage(IN HANDLE ImageHandle,
946 IN BOOLEAN PurgeSection)
947 {
948 NTSTATUS Status;
949 HANDLE SectionHandle;
950 PVOID ViewBase = NULL;
951 SIZE_T ViewSize = 0;
952 IO_STATUS_BLOCK IoStatusBlock;
953 FILE_STANDARD_INFORMATION FileStandardInfo;
954 KAPC_STATE ApcState;
955 PAGED_CODE();
956
957 /* Create a section for the DLL */
958 Status = ZwCreateSection(&SectionHandle,
959 SECTION_MAP_EXECUTE,
960 NULL,
961 NULL,
962 PAGE_EXECUTE,
963 SEC_COMMIT,
964 ImageHandle);
965 if (!NT_SUCCESS(Status)) return Status;
966
967 /* Make sure we're in the system process */
968 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
969
970 /* Map it */
971 Status = ZwMapViewOfSection(SectionHandle,
972 NtCurrentProcess(),
973 &ViewBase,
974 0,
975 0,
976 NULL,
977 &ViewSize,
978 ViewShare,
979 0,
980 PAGE_EXECUTE);
981 if (!NT_SUCCESS(Status))
982 {
983 /* We failed, close the handle and return */
984 KeUnstackDetachProcess(&ApcState);
985 ZwClose(SectionHandle);
986 return Status;
987 }
988
989 /* Now query image information */
990 Status = ZwQueryInformationFile(ImageHandle,
991 &IoStatusBlock,
992 &FileStandardInfo,
993 sizeof(FileStandardInfo),
994 FileStandardInformation);
995 if ( NT_SUCCESS(Status) )
996 {
997 /* First, verify the checksum */
998 if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
999 FileStandardInfo.
1000 EndOfFile.LowPart,
1001 FileStandardInfo.
1002 EndOfFile.LowPart))
1003 {
1004 /* Set checksum failure */
1005 Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1006 }
1007
1008 /* Check that it's a valid SMP image if we have more then one CPU */
1009 if (!MmVerifyImageIsOkForMpUse(ViewBase))
1010 {
1011 /* Otherwise it's not the right image */
1012 Status = STATUS_IMAGE_MP_UP_MISMATCH;
1013 }
1014 }
1015
1016 /* Unmap the section, close the handle, and return status */
1017 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1018 KeUnstackDetachProcess(&ApcState);
1019 ZwClose(SectionHandle);
1020 return Status;
1021 }