ef40fed3dcc2c8032dcb9c9219734d51027ef3fd
[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 extern ULONG NtGlobalFlag;
22
23 /* FUNCTIONS *****************************************************************/
24
25 VOID
26 NTAPI
27 MiFreeBootDriverMemory(PVOID BaseAddress,
28 ULONG Length)
29 {
30 ULONG i;
31
32 /* Loop each page */
33 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
34 {
35 /* Free the page */
36 MmDeleteVirtualMapping(NULL,
37 (PVOID)((ULONG_PTR)BaseAddress + i * PAGE_SIZE),
38 TRUE,
39 NULL,
40 NULL);
41 }
42 }
43
44 NTSTATUS
45 NTAPI
46 MiLoadImageSection(IN OUT PVOID *SectionPtr,
47 OUT PVOID *ImageBase,
48 IN PUNICODE_STRING FileName,
49 IN BOOLEAN SessionLoad,
50 IN PLDR_DATA_TABLE_ENTRY LdrEntry)
51 {
52 PROS_SECTION_OBJECT Section = *SectionPtr;
53 NTSTATUS Status;
54 PEPROCESS Process;
55 PVOID Base = NULL;
56 SIZE_T ViewSize = 0;
57 KAPC_STATE ApcState;
58 LARGE_INTEGER SectionOffset = {{0}};
59 BOOLEAN LoadSymbols = FALSE;
60 ULONG DriverSize;
61 PVOID DriverBase;
62 PAGED_CODE();
63
64 /* Detect session load */
65 if (SessionLoad)
66 {
67 /* Fail */
68 DPRINT1("Session loading not yet supported!\n");
69 while (TRUE);
70 }
71
72 /* Not session load, shouldn't have an entry */
73 ASSERT(LdrEntry == NULL);
74
75 /* Attach to the system process */
76 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
77
78 /* Check if we need to load symbols */
79 if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
80 {
81 /* Yes we do */
82 LoadSymbols = TRUE;
83 NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
84 }
85
86 /* Map the driver */
87 Process = PsGetCurrentProcess();
88 Status = MmMapViewOfSection(Section,
89 Process,
90 &Base,
91 0,
92 0,
93 &SectionOffset,
94 &ViewSize,
95 ViewUnmap,
96 0,
97 PAGE_EXECUTE);
98
99 /* Re-enable the flag */
100 if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
101
102 /* Check if we failed with distinguished status code */
103 if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
104 {
105 /* Change it to something more generic */
106 Status = STATUS_INVALID_IMAGE_FORMAT;
107 }
108
109 /* Now check if we failed */
110 if (!NT_SUCCESS(Status))
111 {
112 /* Detach and return */
113 KeUnstackDetachProcess(&ApcState);
114 return Status;
115 }
116
117 /* Get the driver size */
118 DriverSize = Section->ImageSection->ImageSize;
119
120 /* Allocate a virtual section for the module */
121 DriverBase = MmAllocateSection(DriverSize, NULL);
122 *ImageBase = DriverBase;
123
124 /* Copy the image */
125 RtlCopyMemory(DriverBase, Base, DriverSize);
126
127 /* Now unmap the view */
128 Status = MmUnmapViewOfSection(Process, Base);
129 ASSERT(NT_SUCCESS(Status));
130
131 /* Detach and return status */
132 KeUnstackDetachProcess(&ApcState);
133 return Status;
134 }
135
136 NTSTATUS
137 NTAPI
138 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
139 {
140 /* Check if there's no imports or if we're a boot driver */
141 if ((ImportList == (PVOID)-1) || (ImportList == (PVOID)-2))
142 {
143 /* Then there's nothing to do */
144 return STATUS_SUCCESS;
145 }
146
147 /* Otherwise, FIXME */
148 DPRINT1("Imports not dereferenced!\n");
149 return STATUS_UNSUCCESSFUL;
150 }
151
152 VOID
153 NTAPI
154 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
155 {
156 PAGED_CODE();
157
158 /* Check if there's no imports or we're a boot driver or only one entry */
159 if ((LdrEntry->LoadedImports == (PVOID)-1) ||
160 (LdrEntry->LoadedImports == (PVOID)-2) ||
161 ((ULONG_PTR)LdrEntry->LoadedImports & 1))
162 {
163 /* Nothing to do */
164 return;
165 }
166
167 /* Otherwise, free the import list */
168 ExFreePool(LdrEntry->LoadedImports);
169 }
170
171 PVOID
172 NTAPI
173 MiFindExportedRoutineByName(IN PVOID DllBase,
174 IN PANSI_STRING ExportName)
175 {
176 PULONG NameTable;
177 PUSHORT OrdinalTable;
178 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
179 LONG Low = 0, Mid = 0, High, Ret;
180 USHORT Ordinal;
181 PVOID Function;
182 ULONG ExportSize;
183 PULONG ExportTable;
184 PAGED_CODE();
185
186 /* Get the export directory */
187 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
188 TRUE,
189 IMAGE_DIRECTORY_ENTRY_EXPORT,
190 &ExportSize);
191 if (!ExportDirectory) return NULL;
192
193 /* Setup name tables */
194 NameTable = (PULONG)((ULONG_PTR)DllBase +
195 ExportDirectory->AddressOfNames);
196 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
197 ExportDirectory->AddressOfNameOrdinals);
198
199 /* Do a binary search */
200 High = ExportDirectory->NumberOfNames - 1;
201 while (High >= Low)
202 {
203 /* Get new middle value */
204 Mid = (Low + High) >> 1;
205
206 /* Compare name */
207 Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
208 if (Ret < 0)
209 {
210 /* Update high */
211 High = Mid - 1;
212 }
213 else if (Ret > 0)
214 {
215 /* Update low */
216 Low = Mid + 1;
217 }
218 else
219 {
220 /* We got it */
221 break;
222 }
223 }
224
225 /* Check if we couldn't find it */
226 if (High < Low) return NULL;
227
228 /* Otherwise, this is the ordinal */
229 Ordinal = OrdinalTable[Mid];
230
231 /* Resolve the address and write it */
232 ExportTable = (PULONG)((ULONG_PTR)DllBase +
233 ExportDirectory->AddressOfFunctions);
234 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
235
236 /* We found it! */
237 ASSERT((Function > (PVOID)ExportDirectory) &&
238 (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
239 return Function;
240 }
241
242 PVOID
243 NTAPI
244 MiLocateExportName(IN PVOID DllBase,
245 IN PCHAR ExportName)
246 {
247 PULONG NameTable;
248 PUSHORT OrdinalTable;
249 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
250 LONG Low = 0, Mid = 0, High, Ret;
251 USHORT Ordinal;
252 PVOID Function;
253 ULONG ExportSize;
254 PULONG ExportTable;
255 PAGED_CODE();
256
257 /* Get the export directory */
258 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
259 TRUE,
260 IMAGE_DIRECTORY_ENTRY_EXPORT,
261 &ExportSize);
262 if (!ExportDirectory) return NULL;
263
264 /* Setup name tables */
265 NameTable = (PULONG)((ULONG_PTR)DllBase +
266 ExportDirectory->AddressOfNames);
267 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
268 ExportDirectory->AddressOfNameOrdinals);
269
270 /* Do a binary search */
271 High = ExportDirectory->NumberOfNames - 1;
272 while (High >= Low)
273 {
274 /* Get new middle value */
275 Mid = (Low + High) >> 1;
276
277 /* Compare name */
278 Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
279 if (Ret < 0)
280 {
281 /* Update high */
282 High = Mid - 1;
283 }
284 else if (Ret > 0)
285 {
286 /* Update low */
287 Low = Mid + 1;
288 }
289 else
290 {
291 /* We got it */
292 break;
293 }
294 }
295
296 /* Check if we couldn't find it */
297 if (High < Low) return NULL;
298
299 /* Otherwise, this is the ordinal */
300 Ordinal = OrdinalTable[Mid];
301
302 /* Resolve the address and write it */
303 ExportTable = (PULONG)((ULONG_PTR)DllBase +
304 ExportDirectory->AddressOfFunctions);
305 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
306
307 /* Check if the function is actually a forwarder */
308 if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
309 ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
310 {
311 /* It is, fail */
312 return NULL;
313 }
314
315 /* We found it */
316 return Function;
317 }
318
319 NTSTATUS
320 NTAPI
321 MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
322 IN PLIST_ENTRY ListHead)
323 {
324 PMM_DLL_INITIALIZE DllInit;
325
326 /* Try to see if the image exports a DllInitialize routine */
327 DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
328 "DllInitialize");
329 if (!DllInit) return STATUS_SUCCESS;
330
331 /* FIXME: TODO */
332 DPRINT1("DllInitialize not called!\n");
333 return STATUS_UNSUCCESSFUL;
334 }
335
336 VOID
337 NTAPI
338 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
339 IN BOOLEAN Insert)
340 {
341 KIRQL OldIrql;
342
343 /* Acquire the lock */
344 KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
345
346 /* Insert or remove from the list */
347 Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
348 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
349
350 /* Release the lock */
351 KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
352 }
353
354 VOID
355 NTAPI
356 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
357 IN PVOID OldBase,
358 IN PVOID NewBase,
359 IN ULONG Size)
360 {
361 ULONG_PTR OldBaseTop, Delta;
362 PLDR_DATA_TABLE_ENTRY LdrEntry;
363 PLIST_ENTRY NextEntry;
364 ULONG ImportSize;
365 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
366 PULONG ImageThunk;
367
368 /* Calculate the top and delta */
369 OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
370 Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
371
372 /* Loop the loader block */
373 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
374 NextEntry != &LoaderBlock->LoadOrderListHead;
375 NextEntry = NextEntry->Flink)
376 {
377 /* Get the loader entry */
378 LdrEntry = CONTAINING_RECORD(NextEntry,
379 LDR_DATA_TABLE_ENTRY,
380 InLoadOrderLinks);
381
382 /* Get the import table */
383 ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
384 TRUE,
385 IMAGE_DIRECTORY_ENTRY_IMPORT,
386 &ImportSize);
387 if (!ImportDescriptor) continue;
388
389 /* Make sure we have an IAT */
390 DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
391 while ((ImportDescriptor->Name) &&
392 (ImportDescriptor->OriginalFirstThunk))
393 {
394 /* Get the image thunk */
395 ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
396 ImportDescriptor->FirstThunk);
397 while (*ImageThunk)
398 {
399 /* Check if it's within this module */
400 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
401 {
402 /* Relocate it */
403 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
404 ImageThunk, *ImageThunk, *ImageThunk + Delta);
405 *ImageThunk += Delta;
406 }
407
408 /* Go to the next thunk */
409 ImageThunk++;
410 }
411
412 /* Go to the next import */
413 ImportDescriptor++;
414 }
415 }
416 }
417
418 NTSTATUS
419 NTAPI
420 MiSnapThunk(IN PVOID DllBase,
421 IN PVOID ImageBase,
422 IN PIMAGE_THUNK_DATA Name,
423 IN PIMAGE_THUNK_DATA Address,
424 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
425 IN ULONG ExportSize,
426 IN BOOLEAN SnapForwarder,
427 OUT PCHAR *MissingApi)
428 {
429 BOOLEAN IsOrdinal;
430 USHORT Ordinal;
431 PULONG NameTable;
432 PUSHORT OrdinalTable;
433 PIMAGE_IMPORT_BY_NAME NameImport;
434 USHORT Hint;
435 ULONG Low = 0, Mid = 0, High;
436 LONG Ret;
437 NTSTATUS Status;
438 PCHAR MissingForwarder;
439 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
440 PULONG ExportTable;
441 ANSI_STRING DllName;
442 UNICODE_STRING ForwarderName;
443 PLIST_ENTRY NextEntry;
444 PLDR_DATA_TABLE_ENTRY LdrEntry;
445 ULONG ForwardExportSize;
446 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
447 PIMAGE_IMPORT_BY_NAME ForwardName;
448 ULONG ForwardLength;
449 IMAGE_THUNK_DATA ForwardThunk;
450 PAGED_CODE();
451
452 /* Check if this is an ordinal */
453 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
454 if ((IsOrdinal) && !(SnapForwarder))
455 {
456 /* Get the ordinal number and set it as missing */
457 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
458 ExportDirectory->Base);
459 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
460 }
461 else
462 {
463 /* Get the VA if we don't have to snap */
464 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
465 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
466
467 /* Copy the procedure name */
468 strncpy(*MissingApi,
469 (PCHAR)&NameImport->Name[0],
470 MAXIMUM_FILENAME_LENGTH - 1);
471
472 /* Setup name tables */
473 DPRINT("Import name: %s\n", NameImport->Name);
474 NameTable = (PULONG)((ULONG_PTR)DllBase +
475 ExportDirectory->AddressOfNames);
476 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
477 ExportDirectory->AddressOfNameOrdinals);
478
479 /* Get the hint and check if it's valid */
480 Hint = NameImport->Hint;
481 if ((Hint < ExportDirectory->NumberOfNames) &&
482 !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
483 {
484 /* We have a match, get the ordinal number from here */
485 Ordinal = OrdinalTable[Hint];
486 }
487 else
488 {
489 /* Do a binary search */
490 High = ExportDirectory->NumberOfNames - 1;
491 while (High >= Low)
492 {
493 /* Get new middle value */
494 Mid = (Low + High) >> 1;
495
496 /* Compare name */
497 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
498 if (Ret < 0)
499 {
500 /* Update high */
501 High = Mid - 1;
502 }
503 else if (Ret > 0)
504 {
505 /* Update low */
506 Low = Mid + 1;
507 }
508 else
509 {
510 /* We got it */
511 break;
512 }
513 }
514
515 /* Check if we couldn't find it */
516 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
517
518 /* Otherwise, this is the ordinal */
519 Ordinal = OrdinalTable[Mid];
520 }
521 }
522
523 /* Check if the ordinal is invalid */
524 if (Ordinal >= ExportDirectory->NumberOfFunctions)
525 {
526 /* Fail */
527 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
528 }
529 else
530 {
531 /* In case the forwarder is missing */
532 MissingForwarder = NameBuffer;
533
534 /* Resolve the address and write it */
535 ExportTable = (PULONG)((ULONG_PTR)DllBase +
536 ExportDirectory->AddressOfFunctions);
537 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
538
539 /* Assume success from now on */
540 Status = STATUS_SUCCESS;
541
542 /* Check if the function is actually a forwarder */
543 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
544 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
545 {
546 /* Now assume failure in case the forwarder doesn't exist */
547 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
548
549 /* Build the forwarder name */
550 DllName.Buffer = (PCHAR)Address->u1.Function;
551 DllName.Length = strchr(DllName.Buffer, '.') -
552 DllName.Buffer +
553 sizeof(ANSI_NULL);
554 DllName.MaximumLength = DllName.Length;
555
556 /* Convert it */
557 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
558 &DllName,
559 TRUE)))
560 {
561 /* We failed, just return an error */
562 return Status;
563 }
564
565 /* Loop the module list */
566 NextEntry = PsLoadedModuleList.Flink;
567 while (NextEntry != &PsLoadedModuleList)
568 {
569 /* Get the loader entry */
570 LdrEntry = CONTAINING_RECORD(NextEntry,
571 LDR_DATA_TABLE_ENTRY,
572 InLoadOrderLinks);
573
574 /* Check if it matches */
575 if (RtlPrefixString((PSTRING)&ForwarderName,
576 (PSTRING)&LdrEntry->BaseDllName,
577 TRUE))
578 {
579 /* Get the forwarder export directory */
580 ForwardExportDirectory =
581 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
582 TRUE,
583 IMAGE_DIRECTORY_ENTRY_EXPORT,
584 &ForwardExportSize);
585 if (!ForwardExportDirectory) break;
586
587 /* Allocate a name entry */
588 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
589 sizeof(ANSI_NULL);
590 ForwardName = ExAllocatePoolWithTag(PagedPool,
591 sizeof(*ForwardName) +
592 ForwardLength,
593 TAG_LDR_WSTR);
594 if (!ForwardName) break;
595
596 /* Copy the data */
597 RtlCopyMemory(&ForwardName->Name[0],
598 DllName.Buffer + DllName.Length,
599 ForwardLength);
600 ForwardName->Hint = 0;
601
602 /* Set the new address */
603 *(PULONG)&ForwardThunk.u1.AddressOfData = (ULONG)ForwardName;
604
605 /* Snap the forwarder */
606 Status = MiSnapThunk(LdrEntry->DllBase,
607 ImageBase,
608 &ForwardThunk,
609 &ForwardThunk,
610 ForwardExportDirectory,
611 ForwardExportSize,
612 TRUE,
613 &MissingForwarder);
614
615 /* Free the forwarder name and set the thunk */
616 ExFreePool(ForwardName);
617 Address->u1 = ForwardThunk.u1;
618 break;
619 }
620
621 /* Go to the next entry */
622 NextEntry = NextEntry->Flink;
623 }
624
625 /* Free the name */
626 RtlFreeUnicodeString(&ForwarderName);
627 }
628 }
629
630 /* Return status */
631 return Status;
632 }
633
634 NTSTATUS
635 NTAPI
636 MmUnloadSystemImage(IN PVOID ImageHandle)
637 {
638 PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
639 PVOID BaseAddress = LdrEntry->DllBase;
640 NTSTATUS Status;
641 ANSI_STRING TempName;
642 BOOLEAN HadEntry = FALSE;
643
644 /* Acquire the loader lock */
645 KeEnterCriticalRegion();
646 KeWaitForSingleObject(&MmSystemLoadLock,
647 WrVirtualMemory,
648 KernelMode,
649 FALSE,
650 NULL);
651
652 /* Check if this driver was loaded at boot and didn't get imports parsed */
653 if (LdrEntry->LoadedImports == (PVOID)-1) goto Done;
654
655 /* We should still be alive */
656 ASSERT(LdrEntry->LoadCount != 0);
657 LdrEntry->LoadCount--;
658
659 /* Check if we're still loaded */
660 if (LdrEntry->LoadCount) goto Done;
661
662 /* We should cleanup... are symbols loaded */
663 if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
664 {
665 /* Create the ANSI name */
666 Status = RtlUnicodeStringToAnsiString(&TempName,
667 &LdrEntry->BaseDllName,
668 TRUE);
669 if (NT_SUCCESS(Status))
670 {
671 /* Unload the symbols */
672 DbgUnLoadImageSymbols(&TempName, BaseAddress, -1);
673 RtlFreeAnsiString(&TempName);
674 }
675 }
676
677 /* FIXME: Free the driver */
678 //MmFreeSection(LdrEntry->DllBase);
679
680 /* Check if we're linked in */
681 if (LdrEntry->InLoadOrderLinks.Flink)
682 {
683 /* Remove us */
684 MiProcessLoaderEntry(LdrEntry, FALSE);
685 HadEntry = TRUE;
686 }
687
688 /* Dereference and clear the imports */
689 MiDereferenceImports(LdrEntry->LoadedImports);
690 MiClearImports(LdrEntry);
691
692 /* Check if the entry needs to go away */
693 if (HadEntry)
694 {
695 /* Check if it had a name */
696 if (LdrEntry->FullDllName.Buffer)
697 {
698 /* Free it */
699 ExFreePool(LdrEntry->FullDllName.Buffer);
700 }
701
702 /* Check if we had a section */
703 if (LdrEntry->SectionPointer)
704 {
705 /* Dereference it */
706 ObDereferenceObject(LdrEntry->SectionPointer);
707 }
708
709 /* Free the entry */
710 ExFreePool(LdrEntry);
711 }
712
713 /* Release the system lock and return */
714 Done:
715 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
716 KeLeaveCriticalRegion();
717 return STATUS_SUCCESS;
718 }
719
720 NTSTATUS
721 NTAPI
722 MiResolveImageReferences(IN PVOID ImageBase,
723 IN PUNICODE_STRING ImageFileDirectory,
724 IN PUNICODE_STRING NamePrefix OPTIONAL,
725 OUT PCHAR *MissingApi,
726 OUT PWCHAR *MissingDriver,
727 OUT PLOAD_IMPORTS *LoadImports)
728 {
729 PCHAR MissingApiBuffer = *MissingApi, ImportName;
730 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
731 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
732 PLOAD_IMPORTS LoadedImports;
733 ULONG GdiLink, NormalLink, i;
734 BOOLEAN ReferenceNeeded, Loaded;
735 ANSI_STRING TempString;
736 UNICODE_STRING NameString, DllName;
737 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
738 PVOID ImportBase, DllBase;
739 PLIST_ENTRY NextEntry;
740 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
741 NTSTATUS Status;
742 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
743 PAGED_CODE();
744 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
745 __FUNCTION__, ImageBase, ImageFileDirectory);
746
747 /* Assume no imports */
748 *LoadImports = (PVOID)-2;
749
750 /* Get the import descriptor */
751 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
752 TRUE,
753 IMAGE_DIRECTORY_ENTRY_IMPORT,
754 &ImportSize);
755 if (!ImportDescriptor) return STATUS_SUCCESS;
756
757 /* Loop all imports to count them */
758 for (CurrentImport = ImportDescriptor;
759 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
760 CurrentImport++)
761 {
762 /* One more */
763 ImportCount++;
764 }
765
766 /* Make sure we have non-zero imports */
767 if (ImportCount)
768 {
769 /* Calculate and allocate the list we'll need */
770 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
771 LoadedImports = ExAllocatePoolWithTag(PagedPool,
772 LoadedImportsSize,
773 TAG_LDR_WSTR);
774 if (LoadedImports)
775 {
776 /* Zero it and set the count */
777 RtlZeroMemory(LoadedImports, LoadedImportsSize);
778 LoadedImports->Count = ImportCount;
779 }
780 }
781 else
782 {
783 /* No table */
784 LoadedImports = NULL;
785 }
786
787 /* Reset the import count and loop descriptors again */
788 ImportCount = GdiLink = NormalLink = 0;
789 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
790 {
791 /* Get the name */
792 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
793
794 /* Check if this is a GDI driver */
795 GdiLink = GdiLink |
796 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
797
798 /* We can also allow dxapi */
799 NormalLink = NormalLink |
800 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
801 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
802
803 /* Check if this is a valid GDI driver */
804 if ((GdiLink) && (NormalLink))
805 {
806 /* It's not, it's importing stuff it shouldn't be! */
807 MiDereferenceImports(LoadedImports);
808 if (LoadedImports) ExFreePool(LoadedImports);
809 return STATUS_PROCEDURE_NOT_FOUND;
810 }
811
812 /* Check if this is a "core" import, which doesn't get referenced */
813 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
814 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
815 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
816 {
817 /* Don't reference this */
818 ReferenceNeeded = FALSE;
819 }
820 else
821 {
822 /* Reference these modules */
823 ReferenceNeeded = TRUE;
824 }
825
826 /* Now setup a unicode string for the import */
827 RtlInitAnsiString(&TempString, ImportName);
828 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
829 if (!NT_SUCCESS(Status))
830 {
831 /* Failed */
832 MiDereferenceImports(LoadedImports);
833 if (LoadedImports) ExFreePool(LoadedImports);
834 return Status;
835 }
836
837 /* We don't support name prefixes yet */
838 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
839
840 /* Remember that we haven't loaded the import at this point */
841 CheckDllState:
842 Loaded = FALSE;
843 ImportBase = NULL;
844
845 /* Loop the driver list */
846 NextEntry = PsLoadedModuleList.Flink;
847 while (NextEntry != &PsLoadedModuleList)
848 {
849 /* Get the loader entry and compare the name */
850 LdrEntry = CONTAINING_RECORD(NextEntry,
851 LDR_DATA_TABLE_ENTRY,
852 InLoadOrderLinks);
853 if (RtlEqualUnicodeString(&NameString,
854 &LdrEntry->BaseDllName,
855 TRUE))
856 {
857 /* Get the base address */
858 ImportBase = LdrEntry->DllBase;
859
860 /* Check if we haven't loaded yet, and we need references */
861 if (!(Loaded) && (ReferenceNeeded))
862 {
863 /* Make sure we're not already loading */
864 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
865 {
866 /* Increase the load count */
867 LdrEntry->LoadCount++;
868 }
869 }
870
871 /* Done, break out */
872 break;
873 }
874
875 /* Go to the next entry */
876 NextEntry = NextEntry->Flink;
877 }
878
879 /* Check if we haven't loaded the import yet */
880 if (!ImportBase)
881 {
882 /* Setup the import DLL name */
883 DllName.MaximumLength = NameString.Length +
884 ImageFileDirectory->Length +
885 sizeof(UNICODE_NULL);
886 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
887 DllName.MaximumLength,
888 TAG_LDR_WSTR);
889 if (DllName.Buffer)
890 {
891 /* Setup the base length and copy it */
892 DllName.Length = ImageFileDirectory->Length;
893 RtlCopyMemory(DllName.Buffer,
894 ImageFileDirectory->Buffer,
895 ImageFileDirectory->Length);
896
897 /* Now add the import name and null-terminate it */
898 RtlAppendStringToString((PSTRING)&DllName,
899 (PSTRING)&NameString);
900 DllName.Buffer[(DllName.MaximumLength - 1) / 2] = UNICODE_NULL;
901
902 /* Load the image */
903 Status = MmLoadSystemImage(&DllName,
904 NamePrefix,
905 NULL,
906 0,
907 (PVOID)&DllEntry,
908 &DllBase);
909 if (NT_SUCCESS(Status))
910 {
911 /* We can free the DLL Name */
912 ExFreePool(DllName.Buffer);
913 }
914 else
915 {
916 /* Fill out the information for the error */
917 *MissingDriver = DllName.Buffer;
918 *(PULONG)MissingDriver |= 1;
919 *MissingApi = NULL;
920 }
921 }
922 else
923 {
924 /* We're out of resources */
925 Status = STATUS_INSUFFICIENT_RESOURCES;
926 }
927
928 /* Check if we're OK until now */
929 if (NT_SUCCESS(Status))
930 {
931 /* We're now loaded */
932 Loaded = TRUE;
933
934 /* Sanity check */
935 ASSERT(DllBase = DllEntry->DllBase);
936
937 /* Call the initialization routines */
938 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
939 if (!NT_SUCCESS(Status))
940 {
941 /* We failed, unload the image */
942 MmUnloadSystemImage(DllEntry);
943 while (TRUE);
944 Loaded = FALSE;
945 }
946 }
947
948 /* Check if we failed by here */
949 if (!NT_SUCCESS(Status))
950 {
951 /* Cleanup and return */
952 RtlFreeUnicodeString(&NameString);
953 MiDereferenceImports(LoadedImports);
954 if (LoadedImports) ExFreePool(LoadedImports);
955 return Status;
956 }
957
958 /* Loop again to make sure that everything is OK */
959 goto CheckDllState;
960 }
961
962 /* Check if we're support to reference this import */
963 if ((ReferenceNeeded) && (LoadedImports))
964 {
965 /* Make sure we're not already loading */
966 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
967 {
968 /* Add the entry */
969 LoadedImports->Entry[ImportCount] = LdrEntry;
970 ImportCount++;
971 }
972 }
973
974 /* Free the import name */
975 RtlFreeUnicodeString(&NameString);
976
977 /* Set the missing driver name and get the export directory */
978 *MissingDriver = LdrEntry->BaseDllName.Buffer;
979 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
980 TRUE,
981 IMAGE_DIRECTORY_ENTRY_EXPORT,
982 &ExportSize);
983 if (!ExportDirectory)
984 {
985 /* Cleanup and return */
986 MiDereferenceImports(LoadedImports);
987 if (LoadedImports) ExFreePool(LoadedImports);
988 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
989 }
990
991 /* Make sure we have an IAT */
992 if (ImportDescriptor->OriginalFirstThunk)
993 {
994 /* Get the first thunks */
995 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
996 ImportDescriptor->OriginalFirstThunk);
997 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
998 ImportDescriptor->FirstThunk);
999
1000 /* Loop the IAT */
1001 while (OrigThunk->u1.AddressOfData)
1002 {
1003 /* Snap thunk */
1004 Status = MiSnapThunk(ImportBase,
1005 ImageBase,
1006 OrigThunk++,
1007 FirstThunk++,
1008 ExportDirectory,
1009 ExportSize,
1010 FALSE,
1011 MissingApi);
1012 if (!NT_SUCCESS(Status))
1013 {
1014 /* Cleanup and return */
1015 MiDereferenceImports(LoadedImports);
1016 if (LoadedImports) ExFreePool(LoadedImports);
1017 return Status;
1018 }
1019
1020 /* Reset the buffer */
1021 *MissingApi = MissingApiBuffer;
1022 }
1023 }
1024
1025 /* Go to the next import */
1026 ImportDescriptor++;
1027 }
1028
1029 /* Check if we have an import list */
1030 if (LoadedImports)
1031 {
1032 /* Reset the count again, and loop entries*/
1033 ImportCount = 0;
1034 for (i = 0; i < LoadedImports->Count; i++)
1035 {
1036 if (LoadedImports->Entry[i])
1037 {
1038 /* Got an entry, OR it with 1 in case it's the single entry */
1039 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
1040 ImportCount++;
1041 }
1042 }
1043
1044 /* Check if we had no imports */
1045 if (!ImportCount)
1046 {
1047 /* Free the list and set it to no imports */
1048 ExFreePool(LoadedImports);
1049 LoadedImports = (PVOID)-2;
1050 }
1051 else if (ImportCount == 1)
1052 {
1053 /* Just one entry, we can free the table and only use our entry */
1054 ExFreePool(LoadedImports);
1055 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1056 }
1057 else if (ImportCount != LoadedImports->Count)
1058 {
1059 /* FIXME: Can this happen? */
1060 DPRINT1("Unhandled scenario\n");
1061 while (TRUE);
1062 }
1063
1064 /* Return the list */
1065 *LoadImports = LoadedImports;
1066 }
1067
1068 /* Return success */
1069 return STATUS_SUCCESS;
1070 }
1071
1072 VOID
1073 NTAPI
1074 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1075 {
1076 PLIST_ENTRY NextEntry;
1077 ULONG i = 0;
1078 PIMAGE_NT_HEADERS NtHeader;
1079 PLDR_DATA_TABLE_ENTRY LdrEntry;
1080 PIMAGE_FILE_HEADER FileHeader;
1081 BOOLEAN ValidRelocs;
1082 PIMAGE_DATA_DIRECTORY DataDirectory;
1083 PVOID DllBase, NewImageAddress;
1084 NTSTATUS Status;
1085
1086 /* Loop driver list */
1087 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1088 NextEntry != &LoaderBlock->LoadOrderListHead;
1089 NextEntry = NextEntry->Flink)
1090 {
1091 /* Get the loader entry and NT header */
1092 LdrEntry = CONTAINING_RECORD(NextEntry,
1093 LDR_DATA_TABLE_ENTRY,
1094 InLoadOrderLinks);
1095 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1096
1097 /* Debug info */
1098 DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1099 LdrEntry->DllBase,
1100 (ULONG_PTR)LdrEntry->DllBase+ LdrEntry->SizeOfImage,
1101 &LdrEntry->FullDllName);
1102
1103 /* Skip kernel and HAL */
1104 /* ROS HACK: Skip BOOTVID/KDCOM too */
1105 i++;
1106 if (i <= 4) continue;
1107
1108 /* Skip non-drivers */
1109 if (!NtHeader) continue;
1110
1111 /* Get the file header and make sure we can relocate */
1112 FileHeader = &NtHeader->FileHeader;
1113 if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1114 if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1115 IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1116
1117 /* Everything made sense until now, check the relocation section too */
1118 DataDirectory = &NtHeader->OptionalHeader.
1119 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1120 if (!DataDirectory->VirtualAddress)
1121 {
1122 /* We don't really have relocations */
1123 ValidRelocs = FALSE;
1124 }
1125 else
1126 {
1127 /* Make sure the size is valid */
1128 if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1129 LdrEntry->SizeOfImage)
1130 {
1131 /* They're not, skip */
1132 continue;
1133 }
1134
1135 /* We have relocations */
1136 ValidRelocs = TRUE;
1137 }
1138
1139 /* Remember the original address */
1140 DllBase = LdrEntry->DllBase;
1141
1142 /* Allocate a virtual section for the module */
1143 NewImageAddress = MmAllocateSection(LdrEntry->SizeOfImage, NULL);
1144 if (!NewImageAddress)
1145 {
1146 /* Shouldn't happen */
1147 DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
1148 while (TRUE);
1149 }
1150
1151 /* Sanity check */
1152 DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1153 ASSERT(ExpInitializationPhase == 0);
1154
1155 /* Now copy the entire driver over */
1156 RtlCopyMemory(NewImageAddress, DllBase, LdrEntry->SizeOfImage);
1157
1158 /* Sanity check */
1159 ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1160
1161 /* Set the image base to the old address */
1162 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1163
1164 /* Check if we had relocations */
1165 if (ValidRelocs)
1166 {
1167 /* Relocate the image */
1168 Status = LdrRelocateImageWithBias(NewImageAddress,
1169 0,
1170 "SYSLDR",
1171 STATUS_SUCCESS,
1172 STATUS_CONFLICTING_ADDRESSES,
1173 STATUS_INVALID_IMAGE_FORMAT);
1174 if (!NT_SUCCESS(Status))
1175 {
1176 /* This shouldn't happen */
1177 DPRINT1("Relocations failed!\n");
1178 while (TRUE);
1179 }
1180 }
1181
1182 /* Update the loader entry */
1183 LdrEntry->DllBase = NewImageAddress;
1184
1185 /* Update the thunks */
1186 DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1187 MiUpdateThunks(LoaderBlock,
1188 DllBase,
1189 NewImageAddress,
1190 LdrEntry->SizeOfImage);
1191
1192 /* Update the loader entry */
1193 LdrEntry->Flags |= 0x01000000;
1194 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1195 NtHeader->OptionalHeader.AddressOfEntryPoint);
1196 LdrEntry->SizeOfImage = LdrEntry->SizeOfImage;
1197
1198 /* Free the old copy */
1199 MiFreeBootDriverMemory(DllBase, LdrEntry->SizeOfImage);
1200 }
1201 }
1202
1203 BOOLEAN
1204 NTAPI
1205 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1206 {
1207 PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
1208 PLIST_ENTRY ListHead, NextEntry;
1209 ULONG EntrySize;
1210
1211 /* Setup the loaded module list and lock */
1212 KeInitializeSpinLock(&PsLoadedModuleSpinLock);
1213 InitializeListHead(&PsLoadedModuleList);
1214
1215 /* Get loop variables and the kernel entry */
1216 ListHead = &LoaderBlock->LoadOrderListHead;
1217 NextEntry = ListHead->Flink;
1218 LdrEntry = CONTAINING_RECORD(NextEntry,
1219 LDR_DATA_TABLE_ENTRY,
1220 InLoadOrderLinks);
1221 PsNtosImageBase = LdrEntry->DllBase;
1222
1223 /* Loop the loader block */
1224 while (NextEntry != ListHead)
1225 {
1226 /* Get the loader entry */
1227 LdrEntry = CONTAINING_RECORD(NextEntry,
1228 LDR_DATA_TABLE_ENTRY,
1229 InLoadOrderLinks);
1230
1231 /* FIXME: ROS HACK. Make sure this is a driver */
1232 if (!RtlImageNtHeader(LdrEntry->DllBase))
1233 {
1234 /* Skip this entry */
1235 NextEntry= NextEntry->Flink;
1236 continue;
1237 }
1238
1239 /* Calculate the size we'll need and allocate a copy */
1240 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1241 LdrEntry->BaseDllName.MaximumLength +
1242 sizeof(UNICODE_NULL);
1243 NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
1244 if (!NewEntry) return FALSE;
1245
1246 /* Copy the entry over */
1247 *NewEntry = *LdrEntry;
1248
1249 /* Allocate the name */
1250 NewEntry->FullDllName.Buffer =
1251 ExAllocatePoolWithTag(PagedPool,
1252 LdrEntry->FullDllName.MaximumLength +
1253 sizeof(UNICODE_NULL),
1254 TAG_LDR_WSTR);
1255 if (!NewEntry->FullDllName.Buffer) return FALSE;
1256
1257 /* Set the base name */
1258 NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
1259
1260 /* Copy the full and base name */
1261 RtlCopyMemory(NewEntry->FullDllName.Buffer,
1262 LdrEntry->FullDllName.Buffer,
1263 LdrEntry->FullDllName.MaximumLength);
1264 RtlCopyMemory(NewEntry->BaseDllName.Buffer,
1265 LdrEntry->BaseDllName.Buffer,
1266 LdrEntry->BaseDllName.MaximumLength);
1267
1268 /* Null-terminate the base name */
1269 NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
1270 sizeof(WCHAR)] = UNICODE_NULL;
1271
1272 /* Insert the entry into the list */
1273 InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
1274 NextEntry = NextEntry->Flink;
1275 }
1276
1277 /* Build the import lists for the boot drivers */
1278 //MiBuildImportsForBootDrivers();
1279
1280 /* We're done */
1281 return TRUE;
1282 }
1283
1284 BOOLEAN
1285 NTAPI
1286 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
1287 {
1288 PIMAGE_NT_HEADERS NtHeader;
1289 PAGED_CODE();
1290
1291 /* Get NT Headers */
1292 NtHeader = RtlImageNtHeader(BaseAddress);
1293 if (NtHeader)
1294 {
1295 /* Check if this image is only safe for UP while we have 2+ CPUs */
1296 if ((KeNumberProcessors > 1) &&
1297 (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
1298 {
1299 /* Fail */
1300 return FALSE;
1301 }
1302 }
1303
1304 /* Otherwise, it's safe */
1305 return TRUE;
1306 }
1307
1308 NTSTATUS
1309 NTAPI
1310 MmCheckSystemImage(IN HANDLE ImageHandle,
1311 IN BOOLEAN PurgeSection)
1312 {
1313 NTSTATUS Status;
1314 HANDLE SectionHandle;
1315 PVOID ViewBase = NULL;
1316 SIZE_T ViewSize = 0;
1317 IO_STATUS_BLOCK IoStatusBlock;
1318 FILE_STANDARD_INFORMATION FileStandardInfo;
1319 KAPC_STATE ApcState;
1320 PAGED_CODE();
1321
1322 /* Create a section for the DLL */
1323 Status = ZwCreateSection(&SectionHandle,
1324 SECTION_MAP_EXECUTE,
1325 NULL,
1326 NULL,
1327 PAGE_EXECUTE,
1328 SEC_COMMIT,
1329 ImageHandle);
1330 if (!NT_SUCCESS(Status)) return Status;
1331
1332 /* Make sure we're in the system process */
1333 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
1334
1335 /* Map it */
1336 Status = ZwMapViewOfSection(SectionHandle,
1337 NtCurrentProcess(),
1338 &ViewBase,
1339 0,
1340 0,
1341 NULL,
1342 &ViewSize,
1343 ViewShare,
1344 0,
1345 PAGE_EXECUTE);
1346 if (!NT_SUCCESS(Status))
1347 {
1348 /* We failed, close the handle and return */
1349 KeUnstackDetachProcess(&ApcState);
1350 ZwClose(SectionHandle);
1351 return Status;
1352 }
1353
1354 /* Now query image information */
1355 Status = ZwQueryInformationFile(ImageHandle,
1356 &IoStatusBlock,
1357 &FileStandardInfo,
1358 sizeof(FileStandardInfo),
1359 FileStandardInformation);
1360 if ( NT_SUCCESS(Status) )
1361 {
1362 /* First, verify the checksum */
1363 if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
1364 FileStandardInfo.
1365 EndOfFile.LowPart,
1366 FileStandardInfo.
1367 EndOfFile.LowPart))
1368 {
1369 /* Set checksum failure */
1370 Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1371 }
1372
1373 /* Check that it's a valid SMP image if we have more then one CPU */
1374 if (!MmVerifyImageIsOkForMpUse(ViewBase))
1375 {
1376 /* Otherwise it's not the right image */
1377 Status = STATUS_IMAGE_MP_UP_MISMATCH;
1378 }
1379 }
1380
1381 /* Unmap the section, close the handle, and return status */
1382 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1383 KeUnstackDetachProcess(&ApcState);
1384 ZwClose(SectionHandle);
1385 return Status;
1386 }
1387
1388 NTSTATUS
1389 NTAPI
1390 MmLoadSystemImage(IN PUNICODE_STRING FileName,
1391 IN PUNICODE_STRING NamePrefix OPTIONAL,
1392 IN PUNICODE_STRING LoadedName OPTIONAL,
1393 IN ULONG Flags,
1394 OUT PVOID *ModuleObject,
1395 OUT PVOID *ImageBaseAddress)
1396 {
1397 PVOID ModuleLoadBase = NULL;
1398 NTSTATUS Status;
1399 HANDLE FileHandle = NULL;
1400 OBJECT_ATTRIBUTES ObjectAttributes;
1401 IO_STATUS_BLOCK IoStatusBlock;
1402 PIMAGE_NT_HEADERS NtHeader;
1403 UNICODE_STRING BaseName, BaseDirectory, PrefixName;
1404 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
1405 ULONG EntrySize;
1406 PLOAD_IMPORTS LoadedImports = (PVOID)-2;
1407 PCHAR MissingApiName, Buffer;
1408 PWCHAR MissingDriverName;
1409 HANDLE SectionHandle;
1410 ACCESS_MASK DesiredAccess;
1411 PVOID Section = NULL;
1412 BOOLEAN LockOwned = FALSE;
1413 PLIST_ENTRY NextEntry;
1414 PAGED_CODE();
1415
1416 /* Detect session-load */
1417 if (Flags)
1418 {
1419 /* Sanity checks */
1420 ASSERT(NamePrefix == NULL);
1421 ASSERT(LoadedName == NULL);
1422
1423 /* Make sure the process is in session too */
1424 if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
1425 }
1426
1427 if (ModuleObject) *ModuleObject = NULL;
1428 if (ImageBaseAddress) *ImageBaseAddress = NULL;
1429
1430 /* Allocate a buffer we'll use for names */
1431 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
1432 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1433
1434 /* Check for a separator */
1435 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1436 {
1437 PWCHAR p;
1438 ULONG BaseLength;
1439
1440 /* Loop the path until we get to the base name */
1441 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
1442 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
1443
1444 /* Get the length */
1445 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
1446 BaseLength *= sizeof(WCHAR);
1447
1448 /* Setup the string */
1449 BaseName.Length = (USHORT)BaseLength;
1450 BaseName.Buffer = p;
1451 }
1452 else
1453 {
1454 /* Otherwise, we already have a base name */
1455 BaseName.Length = FileName->Length;
1456 BaseName.Buffer = FileName->Buffer;
1457 }
1458
1459 /* Setup the maximum length */
1460 BaseName.MaximumLength = BaseName.Length;
1461
1462 /* Now compute the base directory */
1463 BaseDirectory = *FileName;
1464 BaseDirectory.Length -= BaseName.Length;
1465 BaseDirectory.MaximumLength = BaseDirectory.Length;
1466
1467 /* And the prefix, which for now is just the name itself */
1468 PrefixName = *FileName;
1469
1470 /* Check if we have a prefix */
1471 if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
1472
1473 /* Check if we already have a name, use it instead */
1474 if (LoadedName) BaseName = *LoadedName;
1475
1476 /* Acquire the load lock */
1477 LoaderScan:
1478 ASSERT(LockOwned == FALSE);
1479 LockOwned = TRUE;
1480 KeEnterCriticalRegion();
1481 KeWaitForSingleObject(&MmSystemLoadLock,
1482 WrVirtualMemory,
1483 KernelMode,
1484 FALSE,
1485 NULL);
1486
1487 /* Scan the module list */
1488 NextEntry = PsLoadedModuleList.Flink;
1489 while (NextEntry != &PsLoadedModuleList)
1490 {
1491 /* Get the entry and compare the names */
1492 LdrEntry = CONTAINING_RECORD(NextEntry,
1493 LDR_DATA_TABLE_ENTRY,
1494 InLoadOrderLinks);
1495 if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
1496 {
1497 /* Found it, break out */
1498 break;
1499 }
1500
1501 /* Keep scanning */
1502 NextEntry = NextEntry->Flink;
1503 }
1504
1505 /* Check if we found the image */
1506 if (NextEntry != &PsLoadedModuleList)
1507 {
1508 /* Check if we had already mapped a section */
1509 if (Section)
1510 {
1511 /* Dereference and clear */
1512 ObDereferenceObject(Section);
1513 Section = NULL;
1514 }
1515
1516 /* Check if this was supposed to be a session load */
1517 if (!Flags)
1518 {
1519 /* It wasn't, so just return the data */
1520 if (ModuleObject) *ModuleObject = LdrEntry;
1521 if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
1522 Status = STATUS_IMAGE_ALREADY_LOADED;
1523 }
1524 else
1525 {
1526 /* We don't support session loading yet */
1527 DPRINT1("Unsupported Session-Load!\n");
1528 while (TRUE);
1529 }
1530
1531 /* Do cleanup */
1532 goto Quickie;
1533 }
1534 else if (!Section)
1535 {
1536 /* It wasn't loaded, and we didn't have a previous attempt */
1537 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1538 KeLeaveCriticalRegion();
1539 LockOwned = FALSE;
1540
1541 /* Check if KD is enabled */
1542 if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
1543 {
1544 /* FIXME: Attempt to get image from KD */
1545 }
1546
1547 /* We don't have a valid entry */
1548 LdrEntry = NULL;
1549
1550 /* Setup image attributes */
1551 InitializeObjectAttributes(&ObjectAttributes,
1552 FileName,
1553 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1554 NULL,
1555 NULL);
1556
1557 /* Open the image */
1558 Status = ZwOpenFile(&FileHandle,
1559 FILE_EXECUTE,
1560 &ObjectAttributes,
1561 &IoStatusBlock,
1562 FILE_SHARE_READ | FILE_SHARE_DELETE,
1563 0);
1564 if (!NT_SUCCESS(Status)) goto Quickie;
1565
1566 /* Validate it */
1567 Status = MmCheckSystemImage(FileHandle, FALSE);
1568 if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
1569 (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
1570 (Status == STATUS_INVALID_IMAGE_FORMAT))
1571 {
1572 /* Fail loading */
1573 goto Quickie;
1574 }
1575
1576 /* Check if this is a session-load */
1577 if (Flags)
1578 {
1579 /* Then we only need read and execute */
1580 DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
1581 }
1582 else
1583 {
1584 /* Otherwise, we can allow write access */
1585 DesiredAccess = SECTION_ALL_ACCESS;
1586 }
1587
1588 /* Initialize the attributes for the section */
1589 InitializeObjectAttributes(&ObjectAttributes,
1590 NULL,
1591 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1592 NULL,
1593 NULL);
1594
1595 /* Create the section */
1596 Status = ZwCreateSection(&SectionHandle,
1597 DesiredAccess,
1598 &ObjectAttributes,
1599 NULL,
1600 PAGE_EXECUTE,
1601 SEC_IMAGE,
1602 FileHandle);
1603 if (!NT_SUCCESS(Status)) goto Quickie;
1604
1605 /* Now get the section pointer */
1606 Status = ObReferenceObjectByHandle(SectionHandle,
1607 SECTION_MAP_EXECUTE,
1608 MmSectionObjectType,
1609 KernelMode,
1610 &Section,
1611 NULL);
1612 ZwClose(SectionHandle);
1613 if (!NT_SUCCESS(Status)) goto Quickie;
1614
1615 /* Check if this was supposed to be a session-load */
1616 if (Flags)
1617 {
1618 /* We don't support session loading yet */
1619 DPRINT1("Unsupported Session-Load!\n");
1620 while (TRUE);
1621 }
1622
1623 /* Check the loader list again, we should end up in the path below */
1624 goto LoaderScan;
1625 }
1626 else
1627 {
1628 /* We don't have a valid entry */
1629 LdrEntry = NULL;
1630 }
1631
1632 /* Load the image */
1633 Status = MiLoadImageSection(&Section,
1634 &ModuleLoadBase,
1635 FileName,
1636 FALSE,
1637 NULL);
1638 ASSERT(Status != STATUS_ALREADY_COMMITTED);
1639
1640 /* Get the NT Header */
1641 NtHeader = RtlImageNtHeader(ModuleLoadBase);
1642
1643 /* Relocate the driver */
1644 Status = LdrRelocateImageWithBias(ModuleLoadBase,
1645 0,
1646 "SYSLDR",
1647 STATUS_SUCCESS,
1648 STATUS_CONFLICTING_ADDRESSES,
1649 STATUS_INVALID_IMAGE_FORMAT);
1650 if (!NT_SUCCESS(Status)) goto Quickie;
1651
1652 /* Calculate the size we'll need for the entry and allocate it */
1653 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1654 BaseName.Length +
1655 sizeof(UNICODE_NULL);
1656
1657 /* Allocate the entry */
1658 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
1659 if (!LdrEntry)
1660 {
1661 /* Fail */
1662 Status = STATUS_INSUFFICIENT_RESOURCES;
1663 goto Quickie;
1664 }
1665
1666 /* Setup the entry */
1667 LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
1668 LdrEntry->LoadCount = 1;
1669 LdrEntry->LoadedImports = LoadedImports;
1670 LdrEntry->PatchInformation = NULL;
1671
1672 /* Check the version */
1673 if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
1674 (NtHeader->OptionalHeader.MajorImageVersion >= 5))
1675 {
1676 /* Mark this image as a native image */
1677 LdrEntry->Flags |= 0x80000000;
1678 }
1679
1680 /* Setup the rest of the entry */
1681 LdrEntry->DllBase = ModuleLoadBase;
1682 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
1683 NtHeader->OptionalHeader.AddressOfEntryPoint);
1684 LdrEntry->SizeOfImage = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
1685 LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
1686 LdrEntry->SectionPointer = LdrEntry;
1687
1688 /* Now write the DLL name */
1689 LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
1690 LdrEntry->BaseDllName.Length = BaseName.Length;
1691 LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
1692
1693 /* Copy and null-terminate it */
1694 RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
1695 BaseName.Buffer,
1696 BaseName.Length);
1697 LdrEntry->BaseDllName.Buffer[BaseName.Length / 2] = UNICODE_NULL;
1698
1699 /* Now allocate the full name */
1700 LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
1701 PrefixName.Length +
1702 sizeof(UNICODE_NULL),
1703 TAG_LDR_WSTR);
1704 if (!LdrEntry->FullDllName.Buffer)
1705 {
1706 /* Don't fail, just set it to zero */
1707 LdrEntry->FullDllName.Length = 0;
1708 LdrEntry->FullDllName.MaximumLength = 0;
1709 }
1710 else
1711 {
1712 /* Set it up */
1713 LdrEntry->FullDllName.Length = PrefixName.Length;
1714 LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
1715
1716 /* Copy and null-terminate */
1717 RtlCopyMemory(LdrEntry->FullDllName.Buffer,
1718 PrefixName.Buffer,
1719 PrefixName.Length);
1720 LdrEntry->FullDllName.Buffer[PrefixName.Length / 2] = UNICODE_NULL;
1721 }
1722
1723 /* Add the entry */
1724 MiProcessLoaderEntry(LdrEntry, TRUE);
1725
1726 /* Resolve imports */
1727 MissingApiName = Buffer;
1728 Status = MiResolveImageReferences(ModuleLoadBase,
1729 &BaseDirectory,
1730 NULL,
1731 &MissingApiName,
1732 &MissingDriverName,
1733 &LoadedImports);
1734 if (!NT_SUCCESS(Status))
1735 {
1736 /* Fail */
1737 MiProcessLoaderEntry(LdrEntry, FALSE);
1738
1739 /* Check if we need to free the name */
1740 if (LdrEntry->FullDllName.Buffer)
1741 {
1742 /* Free it */
1743 ExFreePool(LdrEntry->FullDllName.Buffer);
1744 }
1745
1746 /* Free the entry itself */
1747 ExFreePool(LdrEntry);
1748 LdrEntry = NULL;
1749 goto Quickie;
1750 }
1751
1752 if (ModuleObject) *ModuleObject = LdrEntry;
1753 if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
1754
1755 /* Hook for KDB on loading a driver. */
1756 KDB_LOADDRIVER_HOOK(FileName, LdrEntry);
1757
1758 Quickie:
1759 /* If we have a file handle, close it */
1760 if (FileHandle) ZwClose(FileHandle);
1761
1762 /* Check if we have the lock acquired */
1763 if (LockOwned)
1764 {
1765 /* Release the lock */
1766 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1767 KeLeaveCriticalRegion();
1768 LockOwned = FALSE;
1769 }
1770
1771 /* Check if we had a prefix */
1772 if (NamePrefix) ExFreePool(PrefixName.Buffer);
1773
1774 /* Free the name buffer and return status */
1775 ExFreePool(Buffer);
1776 return Status;
1777 }
1778
1779 /*
1780 * @implemented
1781 */
1782 PVOID
1783 NTAPI
1784 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
1785 {
1786 PVOID ProcAddress = NULL;
1787 ANSI_STRING AnsiRoutineName;
1788 NTSTATUS Status;
1789 PLIST_ENTRY NextEntry;
1790 extern LIST_ENTRY PsLoadedModuleList;
1791 PLDR_DATA_TABLE_ENTRY LdrEntry;
1792 BOOLEAN Found = FALSE;
1793 UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
1794 UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
1795 ULONG Modules = 0;
1796
1797 /* Convert routine to ansi name */
1798 Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
1799 SystemRoutineName,
1800 TRUE);
1801 if (!NT_SUCCESS(Status)) return NULL;
1802
1803 /* Lock the list */
1804 KeEnterCriticalRegion();
1805
1806 /* Loop the loaded module list */
1807 NextEntry = PsLoadedModuleList.Flink;
1808 while (NextEntry != &PsLoadedModuleList)
1809 {
1810 /* Get the entry */
1811 LdrEntry = CONTAINING_RECORD(NextEntry,
1812 LDR_DATA_TABLE_ENTRY,
1813 InLoadOrderLinks);
1814
1815 /* Check if it's the kernel or HAL */
1816 if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
1817 {
1818 /* Found it */
1819 Found = TRUE;
1820 Modules++;
1821 }
1822 else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
1823 {
1824 /* Found it */
1825 Found = TRUE;
1826 Modules++;
1827 }
1828
1829 /* Check if we found a valid binary */
1830 if (Found)
1831 {
1832 /* Find the procedure name */
1833 ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
1834 &AnsiRoutineName);
1835
1836 /* Break out if we found it or if we already tried both modules */
1837 if (ProcAddress) break;
1838 if (Modules == 2) break;
1839 }
1840
1841 /* Keep looping */
1842 NextEntry = NextEntry->Flink;
1843 }
1844
1845 /* Release the lock */
1846 KeLeaveCriticalRegion();
1847
1848 /* Free the string and return */
1849 RtlFreeAnsiString(&AnsiRoutineName);
1850 return ProcAddress;
1851 }
1852