[NTOS]: Enable MmPageEntireDriver by implementing MiSetPagingOfDriver.
[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 * ReactOS Portable Systems Group
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #line 16 "ARMĀ³::LOADER"
17 #define MODULE_INVOLVED_IN_ARM3
18 #include "./ARM3/miarm.h"
19
20 /* GCC's incompetence strikes again */
21 __inline
22 VOID
23 sprintf_nt(IN PCHAR Buffer,
24 IN PCHAR Format,
25 IN ...)
26 {
27 va_list ap;
28 va_start(ap, Format);
29 vsprintf(Buffer, Format, ap);
30 va_end(ap);
31 }
32
33 /* GLOBALS ********************************************************************/
34
35 LIST_ENTRY PsLoadedModuleList;
36 LIST_ENTRY MmLoadedUserImageList;
37 KSPIN_LOCK PsLoadedModuleSpinLock;
38 ERESOURCE PsLoadedModuleResource;
39 ULONG_PTR PsNtosImageBase;
40 KMUTANT MmSystemLoadLock;
41
42 PFN_NUMBER MmTotalSystemDriverPages;
43
44 PVOID MmUnloadedDrivers;
45 PVOID MmLastUnloadedDrivers;
46
47 BOOLEAN MmMakeLowMemory;
48 BOOLEAN MmEnforceWriteProtection = TRUE;
49
50 /* FUNCTIONS ******************************************************************/
51
52 PVOID
53 NTAPI
54 MiCacheImageSymbols(IN PVOID BaseAddress)
55 {
56 ULONG DebugSize;
57 PVOID DebugDirectory = NULL;
58 PAGED_CODE();
59
60 /* Make sure it's safe to access the image */
61 _SEH2_TRY
62 {
63 /* Get the debug directory */
64 DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
65 TRUE,
66 IMAGE_DIRECTORY_ENTRY_DEBUG,
67 &DebugSize);
68 }
69 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
70 {
71 /* Nothing */
72 }
73 _SEH2_END;
74
75 /* Return the directory */
76 return DebugDirectory;
77 }
78
79 NTSTATUS
80 NTAPI
81 MiLoadImageSection(IN OUT PVOID *SectionPtr,
82 OUT PVOID *ImageBase,
83 IN PUNICODE_STRING FileName,
84 IN BOOLEAN SessionLoad,
85 IN PLDR_DATA_TABLE_ENTRY LdrEntry)
86 {
87 PROS_SECTION_OBJECT Section = *SectionPtr;
88 NTSTATUS Status;
89 PEPROCESS Process;
90 PVOID Base = NULL;
91 SIZE_T ViewSize = 0;
92 KAPC_STATE ApcState;
93 LARGE_INTEGER SectionOffset = {{0, 0}};
94 BOOLEAN LoadSymbols = FALSE;
95 PFN_NUMBER PteCount;
96 PMMPTE PointerPte, LastPte;
97 PVOID DriverBase;
98 MMPTE TempPte;
99 PAGED_CODE();
100
101 /* Detect session load */
102 if (SessionLoad)
103 {
104 /* Fail */
105 DPRINT1("Session loading not yet supported!\n");
106 while (TRUE);
107 }
108
109 /* Not session load, shouldn't have an entry */
110 ASSERT(LdrEntry == NULL);
111
112 /* Attach to the system process */
113 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
114
115 /* Check if we need to load symbols */
116 if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
117 {
118 /* Yes we do */
119 LoadSymbols = TRUE;
120 NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
121 }
122
123 /* Map the driver */
124 Process = PsGetCurrentProcess();
125 Status = MmMapViewOfSection(Section,
126 Process,
127 &Base,
128 0,
129 0,
130 &SectionOffset,
131 &ViewSize,
132 ViewUnmap,
133 0,
134 PAGE_EXECUTE);
135
136 /* Re-enable the flag */
137 if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
138
139 /* Check if we failed with distinguished status code */
140 if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
141 {
142 /* Change it to something more generic */
143 Status = STATUS_INVALID_IMAGE_FORMAT;
144 }
145
146 /* Now check if we failed */
147 if (!NT_SUCCESS(Status))
148 {
149 /* Detach and return */
150 KeUnstackDetachProcess(&ApcState);
151 return Status;
152 }
153
154 /* Reserve system PTEs needed */
155 PteCount = ROUND_TO_PAGES(Section->ImageSection->ImageSize) >> PAGE_SHIFT;
156 PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
157 if (!PointerPte) return STATUS_INSUFFICIENT_RESOURCES;
158
159 /* New driver base */
160 LastPte = PointerPte + PteCount;
161 DriverBase = MiPteToAddress(PointerPte);
162
163 /* The driver is here */
164 *ImageBase = DriverBase;
165
166 /* Loop the new driver PTEs */
167 TempPte = ValidKernelPte;
168 while (PointerPte < LastPte)
169 {
170 /* Allocate a page */
171 TempPte.u.Hard.PageFrameNumber = MmAllocPage(MC_NPPOOL);
172
173 /* Write it */
174 ASSERT(PointerPte->u.Hard.Valid == 0);
175 ASSERT(TempPte.u.Hard.Valid == 1);
176 *PointerPte = TempPte;
177
178 /* Move on */
179 PointerPte++;
180 }
181
182 /* Copy the image */
183 RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);
184
185 /* Now unmap the view */
186 Status = MmUnmapViewOfSection(Process, Base);
187 ASSERT(NT_SUCCESS(Status));
188
189 /* Detach and return status */
190 KeUnstackDetachProcess(&ApcState);
191 return Status;
192 }
193
194 NTSTATUS
195 NTAPI
196 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
197 {
198 SIZE_T i;
199
200 /* Check if there's no imports or if we're a boot driver */
201 if ((ImportList == (PVOID)-1) ||
202 (ImportList == (PVOID)-2) ||
203 (ImportList->Count == 0))
204 {
205 /* Then there's nothing to do */
206 return STATUS_SUCCESS;
207 }
208
209 /* Otherwise, FIXME */
210 DPRINT1("%u imports not dereferenced!\n", ImportList->Count);
211 for (i = 0; i < ImportList->Count; i++)
212 {
213 DPRINT1("%wZ <%wZ>\n", &ImportList->Entry[i]->FullDllName, &ImportList->Entry[i]->BaseDllName);
214 }
215 return STATUS_UNSUCCESSFUL;
216 }
217
218 VOID
219 NTAPI
220 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
221 {
222 PAGED_CODE();
223
224 /* Check if there's no imports or we're a boot driver or only one entry */
225 if ((LdrEntry->LoadedImports == (PVOID)-1) ||
226 (LdrEntry->LoadedImports == (PVOID)-2) ||
227 ((ULONG_PTR)LdrEntry->LoadedImports & 1))
228 {
229 /* Nothing to do */
230 return;
231 }
232
233 /* Otherwise, free the import list */
234 ExFreePool(LdrEntry->LoadedImports);
235 }
236
237 PVOID
238 NTAPI
239 MiFindExportedRoutineByName(IN PVOID DllBase,
240 IN PANSI_STRING ExportName)
241 {
242 PULONG NameTable;
243 PUSHORT OrdinalTable;
244 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
245 LONG Low = 0, Mid = 0, High, Ret;
246 USHORT Ordinal;
247 PVOID Function;
248 ULONG ExportSize;
249 PULONG ExportTable;
250 PAGED_CODE();
251
252 /* Get the export directory */
253 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
254 TRUE,
255 IMAGE_DIRECTORY_ENTRY_EXPORT,
256 &ExportSize);
257 if (!ExportDirectory) return NULL;
258
259 /* Setup name tables */
260 NameTable = (PULONG)((ULONG_PTR)DllBase +
261 ExportDirectory->AddressOfNames);
262 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
263 ExportDirectory->AddressOfNameOrdinals);
264
265 /* Do a binary search */
266 High = ExportDirectory->NumberOfNames - 1;
267 while (High >= Low)
268 {
269 /* Get new middle value */
270 Mid = (Low + High) >> 1;
271
272 /* Compare name */
273 Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
274 if (Ret < 0)
275 {
276 /* Update high */
277 High = Mid - 1;
278 }
279 else if (Ret > 0)
280 {
281 /* Update low */
282 Low = Mid + 1;
283 }
284 else
285 {
286 /* We got it */
287 break;
288 }
289 }
290
291 /* Check if we couldn't find it */
292 if (High < Low) return NULL;
293
294 /* Otherwise, this is the ordinal */
295 Ordinal = OrdinalTable[Mid];
296
297 /* Resolve the address and write it */
298 ExportTable = (PULONG)((ULONG_PTR)DllBase +
299 ExportDirectory->AddressOfFunctions);
300 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
301
302 /* We found it! */
303 ASSERT(!(Function > (PVOID)ExportDirectory) &&
304 (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
305 return Function;
306 }
307
308 PVOID
309 NTAPI
310 MiLocateExportName(IN PVOID DllBase,
311 IN PCHAR ExportName)
312 {
313 PULONG NameTable;
314 PUSHORT OrdinalTable;
315 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
316 LONG Low = 0, Mid = 0, High, Ret;
317 USHORT Ordinal;
318 PVOID Function;
319 ULONG ExportSize;
320 PULONG ExportTable;
321 PAGED_CODE();
322
323 /* Get the export directory */
324 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
325 TRUE,
326 IMAGE_DIRECTORY_ENTRY_EXPORT,
327 &ExportSize);
328 if (!ExportDirectory) return NULL;
329
330 /* Setup name tables */
331 NameTable = (PULONG)((ULONG_PTR)DllBase +
332 ExportDirectory->AddressOfNames);
333 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
334 ExportDirectory->AddressOfNameOrdinals);
335
336 /* Do a binary search */
337 High = ExportDirectory->NumberOfNames - 1;
338 while (High >= Low)
339 {
340 /* Get new middle value */
341 Mid = (Low + High) >> 1;
342
343 /* Compare name */
344 Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
345 if (Ret < 0)
346 {
347 /* Update high */
348 High = Mid - 1;
349 }
350 else if (Ret > 0)
351 {
352 /* Update low */
353 Low = Mid + 1;
354 }
355 else
356 {
357 /* We got it */
358 break;
359 }
360 }
361
362 /* Check if we couldn't find it */
363 if (High < Low) return NULL;
364
365 /* Otherwise, this is the ordinal */
366 Ordinal = OrdinalTable[Mid];
367
368 /* Resolve the address and write it */
369 ExportTable = (PULONG)((ULONG_PTR)DllBase +
370 ExportDirectory->AddressOfFunctions);
371 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
372
373 /* Check if the function is actually a forwarder */
374 if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
375 ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
376 {
377 /* It is, fail */
378 return NULL;
379 }
380
381 /* We found it */
382 return Function;
383 }
384
385 NTSTATUS
386 NTAPI
387 MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
388 IN PLIST_ENTRY ListHead)
389 {
390 UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
391 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
392 PMM_DLL_INITIALIZE DllInit;
393 UNICODE_STRING RegPath, ImportName;
394 NTSTATUS Status;
395
396 /* Try to see if the image exports a DllInitialize routine */
397 DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
398 "DllInitialize");
399 if (!DllInit) return STATUS_SUCCESS;
400
401 /* Do a temporary copy of BaseDllName called ImportName
402 * because we'll alter the length of the string
403 */
404 ImportName.Length = LdrEntry->BaseDllName.Length;
405 ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
406 ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
407
408 /* Obtain the path to this dll's service in the registry */
409 RegPath.MaximumLength = ServicesKeyName.Length +
410 ImportName.Length + sizeof(UNICODE_NULL);
411 RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
412 RegPath.MaximumLength,
413 TAG_LDR_WSTR);
414
415 /* Check if this allocation was unsuccessful */
416 if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
417
418 /* Build and append the service name itself */
419 RegPath.Length = ServicesKeyName.Length;
420 RtlCopyMemory(RegPath.Buffer,
421 ServicesKeyName.Buffer,
422 ServicesKeyName.Length);
423
424 /* Check if there is a dot in the filename */
425 if (wcschr(ImportName.Buffer, L'.'))
426 {
427 /* Remove the extension */
428 ImportName.Length = (wcschr(ImportName.Buffer, L'.') -
429 ImportName.Buffer) * sizeof(WCHAR);
430 }
431
432 /* Append service name (the basename without extension) */
433 RtlAppendUnicodeStringToString(&RegPath, &ImportName);
434
435 /* Now call the DllInit func */
436 DPRINT("Calling DllInit(%wZ)\n", &RegPath);
437 Status = DllInit(&RegPath);
438
439 /* Clean up */
440 ExFreePool(RegPath.Buffer);
441
442 /* Return status value which DllInitialize returned */
443 return Status;
444 }
445
446 VOID
447 NTAPI
448 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
449 IN BOOLEAN Insert)
450 {
451 KIRQL OldIrql;
452
453 /* Acquire module list lock */
454 KeEnterCriticalRegion();
455 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
456
457 /* Acquire the spinlock too as we will insert or remove the entry */
458 OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
459
460 /* Insert or remove from the list */
461 Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
462 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
463
464 /* Release locks */
465 KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
466 ExReleaseResourceLite(&PsLoadedModuleResource);
467 KeLeaveCriticalRegion();
468 }
469
470 VOID
471 NTAPI
472 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
473 IN PVOID OldBase,
474 IN PVOID NewBase,
475 IN ULONG Size)
476 {
477 ULONG_PTR OldBaseTop, Delta;
478 PLDR_DATA_TABLE_ENTRY LdrEntry;
479 PLIST_ENTRY NextEntry;
480 ULONG ImportSize;
481 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
482 PULONG ImageThunk;
483
484 /* Calculate the top and delta */
485 OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
486 Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
487
488 /* Loop the loader block */
489 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
490 NextEntry != &LoaderBlock->LoadOrderListHead;
491 NextEntry = NextEntry->Flink)
492 {
493 /* Get the loader entry */
494 LdrEntry = CONTAINING_RECORD(NextEntry,
495 LDR_DATA_TABLE_ENTRY,
496 InLoadOrderLinks);
497
498 /* Get the import table */
499 ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
500 TRUE,
501 IMAGE_DIRECTORY_ENTRY_IMPORT,
502 &ImportSize);
503 if (!ImportDescriptor) continue;
504
505 /* Make sure we have an IAT */
506 DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
507 while ((ImportDescriptor->Name) &&
508 (ImportDescriptor->OriginalFirstThunk))
509 {
510 /* Get the image thunk */
511 ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
512 ImportDescriptor->FirstThunk);
513 while (*ImageThunk)
514 {
515 /* Check if it's within this module */
516 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
517 {
518 /* Relocate it */
519 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
520 ImageThunk, *ImageThunk, *ImageThunk + Delta);
521 *ImageThunk += Delta;
522 }
523
524 /* Go to the next thunk */
525 ImageThunk++;
526 }
527
528 /* Go to the next import */
529 ImportDescriptor++;
530 }
531 }
532 }
533
534 NTSTATUS
535 NTAPI
536 MiSnapThunk(IN PVOID DllBase,
537 IN PVOID ImageBase,
538 IN PIMAGE_THUNK_DATA Name,
539 IN PIMAGE_THUNK_DATA Address,
540 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
541 IN ULONG ExportSize,
542 IN BOOLEAN SnapForwarder,
543 OUT PCHAR *MissingApi)
544 {
545 BOOLEAN IsOrdinal;
546 USHORT Ordinal;
547 PULONG NameTable;
548 PUSHORT OrdinalTable;
549 PIMAGE_IMPORT_BY_NAME NameImport;
550 USHORT Hint;
551 ULONG Low = 0, Mid = 0, High;
552 LONG Ret;
553 NTSTATUS Status;
554 PCHAR MissingForwarder;
555 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
556 PULONG ExportTable;
557 ANSI_STRING DllName;
558 UNICODE_STRING ForwarderName;
559 PLIST_ENTRY NextEntry;
560 PLDR_DATA_TABLE_ENTRY LdrEntry;
561 ULONG ForwardExportSize;
562 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
563 PIMAGE_IMPORT_BY_NAME ForwardName;
564 ULONG ForwardLength;
565 IMAGE_THUNK_DATA ForwardThunk;
566 PAGED_CODE();
567
568 /* Check if this is an ordinal */
569 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
570 if ((IsOrdinal) && !(SnapForwarder))
571 {
572 /* Get the ordinal number and set it as missing */
573 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
574 ExportDirectory->Base);
575 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
576 }
577 else
578 {
579 /* Get the VA if we don't have to snap */
580 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
581 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
582
583 /* Copy the procedure name */
584 strncpy(*MissingApi,
585 (PCHAR)&NameImport->Name[0],
586 MAXIMUM_FILENAME_LENGTH - 1);
587
588 /* Setup name tables */
589 DPRINT("Import name: %s\n", NameImport->Name);
590 NameTable = (PULONG)((ULONG_PTR)DllBase +
591 ExportDirectory->AddressOfNames);
592 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
593 ExportDirectory->AddressOfNameOrdinals);
594
595 /* Get the hint and check if it's valid */
596 Hint = NameImport->Hint;
597 if ((Hint < ExportDirectory->NumberOfNames) &&
598 !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
599 {
600 /* We have a match, get the ordinal number from here */
601 Ordinal = OrdinalTable[Hint];
602 }
603 else
604 {
605 /* Do a binary search */
606 High = ExportDirectory->NumberOfNames - 1;
607 while (High >= Low)
608 {
609 /* Get new middle value */
610 Mid = (Low + High) >> 1;
611
612 /* Compare name */
613 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
614 if (Ret < 0)
615 {
616 /* Update high */
617 High = Mid - 1;
618 }
619 else if (Ret > 0)
620 {
621 /* Update low */
622 Low = Mid + 1;
623 }
624 else
625 {
626 /* We got it */
627 break;
628 }
629 }
630
631 /* Check if we couldn't find it */
632 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
633
634 /* Otherwise, this is the ordinal */
635 Ordinal = OrdinalTable[Mid];
636 }
637 }
638
639 /* Check if the ordinal is invalid */
640 if (Ordinal >= ExportDirectory->NumberOfFunctions)
641 {
642 /* Fail */
643 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
644 }
645 else
646 {
647 /* In case the forwarder is missing */
648 MissingForwarder = NameBuffer;
649
650 /* Resolve the address and write it */
651 ExportTable = (PULONG)((ULONG_PTR)DllBase +
652 ExportDirectory->AddressOfFunctions);
653 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
654
655 /* Assume success from now on */
656 Status = STATUS_SUCCESS;
657
658 /* Check if the function is actually a forwarder */
659 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
660 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
661 {
662 /* Now assume failure in case the forwarder doesn't exist */
663 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
664
665 /* Build the forwarder name */
666 DllName.Buffer = (PCHAR)Address->u1.Function;
667 DllName.Length = strchr(DllName.Buffer, '.') -
668 DllName.Buffer +
669 sizeof(ANSI_NULL);
670 DllName.MaximumLength = DllName.Length;
671
672 /* Convert it */
673 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
674 &DllName,
675 TRUE)))
676 {
677 /* We failed, just return an error */
678 return Status;
679 }
680
681 /* Loop the module list */
682 NextEntry = PsLoadedModuleList.Flink;
683 while (NextEntry != &PsLoadedModuleList)
684 {
685 /* Get the loader entry */
686 LdrEntry = CONTAINING_RECORD(NextEntry,
687 LDR_DATA_TABLE_ENTRY,
688 InLoadOrderLinks);
689
690 /* Check if it matches */
691 if (RtlPrefixString((PSTRING)&ForwarderName,
692 (PSTRING)&LdrEntry->BaseDllName,
693 TRUE))
694 {
695 /* Get the forwarder export directory */
696 ForwardExportDirectory =
697 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
698 TRUE,
699 IMAGE_DIRECTORY_ENTRY_EXPORT,
700 &ForwardExportSize);
701 if (!ForwardExportDirectory) break;
702
703 /* Allocate a name entry */
704 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
705 sizeof(ANSI_NULL);
706 ForwardName = ExAllocatePoolWithTag(PagedPool,
707 sizeof(*ForwardName) +
708 ForwardLength,
709 TAG_LDR_WSTR);
710 if (!ForwardName) break;
711
712 /* Copy the data */
713 RtlCopyMemory(&ForwardName->Name[0],
714 DllName.Buffer + DllName.Length,
715 ForwardLength);
716 ForwardName->Hint = 0;
717
718 /* Set the new address */
719 ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
720
721 /* Snap the forwarder */
722 Status = MiSnapThunk(LdrEntry->DllBase,
723 ImageBase,
724 &ForwardThunk,
725 &ForwardThunk,
726 ForwardExportDirectory,
727 ForwardExportSize,
728 TRUE,
729 &MissingForwarder);
730
731 /* Free the forwarder name and set the thunk */
732 ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
733 Address->u1 = ForwardThunk.u1;
734 break;
735 }
736
737 /* Go to the next entry */
738 NextEntry = NextEntry->Flink;
739 }
740
741 /* Free the name */
742 RtlFreeUnicodeString(&ForwarderName);
743 }
744 }
745
746 /* Return status */
747 return Status;
748 }
749
750 NTSTATUS
751 NTAPI
752 MmUnloadSystemImage(IN PVOID ImageHandle)
753 {
754 PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
755 PVOID BaseAddress = LdrEntry->DllBase;
756 NTSTATUS Status;
757 STRING TempName;
758 BOOLEAN HadEntry = FALSE;
759
760 /* Acquire the loader lock */
761 KeEnterCriticalRegion();
762 KeWaitForSingleObject(&MmSystemLoadLock,
763 WrVirtualMemory,
764 KernelMode,
765 FALSE,
766 NULL);
767
768 /* Check if this driver was loaded at boot and didn't get imports parsed */
769 if (LdrEntry->LoadedImports == (PVOID)-1) goto Done;
770
771 /* We should still be alive */
772 ASSERT(LdrEntry->LoadCount != 0);
773 LdrEntry->LoadCount--;
774
775 /* Check if we're still loaded */
776 if (LdrEntry->LoadCount) goto Done;
777
778 /* We should cleanup... are symbols loaded */
779 if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
780 {
781 /* Create the ANSI name */
782 Status = RtlUnicodeStringToAnsiString(&TempName,
783 &LdrEntry->BaseDllName,
784 TRUE);
785 if (NT_SUCCESS(Status))
786 {
787 /* Unload the symbols */
788 DbgUnLoadImageSymbols(&TempName,
789 BaseAddress,
790 (ULONG_PTR)ZwCurrentProcess());
791 RtlFreeAnsiString(&TempName);
792 }
793 }
794
795 /* FIXME: Free the driver */
796 //MmFreeSection(LdrEntry->DllBase);
797
798 /* Check if we're linked in */
799 if (LdrEntry->InLoadOrderLinks.Flink)
800 {
801 /* Remove us */
802 MiProcessLoaderEntry(LdrEntry, FALSE);
803 HadEntry = TRUE;
804 }
805
806 /* Dereference and clear the imports */
807 MiDereferenceImports(LdrEntry->LoadedImports);
808 MiClearImports(LdrEntry);
809
810 /* Check if the entry needs to go away */
811 if (HadEntry)
812 {
813 /* Check if it had a name */
814 if (LdrEntry->FullDllName.Buffer)
815 {
816 /* Free it */
817 ExFreePool(LdrEntry->FullDllName.Buffer);
818 }
819
820 /* Check if we had a section */
821 if (LdrEntry->SectionPointer)
822 {
823 /* Dereference it */
824 ObDereferenceObject(LdrEntry->SectionPointer);
825 }
826
827 /* Free the entry */
828 ExFreePool(LdrEntry);
829 }
830
831 /* Release the system lock and return */
832 Done:
833 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
834 KeLeaveCriticalRegion();
835 return STATUS_SUCCESS;
836 }
837
838 NTSTATUS
839 NTAPI
840 MiResolveImageReferences(IN PVOID ImageBase,
841 IN PUNICODE_STRING ImageFileDirectory,
842 IN PUNICODE_STRING NamePrefix OPTIONAL,
843 OUT PCHAR *MissingApi,
844 OUT PWCHAR *MissingDriver,
845 OUT PLOAD_IMPORTS *LoadImports)
846 {
847 PCHAR MissingApiBuffer = *MissingApi, ImportName;
848 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
849 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
850 PLOAD_IMPORTS LoadedImports, NewImports;
851 ULONG GdiLink, NormalLink, i;
852 BOOLEAN ReferenceNeeded, Loaded;
853 ANSI_STRING TempString;
854 UNICODE_STRING NameString, DllName;
855 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
856 PVOID ImportBase, DllBase;
857 PLIST_ENTRY NextEntry;
858 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
859 NTSTATUS Status;
860 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
861 PAGED_CODE();
862 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
863 __FUNCTION__, ImageBase, ImageFileDirectory);
864
865 /* Assume no imports */
866 *LoadImports = (PVOID)-2;
867
868 /* Get the import descriptor */
869 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
870 TRUE,
871 IMAGE_DIRECTORY_ENTRY_IMPORT,
872 &ImportSize);
873 if (!ImportDescriptor) return STATUS_SUCCESS;
874
875 /* Loop all imports to count them */
876 for (CurrentImport = ImportDescriptor;
877 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
878 CurrentImport++)
879 {
880 /* One more */
881 ImportCount++;
882 }
883
884 /* Make sure we have non-zero imports */
885 if (ImportCount)
886 {
887 /* Calculate and allocate the list we'll need */
888 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
889 LoadedImports = ExAllocatePoolWithTag(PagedPool,
890 LoadedImportsSize,
891 TAG_LDR_WSTR);
892 if (LoadedImports)
893 {
894 /* Zero it */
895 RtlZeroMemory(LoadedImports, LoadedImportsSize);
896 }
897 }
898 else
899 {
900 /* No table */
901 LoadedImports = NULL;
902 }
903
904 /* Reset the import count and loop descriptors again */
905 ImportCount = GdiLink = NormalLink = 0;
906 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
907 {
908 /* Get the name */
909 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
910
911 /* Check if this is a GDI driver */
912 GdiLink = GdiLink |
913 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
914
915 /* We can also allow dxapi */
916 NormalLink = NormalLink |
917 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
918 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
919
920 /* Check if this is a valid GDI driver */
921 if ((GdiLink) && (NormalLink))
922 {
923 /* It's not, it's importing stuff it shouldn't be! */
924 MiDereferenceImports(LoadedImports);
925 if (LoadedImports) ExFreePool(LoadedImports);
926 return STATUS_PROCEDURE_NOT_FOUND;
927 }
928
929 /* Check if this is a "core" import, which doesn't get referenced */
930 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
931 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
932 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
933 {
934 /* Don't reference this */
935 ReferenceNeeded = FALSE;
936 }
937 else
938 {
939 /* Reference these modules */
940 ReferenceNeeded = TRUE;
941 }
942
943 /* Now setup a unicode string for the import */
944 RtlInitAnsiString(&TempString, ImportName);
945 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
946 if (!NT_SUCCESS(Status))
947 {
948 /* Failed */
949 MiDereferenceImports(LoadedImports);
950 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
951 return Status;
952 }
953
954 /* We don't support name prefixes yet */
955 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
956
957 /* Remember that we haven't loaded the import at this point */
958 CheckDllState:
959 Loaded = FALSE;
960 ImportBase = NULL;
961
962 /* Loop the driver list */
963 NextEntry = PsLoadedModuleList.Flink;
964 while (NextEntry != &PsLoadedModuleList)
965 {
966 /* Get the loader entry and compare the name */
967 LdrEntry = CONTAINING_RECORD(NextEntry,
968 LDR_DATA_TABLE_ENTRY,
969 InLoadOrderLinks);
970 if (RtlEqualUnicodeString(&NameString,
971 &LdrEntry->BaseDllName,
972 TRUE))
973 {
974 /* Get the base address */
975 ImportBase = LdrEntry->DllBase;
976
977 /* Check if we haven't loaded yet, and we need references */
978 if (!(Loaded) && (ReferenceNeeded))
979 {
980 /* Make sure we're not already loading */
981 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
982 {
983 /* Increase the load count */
984 LdrEntry->LoadCount++;
985 }
986 }
987
988 /* Done, break out */
989 break;
990 }
991
992 /* Go to the next entry */
993 NextEntry = NextEntry->Flink;
994 }
995
996 /* Check if we haven't loaded the import yet */
997 if (!ImportBase)
998 {
999 /* Setup the import DLL name */
1000 DllName.MaximumLength = NameString.Length +
1001 ImageFileDirectory->Length +
1002 sizeof(UNICODE_NULL);
1003 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
1004 DllName.MaximumLength,
1005 TAG_LDR_WSTR);
1006 if (DllName.Buffer)
1007 {
1008 /* Setup the base length and copy it */
1009 DllName.Length = ImageFileDirectory->Length;
1010 RtlCopyMemory(DllName.Buffer,
1011 ImageFileDirectory->Buffer,
1012 ImageFileDirectory->Length);
1013
1014 /* Now add the import name and null-terminate it */
1015 RtlAppendStringToString((PSTRING)&DllName,
1016 (PSTRING)&NameString);
1017 DllName.Buffer[(DllName.MaximumLength - 1) / sizeof(WCHAR)] = UNICODE_NULL;
1018
1019 /* Load the image */
1020 Status = MmLoadSystemImage(&DllName,
1021 NamePrefix,
1022 NULL,
1023 0,
1024 (PVOID)&DllEntry,
1025 &DllBase);
1026 if (NT_SUCCESS(Status))
1027 {
1028 /* We can free the DLL Name */
1029 ExFreePool(DllName.Buffer);
1030 }
1031 else
1032 {
1033 /* Fill out the information for the error */
1034 *MissingDriver = DllName.Buffer;
1035 *(PULONG)MissingDriver |= 1;
1036 *MissingApi = NULL;
1037 }
1038 }
1039 else
1040 {
1041 /* We're out of resources */
1042 Status = STATUS_INSUFFICIENT_RESOURCES;
1043 }
1044
1045 /* Check if we're OK until now */
1046 if (NT_SUCCESS(Status))
1047 {
1048 /* We're now loaded */
1049 Loaded = TRUE;
1050
1051 /* Sanity check */
1052 ASSERT(DllBase = DllEntry->DllBase);
1053
1054 /* Call the initialization routines */
1055 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
1056 if (!NT_SUCCESS(Status))
1057 {
1058 /* We failed, unload the image */
1059 MmUnloadSystemImage(DllEntry);
1060 while (TRUE);
1061 Loaded = FALSE;
1062 }
1063 }
1064
1065 /* Check if we failed by here */
1066 if (!NT_SUCCESS(Status))
1067 {
1068 /* Cleanup and return */
1069 RtlFreeUnicodeString(&NameString);
1070 MiDereferenceImports(LoadedImports);
1071 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1072 return Status;
1073 }
1074
1075 /* Loop again to make sure that everything is OK */
1076 goto CheckDllState;
1077 }
1078
1079 /* Check if we're support to reference this import */
1080 if ((ReferenceNeeded) && (LoadedImports))
1081 {
1082 /* Make sure we're not already loading */
1083 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1084 {
1085 /* Add the entry */
1086 LoadedImports->Entry[LoadedImports->Count] = LdrEntry;
1087 LoadedImports->Count++;
1088 }
1089 }
1090
1091 /* Free the import name */
1092 RtlFreeUnicodeString(&NameString);
1093
1094 /* Set the missing driver name and get the export directory */
1095 *MissingDriver = LdrEntry->BaseDllName.Buffer;
1096 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
1097 TRUE,
1098 IMAGE_DIRECTORY_ENTRY_EXPORT,
1099 &ExportSize);
1100 if (!ExportDirectory)
1101 {
1102 /* Cleanup and return */
1103 MiDereferenceImports(LoadedImports);
1104 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1105 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
1106 }
1107
1108 /* Make sure we have an IAT */
1109 if (ImportDescriptor->OriginalFirstThunk)
1110 {
1111 /* Get the first thunks */
1112 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
1113 ImportDescriptor->OriginalFirstThunk);
1114 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
1115 ImportDescriptor->FirstThunk);
1116
1117 /* Loop the IAT */
1118 while (OrigThunk->u1.AddressOfData)
1119 {
1120 /* Snap thunk */
1121 Status = MiSnapThunk(ImportBase,
1122 ImageBase,
1123 OrigThunk++,
1124 FirstThunk++,
1125 ExportDirectory,
1126 ExportSize,
1127 FALSE,
1128 MissingApi);
1129 if (!NT_SUCCESS(Status))
1130 {
1131 /* Cleanup and return */
1132 MiDereferenceImports(LoadedImports);
1133 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1134 return Status;
1135 }
1136
1137 /* Reset the buffer */
1138 *MissingApi = MissingApiBuffer;
1139 }
1140 }
1141
1142 /* Go to the next import */
1143 ImportDescriptor++;
1144 }
1145
1146 /* Check if we have an import list */
1147 if (LoadedImports)
1148 {
1149 /* Reset the count again, and loop entries*/
1150 ImportCount = 0;
1151 for (i = 0; i < LoadedImports->Count; i++)
1152 {
1153 if (LoadedImports->Entry[i])
1154 {
1155 /* Got an entry, OR it with 1 in case it's the single entry */
1156 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
1157 ImportCount++;
1158 }
1159 }
1160
1161 /* Check if we had no imports */
1162 if (!ImportCount)
1163 {
1164 /* Free the list and set it to no imports */
1165 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1166 LoadedImports = (PVOID)-2;
1167 }
1168 else if (ImportCount == 1)
1169 {
1170 /* Just one entry, we can free the table and only use our entry */
1171 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1172 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1173 }
1174 else if (ImportCount != LoadedImports->Count)
1175 {
1176 /* Allocate a new list */
1177 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1178 NewImports = ExAllocatePoolWithTag(PagedPool,
1179 LoadedImportsSize,
1180 TAG_LDR_WSTR);
1181 if (NewImports)
1182 {
1183 /* Set count */
1184 NewImports->Count = 0;
1185
1186 /* Loop all the imports */
1187 for (i = 0; i < LoadedImports->Count; i++)
1188 {
1189 /* Make sure it's valid */
1190 if (LoadedImports->Entry[i])
1191 {
1192 /* Copy it */
1193 NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
1194 NewImports->Count++;
1195 }
1196 }
1197
1198 /* Free the old copy */
1199 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1200 LoadedImports = NewImports;
1201 }
1202 }
1203
1204 /* Return the list */
1205 *LoadImports = LoadedImports;
1206 }
1207
1208 /* Return success */
1209 return STATUS_SUCCESS;
1210 }
1211
1212 VOID
1213 NTAPI
1214 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1215 {
1216 PLIST_ENTRY NextEntry;
1217 ULONG i = 0;
1218 PIMAGE_NT_HEADERS NtHeader;
1219 PLDR_DATA_TABLE_ENTRY LdrEntry;
1220 PIMAGE_FILE_HEADER FileHeader;
1221 BOOLEAN ValidRelocs;
1222 PIMAGE_DATA_DIRECTORY DataDirectory;
1223 PVOID DllBase, NewImageAddress;
1224 NTSTATUS Status;
1225 PMMPTE PointerPte, StartPte, LastPte;
1226 PFN_NUMBER PteCount;
1227 PMMPFN Pfn1;
1228 MMPTE TempPte, OldPte;
1229
1230 /* Loop driver list */
1231 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1232 NextEntry != &LoaderBlock->LoadOrderListHead;
1233 NextEntry = NextEntry->Flink)
1234 {
1235 /* Get the loader entry and NT header */
1236 LdrEntry = CONTAINING_RECORD(NextEntry,
1237 LDR_DATA_TABLE_ENTRY,
1238 InLoadOrderLinks);
1239 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1240
1241 /* Debug info */
1242 DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1243 LdrEntry->DllBase,
1244 (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
1245 &LdrEntry->FullDllName);
1246
1247 /* Skip kernel and HAL */
1248 /* ROS HACK: Skip BOOTVID/KDCOM too */
1249 i++;
1250 if (i <= 4) continue;
1251
1252 /* Skip non-drivers */
1253 if (!NtHeader) continue;
1254
1255 /* Get the file header and make sure we can relocate */
1256 FileHeader = &NtHeader->FileHeader;
1257 if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1258 if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1259 IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1260
1261 /* Everything made sense until now, check the relocation section too */
1262 DataDirectory = &NtHeader->OptionalHeader.
1263 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1264 if (!DataDirectory->VirtualAddress)
1265 {
1266 /* We don't really have relocations */
1267 ValidRelocs = FALSE;
1268 }
1269 else
1270 {
1271 /* Make sure the size is valid */
1272 if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1273 LdrEntry->SizeOfImage)
1274 {
1275 /* They're not, skip */
1276 continue;
1277 }
1278
1279 /* We have relocations */
1280 ValidRelocs = TRUE;
1281 }
1282
1283 /* Remember the original address */
1284 DllBase = LdrEntry->DllBase;
1285
1286 /* Get the first PTE and the number of PTEs we'll need */
1287 PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
1288 PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
1289 LastPte = StartPte + PteCount;
1290
1291 /* Loop the PTEs */
1292 while (PointerPte < LastPte)
1293 {
1294 /* Mark the page modified in the PFN database */
1295 ASSERT(PointerPte->u.Hard.Valid == 1);
1296 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1297 ASSERT(Pfn1->u3.e1.Rom == 0);
1298 Pfn1->u3.e1.Modified = TRUE;
1299
1300 /* Next */
1301 PointerPte++;
1302 }
1303
1304 /* Now reserve system PTEs for the image */
1305 PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
1306 if (!PointerPte)
1307 {
1308 /* Shouldn't happen */
1309 DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
1310 while (TRUE);
1311 }
1312
1313 /* This is the new virtual address for the module */
1314 LastPte = PointerPte + PteCount;
1315 NewImageAddress = MiPteToAddress(PointerPte);
1316
1317 /* Sanity check */
1318 DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1319 ASSERT(ExpInitializationPhase == 0);
1320
1321 /* Loop the new driver PTEs */
1322 TempPte = ValidKernelPte;
1323 while (PointerPte < LastPte)
1324 {
1325 /* Copy the old data */
1326 OldPte = *StartPte;
1327 ASSERT(OldPte.u.Hard.Valid == 1);
1328
1329 /* Set page number from the loader's memory */
1330 TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
1331
1332 /* Write it */
1333 ASSERT(PointerPte->u.Hard.Valid == 0);
1334 ASSERT(TempPte.u.Hard.Valid == 1);
1335 *PointerPte = TempPte;
1336
1337 /* Move on */
1338 PointerPte++;
1339 StartPte++;
1340 }
1341
1342 /* Update position */
1343 PointerPte -= PteCount;
1344
1345 /* Sanity check */
1346 ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1347
1348 /* Set the image base to the address where the loader put it */
1349 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1350
1351 /* Check if we had relocations */
1352 if (ValidRelocs)
1353 {
1354 /* Relocate the image */
1355 Status = LdrRelocateImageWithBias(NewImageAddress,
1356 0,
1357 "SYSLDR",
1358 STATUS_SUCCESS,
1359 STATUS_CONFLICTING_ADDRESSES,
1360 STATUS_INVALID_IMAGE_FORMAT);
1361 if (!NT_SUCCESS(Status))
1362 {
1363 /* This shouldn't happen */
1364 DPRINT1("Relocations failed!\n");
1365 while (TRUE);
1366 }
1367 }
1368
1369 /* Update the loader entry */
1370 LdrEntry->DllBase = NewImageAddress;
1371
1372 /* Update the thunks */
1373 DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1374 MiUpdateThunks(LoaderBlock,
1375 DllBase,
1376 NewImageAddress,
1377 LdrEntry->SizeOfImage);
1378
1379 /* Update the loader entry */
1380 LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
1381 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1382 NtHeader->OptionalHeader.AddressOfEntryPoint);
1383 LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
1384
1385 /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
1386 }
1387 }
1388
1389 BOOLEAN
1390 NTAPI
1391 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1392 {
1393 PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
1394 PLIST_ENTRY ListHead, NextEntry;
1395 ULONG EntrySize;
1396
1397 /* Setup the loaded module list and locks */
1398 ExInitializeResourceLite(&PsLoadedModuleResource);
1399 KeInitializeSpinLock(&PsLoadedModuleSpinLock);
1400 InitializeListHead(&PsLoadedModuleList);
1401
1402 /* Get loop variables and the kernel entry */
1403 ListHead = &LoaderBlock->LoadOrderListHead;
1404 NextEntry = ListHead->Flink;
1405 LdrEntry = CONTAINING_RECORD(NextEntry,
1406 LDR_DATA_TABLE_ENTRY,
1407 InLoadOrderLinks);
1408 PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
1409
1410 /* Loop the loader block */
1411 while (NextEntry != ListHead)
1412 {
1413 /* Get the loader entry */
1414 LdrEntry = CONTAINING_RECORD(NextEntry,
1415 LDR_DATA_TABLE_ENTRY,
1416 InLoadOrderLinks);
1417
1418 /* FIXME: ROS HACK. Make sure this is a driver */
1419 if (!RtlImageNtHeader(LdrEntry->DllBase))
1420 {
1421 /* Skip this entry */
1422 NextEntry= NextEntry->Flink;
1423 continue;
1424 }
1425
1426 /* Calculate the size we'll need and allocate a copy */
1427 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1428 LdrEntry->BaseDllName.MaximumLength +
1429 sizeof(UNICODE_NULL);
1430 NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
1431 if (!NewEntry) return FALSE;
1432
1433 /* Copy the entry over */
1434 *NewEntry = *LdrEntry;
1435
1436 /* Allocate the name */
1437 NewEntry->FullDllName.Buffer =
1438 ExAllocatePoolWithTag(PagedPool,
1439 LdrEntry->FullDllName.MaximumLength +
1440 sizeof(UNICODE_NULL),
1441 TAG_LDR_WSTR);
1442 if (!NewEntry->FullDllName.Buffer) return FALSE;
1443
1444 /* Set the base name */
1445 NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
1446
1447 /* Copy the full and base name */
1448 RtlCopyMemory(NewEntry->FullDllName.Buffer,
1449 LdrEntry->FullDllName.Buffer,
1450 LdrEntry->FullDllName.MaximumLength);
1451 RtlCopyMemory(NewEntry->BaseDllName.Buffer,
1452 LdrEntry->BaseDllName.Buffer,
1453 LdrEntry->BaseDllName.MaximumLength);
1454
1455 /* Null-terminate the base name */
1456 NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
1457 sizeof(WCHAR)] = UNICODE_NULL;
1458
1459 /* Insert the entry into the list */
1460 InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
1461 NextEntry = NextEntry->Flink;
1462 }
1463
1464 /* Build the import lists for the boot drivers */
1465 //MiBuildImportsForBootDrivers();
1466
1467 /* We're done */
1468 return TRUE;
1469 }
1470
1471 ULONG
1472 NTAPI
1473 MiComputeDriverProtection(IN BOOLEAN SessionSpace,
1474 IN ULONG SectionProtection)
1475 {
1476 ULONG Protection = MM_ZERO_ACCESS;
1477
1478 /* Check if the caller gave anything */
1479 if (SectionProtection)
1480 {
1481 /* Always turn on execute access */
1482 SectionProtection |= IMAGE_SCN_MEM_EXECUTE;
1483
1484 /* Check if the registry setting is on or not */
1485 if (!MmEnforceWriteProtection)
1486 {
1487 /* Turn on write access too */
1488 SectionProtection |= (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE);
1489 }
1490 }
1491
1492 /* Convert to internal PTE flags */
1493 if (SectionProtection & IMAGE_SCN_MEM_EXECUTE) Protection |= MM_EXECUTE;
1494 if (SectionProtection & IMAGE_SCN_MEM_READ) Protection |= MM_READONLY;
1495
1496 /* Check for write access */
1497 if (SectionProtection & IMAGE_SCN_MEM_WRITE)
1498 {
1499 /* Session space is not supported */
1500 if (SessionSpace)
1501 {
1502 DPRINT1("Session drivers not supported\n");
1503 ASSERT(SessionSpace == FALSE);
1504 }
1505 else
1506 {
1507 /* Convert to internal PTE flag */
1508 Protection = (Protection & MM_EXECUTE) ? MM_EXECUTE_READWRITE : MM_READWRITE;
1509 }
1510 }
1511
1512 /* If there's no access at all by now, convert to internal no access flag */
1513 if (Protection == MM_ZERO_ACCESS) Protection = MM_NOACCESS;
1514
1515 /* Return the computed PTE protection */
1516 return Protection;
1517 }
1518
1519 VOID
1520 NTAPI
1521 MiSetSystemCodeProtection(IN PMMPTE FirstPte,
1522 IN PMMPTE LastPte,
1523 IN ULONG ProtectionMask)
1524 {
1525 /* I'm afraid to introduce regressions at the moment... */
1526 return;
1527 }
1528
1529 VOID
1530 NTAPI
1531 MiWriteProtectSystemImage(IN PVOID ImageBase)
1532 {
1533 PIMAGE_NT_HEADERS NtHeaders;
1534 PIMAGE_SECTION_HEADER Section;
1535 PFN_NUMBER DriverPages;
1536 ULONG CurrentProtection, SectionProtection, CombinedProtection, ProtectionMask;
1537 ULONG Sections, Size;
1538 ULONG_PTR BaseAddress, CurrentAddress;
1539 PMMPTE PointerPte, StartPte, LastPte, CurrentPte, ComboPte = NULL;
1540 ULONG CurrentMask, CombinedMask = 0;
1541 PAGED_CODE();
1542
1543 /* No need to write protect physical memory-backed drivers (large pages) */
1544 if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
1545
1546 /* Get the image headers */
1547 NtHeaders = RtlImageNtHeader(ImageBase);
1548 if (!NtHeaders) return;
1549
1550 /* Check if this is a session driver or not */
1551 if (!MI_IS_SESSION_ADDRESS(ImageBase))
1552 {
1553 /* Don't touch NT4 drivers */
1554 if (NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) return;
1555 if (NtHeaders->OptionalHeader.MajorImageVersion < 5) return;
1556 }
1557 else
1558 {
1559 /* Not supported */
1560 DPRINT1("Session drivers not supported\n");
1561 ASSERT(FALSE);
1562 }
1563
1564 /* These are the only protection masks we care about */
1565 ProtectionMask = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
1566
1567 /* Calculate the number of pages this driver is occupying */
1568 DriverPages = BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
1569
1570 /* Get the number of sections and the first section header */
1571 Sections = NtHeaders->FileHeader.NumberOfSections;
1572 ASSERT(Sections != 0);
1573 Section = IMAGE_FIRST_SECTION(NtHeaders);
1574
1575 /* Loop all the sections */
1576 CurrentAddress = (ULONG_PTR)ImageBase;
1577 while (Sections)
1578 {
1579 /* Get the section size */
1580 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1581
1582 /* Get its virtual address */
1583 BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
1584 if (BaseAddress < CurrentAddress)
1585 {
1586 /* Windows doesn't like these */
1587 DPRINT1("Badly linked image!\n");
1588 return;
1589 }
1590
1591 /* Remember the current address */
1592 CurrentAddress = BaseAddress + Size - 1;
1593
1594 /* Next */
1595 Sections--;
1596 Section++;
1597 }
1598
1599 /* Get the number of sections and the first section header */
1600 Sections = NtHeaders->FileHeader.NumberOfSections;
1601 ASSERT(Sections != 0);
1602 Section = IMAGE_FIRST_SECTION(NtHeaders);
1603
1604 /* Set the address at the end to initialize the loop */
1605 CurrentAddress = (ULONG_PTR)Section + Sections - 1;
1606 CurrentProtection = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
1607
1608 /* Set the PTE points for the image, and loop its sections */
1609 StartPte = MiAddressToPte(ImageBase);
1610 LastPte = StartPte + DriverPages;
1611 while (Sections)
1612 {
1613 /* Get the section size */
1614 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1615
1616 /* Get its virtual address and PTE */
1617 BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
1618 PointerPte = MiAddressToPte(BaseAddress);
1619
1620 /* Check if we were already protecting a run, and found a new run */
1621 if ((ComboPte) && (PointerPte > ComboPte))
1622 {
1623 /* Compute protection */
1624 CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
1625
1626 /* Set it */
1627 MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
1628
1629 /* Check for overlap */
1630 if (ComboPte == StartPte) StartPte++;
1631
1632 /* One done, reset variables */
1633 ComboPte = NULL;
1634 CombinedProtection = 0;
1635 }
1636
1637 /* Break out when needed */
1638 if (PointerPte >= LastPte) break;
1639
1640 /* Get the requested protection from the image header */
1641 SectionProtection = Section->Characteristics & ProtectionMask;
1642 if (SectionProtection == CurrentProtection)
1643 {
1644 /* Same protection, so merge the request */
1645 CurrentAddress = BaseAddress + Size - 1;
1646 continue;
1647 }
1648
1649 /* This is now a new section, so close up the old one */
1650 CurrentPte = MiAddressToPte(CurrentAddress);
1651
1652 /* Check for overlap */
1653 if (CurrentPte == PointerPte)
1654 {
1655 /* Skip the last PTE, since it overlaps with us */
1656 CurrentPte--;
1657
1658 /* And set the PTE we will merge with */
1659 ASSERT((ComboPte == NULL) || (ComboPte == PointerPte));
1660 ComboPte = PointerPte;
1661
1662 /* Get the most flexible protection by merging both */
1663 CombinedMask |= (SectionProtection | CurrentProtection);
1664 }
1665
1666 /* Loop any PTEs left */
1667 if (CurrentPte >= StartPte)
1668 {
1669 /* Sanity check */
1670 ASSERT(StartPte < LastPte);
1671
1672 /* Make sure we don't overflow past the last PTE in the driver */
1673 if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
1674 ASSERT(CurrentPte >= StartPte);
1675
1676 /* Compute the protection and set it */
1677 CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
1678 MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
1679 }
1680
1681 /* Set new state */
1682 StartPte = PointerPte;
1683 CurrentAddress = BaseAddress + Size - 1;
1684 CurrentProtection = SectionProtection;
1685
1686 /* Next */
1687 Sections--;
1688 Section++;
1689 }
1690
1691 /* Is there a leftover section to merge? */
1692 if (ComboPte)
1693 {
1694 /* Compute and set the protection */
1695 CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
1696 MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
1697
1698 /* Handle overlap */
1699 if (ComboPte == StartPte) StartPte++;
1700 }
1701
1702 /* Finally, handle the last section */
1703 CurrentPte = MiPteToAddress(CurrentAddress);
1704 if ((StartPte < LastPte) && (CurrentPte >= StartPte))
1705 {
1706 /* Handle overlap */
1707 if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
1708 ASSERT(CurrentPte >= StartPte);
1709
1710 /* Compute and set the protection */
1711 CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
1712 MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
1713 }
1714 }
1715
1716 VOID
1717 NTAPI
1718 MiSetPagingOfDriver(IN PMMPTE PointerPte,
1719 IN PMMPTE LastPte)
1720 {
1721 PVOID ImageBase;
1722 PETHREAD CurrentThread;
1723 PFN_NUMBER PageCount = 0, PageFrameIndex;
1724 PMMPFN Pfn1;
1725 PAGED_CODE();
1726
1727 /* Get the driver's base address */
1728 ImageBase = MiPteToAddress(PointerPte);
1729 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
1730
1731 /* If this is a large page, it's stuck in physical memory */
1732 if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
1733
1734 /* We should lock the system working set -- we don't have one yet, so just be consistent */
1735 CurrentThread = PsGetCurrentThread();
1736 KeEnterGuardedRegion();
1737 ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
1738 (CurrentThread->OwnsSystemWorkingSetShared == 0));
1739 CurrentThread->OwnsSystemWorkingSetExclusive = 1;
1740
1741 /* Loop the PTEs */
1742 while (PointerPte <= LastPte)
1743 {
1744 /* Check for valid PTE */
1745 if (PointerPte->u.Hard.Valid == 1)
1746 {
1747 PageFrameIndex = PFN_FROM_PTE(PointerPte);
1748 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1749 ASSERT(Pfn1->u2.ShareCount == 1);
1750
1751 /* No working sets in ReactOS yet */
1752 PageCount++;
1753 }
1754
1755 ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
1756 PointerPte++;
1757 }
1758
1759 /* Release the working set "lock" */
1760 ASSERT(KeAreAllApcsDisabled() == TRUE);
1761 CurrentThread->OwnsSystemWorkingSetExclusive = 0;
1762 KeLeaveGuardedRegion();
1763
1764 /* Do we have any driver pages? */
1765 if (PageCount)
1766 {
1767 /* Update counters */
1768 InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
1769 }
1770 }
1771
1772 VOID
1773 NTAPI
1774 MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
1775 {
1776 ULONG_PTR ImageBase;
1777 PIMAGE_NT_HEADERS NtHeaders;
1778 ULONG Sections, Alignment, Size;
1779 PIMAGE_SECTION_HEADER Section;
1780 PMMPTE PointerPte = NULL, LastPte = NULL;
1781 if (MmDisablePagingExecutive) return;
1782
1783 /* Get the driver base address and its NT header */
1784 ImageBase = (ULONG_PTR)LdrEntry->DllBase;
1785 NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
1786 if (!NtHeaders) return;
1787
1788 /* Get the sections and their alignment */
1789 Sections = NtHeaders->FileHeader.NumberOfSections;
1790 Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
1791
1792 /* Loop each section */
1793 Section = IMAGE_FIRST_SECTION(NtHeaders);
1794 while (Sections)
1795 {
1796 /* Find PAGE or .edata */
1797 if ((*(PULONG)Section->Name == 'EGAP') ||
1798 (*(PULONG)Section->Name == 'ade.'))
1799 {
1800 /* Had we already done some work? */
1801 if (!PointerPte)
1802 {
1803 /* Nope, setup the first PTE address */
1804 PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
1805 Section->
1806 VirtualAddress));
1807 }
1808
1809 /* Compute the size */
1810 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1811
1812 /* Find the last PTE that maps this section */
1813 LastPte = MiAddressToPte(ImageBase +
1814 Section->VirtualAddress +
1815 Alignment +
1816 Size -
1817 PAGE_SIZE);
1818 }
1819 else
1820 {
1821 /* Had we found a section before? */
1822 if (PointerPte)
1823 {
1824 /* Mark it as pageable */
1825 MiSetPagingOfDriver(PointerPte, LastPte);
1826 PointerPte = NULL;
1827 }
1828 }
1829
1830 /* Keep searching */
1831 Sections--;
1832 Section++;
1833 }
1834
1835 /* Handle the straggler */
1836 if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
1837 }
1838
1839 BOOLEAN
1840 NTAPI
1841 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
1842 {
1843 PIMAGE_NT_HEADERS NtHeader;
1844 PAGED_CODE();
1845
1846 /* Get NT Headers */
1847 NtHeader = RtlImageNtHeader(BaseAddress);
1848 if (NtHeader)
1849 {
1850 /* Check if this image is only safe for UP while we have 2+ CPUs */
1851 if ((KeNumberProcessors > 1) &&
1852 (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
1853 {
1854 /* Fail */
1855 return FALSE;
1856 }
1857 }
1858
1859 /* Otherwise, it's safe */
1860 return TRUE;
1861 }
1862
1863 NTSTATUS
1864 NTAPI
1865 MmCheckSystemImage(IN HANDLE ImageHandle,
1866 IN BOOLEAN PurgeSection)
1867 {
1868 NTSTATUS Status;
1869 HANDLE SectionHandle;
1870 PVOID ViewBase = NULL;
1871 SIZE_T ViewSize = 0;
1872 IO_STATUS_BLOCK IoStatusBlock;
1873 FILE_STANDARD_INFORMATION FileStandardInfo;
1874 KAPC_STATE ApcState;
1875 PAGED_CODE();
1876
1877 /* Create a section for the DLL */
1878 Status = ZwCreateSection(&SectionHandle,
1879 SECTION_MAP_EXECUTE,
1880 NULL,
1881 NULL,
1882 PAGE_EXECUTE,
1883 SEC_COMMIT,
1884 ImageHandle);
1885 if (!NT_SUCCESS(Status)) return Status;
1886
1887 /* Make sure we're in the system process */
1888 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
1889
1890 /* Map it */
1891 Status = ZwMapViewOfSection(SectionHandle,
1892 NtCurrentProcess(),
1893 &ViewBase,
1894 0,
1895 0,
1896 NULL,
1897 &ViewSize,
1898 ViewShare,
1899 0,
1900 PAGE_EXECUTE);
1901 if (!NT_SUCCESS(Status))
1902 {
1903 /* We failed, close the handle and return */
1904 KeUnstackDetachProcess(&ApcState);
1905 ZwClose(SectionHandle);
1906 return Status;
1907 }
1908
1909 /* Now query image information */
1910 Status = ZwQueryInformationFile(ImageHandle,
1911 &IoStatusBlock,
1912 &FileStandardInfo,
1913 sizeof(FileStandardInfo),
1914 FileStandardInformation);
1915 if ( NT_SUCCESS(Status) )
1916 {
1917 /* First, verify the checksum */
1918 if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
1919 FileStandardInfo.
1920 EndOfFile.LowPart,
1921 FileStandardInfo.
1922 EndOfFile.LowPart))
1923 {
1924 /* Set checksum failure */
1925 Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1926 }
1927
1928 /* Check that it's a valid SMP image if we have more then one CPU */
1929 if (!MmVerifyImageIsOkForMpUse(ViewBase))
1930 {
1931 /* Otherwise it's not the right image */
1932 Status = STATUS_IMAGE_MP_UP_MISMATCH;
1933 }
1934 }
1935
1936 /* Unmap the section, close the handle, and return status */
1937 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1938 KeUnstackDetachProcess(&ApcState);
1939 ZwClose(SectionHandle);
1940 return Status;
1941 }
1942
1943 NTSTATUS
1944 NTAPI
1945 MmLoadSystemImage(IN PUNICODE_STRING FileName,
1946 IN PUNICODE_STRING NamePrefix OPTIONAL,
1947 IN PUNICODE_STRING LoadedName OPTIONAL,
1948 IN ULONG Flags,
1949 OUT PVOID *ModuleObject,
1950 OUT PVOID *ImageBaseAddress)
1951 {
1952 PVOID ModuleLoadBase = NULL;
1953 NTSTATUS Status;
1954 HANDLE FileHandle = NULL;
1955 OBJECT_ATTRIBUTES ObjectAttributes;
1956 IO_STATUS_BLOCK IoStatusBlock;
1957 PIMAGE_NT_HEADERS NtHeader;
1958 UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
1959 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
1960 ULONG EntrySize, DriverSize;
1961 PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
1962 PCHAR MissingApiName, Buffer;
1963 PWCHAR MissingDriverName;
1964 HANDLE SectionHandle;
1965 ACCESS_MASK DesiredAccess;
1966 PVOID Section = NULL;
1967 BOOLEAN LockOwned = FALSE;
1968 PLIST_ENTRY NextEntry;
1969 IMAGE_INFO ImageInfo;
1970 STRING AnsiTemp;
1971 PAGED_CODE();
1972
1973 /* Detect session-load */
1974 if (Flags)
1975 {
1976 /* Sanity checks */
1977 ASSERT(NamePrefix == NULL);
1978 ASSERT(LoadedName == NULL);
1979
1980 /* Make sure the process is in session too */
1981 if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
1982 }
1983
1984 /* Allocate a buffer we'll use for names */
1985 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, 'nLmM');
1986 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1987
1988 /* Check for a separator */
1989 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1990 {
1991 PWCHAR p;
1992 ULONG BaseLength;
1993
1994 /* Loop the path until we get to the base name */
1995 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
1996 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
1997
1998 /* Get the length */
1999 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
2000 BaseLength *= sizeof(WCHAR);
2001
2002 /* Setup the string */
2003 BaseName.Length = (USHORT)BaseLength;
2004 BaseName.Buffer = p;
2005 }
2006 else
2007 {
2008 /* Otherwise, we already have a base name */
2009 BaseName.Length = FileName->Length;
2010 BaseName.Buffer = FileName->Buffer;
2011 }
2012
2013 /* Setup the maximum length */
2014 BaseName.MaximumLength = BaseName.Length;
2015
2016 /* Now compute the base directory */
2017 BaseDirectory = *FileName;
2018 BaseDirectory.Length -= BaseName.Length;
2019 BaseDirectory.MaximumLength = BaseDirectory.Length;
2020
2021 /* And the prefix, which for now is just the name itself */
2022 PrefixName = *FileName;
2023
2024 /* Check if we have a prefix */
2025 if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
2026
2027 /* Check if we already have a name, use it instead */
2028 if (LoadedName) BaseName = *LoadedName;
2029
2030 /* Check for loader snap debugging */
2031 if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
2032 {
2033 /* Print out standard string */
2034 DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
2035 &PrefixName, &BaseName, Flags ? "in session space" : "");
2036 }
2037
2038 /* Acquire the load lock */
2039 LoaderScan:
2040 ASSERT(LockOwned == FALSE);
2041 LockOwned = TRUE;
2042 KeEnterCriticalRegion();
2043 KeWaitForSingleObject(&MmSystemLoadLock,
2044 WrVirtualMemory,
2045 KernelMode,
2046 FALSE,
2047 NULL);
2048
2049 /* Scan the module list */
2050 NextEntry = PsLoadedModuleList.Flink;
2051 while (NextEntry != &PsLoadedModuleList)
2052 {
2053 /* Get the entry and compare the names */
2054 LdrEntry = CONTAINING_RECORD(NextEntry,
2055 LDR_DATA_TABLE_ENTRY,
2056 InLoadOrderLinks);
2057 if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
2058 {
2059 /* Found it, break out */
2060 break;
2061 }
2062
2063 /* Keep scanning */
2064 NextEntry = NextEntry->Flink;
2065 }
2066
2067 /* Check if we found the image */
2068 if (NextEntry != &PsLoadedModuleList)
2069 {
2070 /* Check if we had already mapped a section */
2071 if (Section)
2072 {
2073 /* Dereference and clear */
2074 ObDereferenceObject(Section);
2075 Section = NULL;
2076 }
2077
2078 /* Check if this was supposed to be a session load */
2079 if (!Flags)
2080 {
2081 /* It wasn't, so just return the data */
2082 *ModuleObject = LdrEntry;
2083 *ImageBaseAddress = LdrEntry->DllBase;
2084 Status = STATUS_IMAGE_ALREADY_LOADED;
2085 }
2086 else
2087 {
2088 /* We don't support session loading yet */
2089 DPRINT1("Unsupported Session-Load!\n");
2090 while (TRUE);
2091 }
2092
2093 /* Do cleanup */
2094 goto Quickie;
2095 }
2096 else if (!Section)
2097 {
2098 /* It wasn't loaded, and we didn't have a previous attempt */
2099 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
2100 KeLeaveCriticalRegion();
2101 LockOwned = FALSE;
2102
2103 /* Check if KD is enabled */
2104 if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
2105 {
2106 /* FIXME: Attempt to get image from KD */
2107 }
2108
2109 /* We don't have a valid entry */
2110 LdrEntry = NULL;
2111
2112 /* Setup image attributes */
2113 InitializeObjectAttributes(&ObjectAttributes,
2114 FileName,
2115 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2116 NULL,
2117 NULL);
2118
2119 /* Open the image */
2120 Status = ZwOpenFile(&FileHandle,
2121 FILE_EXECUTE,
2122 &ObjectAttributes,
2123 &IoStatusBlock,
2124 FILE_SHARE_READ | FILE_SHARE_DELETE,
2125 0);
2126 if (!NT_SUCCESS(Status)) goto Quickie;
2127
2128 /* Validate it */
2129 Status = MmCheckSystemImage(FileHandle, FALSE);
2130 if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
2131 (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
2132 (Status == STATUS_INVALID_IMAGE_PROTECT))
2133 {
2134 /* Fail loading */
2135 goto Quickie;
2136 }
2137
2138 /* Check if this is a session-load */
2139 if (Flags)
2140 {
2141 /* Then we only need read and execute */
2142 DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
2143 }
2144 else
2145 {
2146 /* Otherwise, we can allow write access */
2147 DesiredAccess = SECTION_ALL_ACCESS;
2148 }
2149
2150 /* Initialize the attributes for the section */
2151 InitializeObjectAttributes(&ObjectAttributes,
2152 NULL,
2153 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2154 NULL,
2155 NULL);
2156
2157 /* Create the section */
2158 Status = ZwCreateSection(&SectionHandle,
2159 DesiredAccess,
2160 &ObjectAttributes,
2161 NULL,
2162 PAGE_EXECUTE,
2163 SEC_IMAGE,
2164 FileHandle);
2165 if (!NT_SUCCESS(Status)) goto Quickie;
2166
2167 /* Now get the section pointer */
2168 Status = ObReferenceObjectByHandle(SectionHandle,
2169 SECTION_MAP_EXECUTE,
2170 MmSectionObjectType,
2171 KernelMode,
2172 &Section,
2173 NULL);
2174 ZwClose(SectionHandle);
2175 if (!NT_SUCCESS(Status)) goto Quickie;
2176
2177 /* Check if this was supposed to be a session-load */
2178 if (Flags)
2179 {
2180 /* We don't support session loading yet */
2181 DPRINT1("Unsupported Session-Load!\n");
2182 while (TRUE);
2183 }
2184
2185 /* Check the loader list again, we should end up in the path below */
2186 goto LoaderScan;
2187 }
2188 else
2189 {
2190 /* We don't have a valid entry */
2191 LdrEntry = NULL;
2192 }
2193
2194 /* Load the image */
2195 Status = MiLoadImageSection(&Section,
2196 &ModuleLoadBase,
2197 FileName,
2198 FALSE,
2199 NULL);
2200 ASSERT(Status != STATUS_ALREADY_COMMITTED);
2201
2202 /* Get the size of the driver */
2203 DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
2204
2205 /* Make sure we're not being loaded into session space */
2206 if (!Flags)
2207 {
2208 /* Check for success */
2209 if (NT_SUCCESS(Status))
2210 {
2211 #if 0
2212 /* Support large pages for drivers */
2213 MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
2214 &ModuleLoadBase,
2215 &BaseName,
2216 TRUE);
2217 #endif
2218 }
2219
2220 /* Dereference the section */
2221 ObDereferenceObject(Section);
2222 Section = NULL;
2223 }
2224
2225 /* Check for failure of the load earlier */
2226 if (!NT_SUCCESS(Status)) goto Quickie;
2227
2228 /* Relocate the driver */
2229 Status = LdrRelocateImageWithBias(ModuleLoadBase,
2230 0,
2231 "SYSLDR",
2232 STATUS_SUCCESS,
2233 STATUS_CONFLICTING_ADDRESSES,
2234 STATUS_INVALID_IMAGE_FORMAT);
2235 if (!NT_SUCCESS(Status)) goto Quickie;
2236
2237
2238 /* Get the NT Header */
2239 NtHeader = RtlImageNtHeader(ModuleLoadBase);
2240
2241 /* Calculate the size we'll need for the entry and allocate it */
2242 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
2243 BaseName.Length +
2244 sizeof(UNICODE_NULL);
2245
2246 /* Allocate the entry */
2247 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
2248 if (!LdrEntry)
2249 {
2250 /* Fail */
2251 Status = STATUS_INSUFFICIENT_RESOURCES;
2252 goto Quickie;
2253 }
2254
2255 /* Setup the entry */
2256 LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
2257 LdrEntry->LoadCount = 1;
2258 LdrEntry->LoadedImports = LoadedImports;
2259 LdrEntry->PatchInformation = NULL;
2260
2261 /* Check the version */
2262 if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
2263 (NtHeader->OptionalHeader.MajorImageVersion >= 5))
2264 {
2265 /* Mark this image as a native image */
2266 LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
2267 }
2268
2269 /* Setup the rest of the entry */
2270 LdrEntry->DllBase = ModuleLoadBase;
2271 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
2272 NtHeader->OptionalHeader.AddressOfEntryPoint);
2273 LdrEntry->SizeOfImage = DriverSize;
2274 LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
2275 LdrEntry->SectionPointer = Section;
2276
2277 /* Now write the DLL name */
2278 LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
2279 LdrEntry->BaseDllName.Length = BaseName.Length;
2280 LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
2281
2282 /* Copy and null-terminate it */
2283 RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
2284 BaseName.Buffer,
2285 BaseName.Length);
2286 LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2287
2288 /* Now allocate the full name */
2289 LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
2290 PrefixName.Length +
2291 sizeof(UNICODE_NULL),
2292 TAG_LDR_WSTR);
2293 if (!LdrEntry->FullDllName.Buffer)
2294 {
2295 /* Don't fail, just set it to zero */
2296 LdrEntry->FullDllName.Length = 0;
2297 LdrEntry->FullDllName.MaximumLength = 0;
2298 }
2299 else
2300 {
2301 /* Set it up */
2302 LdrEntry->FullDllName.Length = PrefixName.Length;
2303 LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
2304
2305 /* Copy and null-terminate */
2306 RtlCopyMemory(LdrEntry->FullDllName.Buffer,
2307 PrefixName.Buffer,
2308 PrefixName.Length);
2309 LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2310 }
2311
2312 /* Add the entry */
2313 MiProcessLoaderEntry(LdrEntry, TRUE);
2314
2315 /* Resolve imports */
2316 MissingApiName = Buffer;
2317 Status = MiResolveImageReferences(ModuleLoadBase,
2318 &BaseDirectory,
2319 NULL,
2320 &MissingApiName,
2321 &MissingDriverName,
2322 &LoadedImports);
2323 if (!NT_SUCCESS(Status))
2324 {
2325 /* Fail */
2326 MiProcessLoaderEntry(LdrEntry, FALSE);
2327
2328 /* Check if we need to free the name */
2329 if (LdrEntry->FullDllName.Buffer)
2330 {
2331 /* Free it */
2332 ExFreePool(LdrEntry->FullDllName.Buffer);
2333 }
2334
2335 /* Free the entry itself */
2336 ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
2337 LdrEntry = NULL;
2338 goto Quickie;
2339 }
2340
2341 /* Update the loader entry */
2342 LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
2343 LDRP_ENTRY_PROCESSED |
2344 LDRP_MM_LOADED);
2345 LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
2346 LdrEntry->LoadedImports = LoadedImports;
2347
2348 /* FIXME: Call driver verifier's loader function */
2349
2350 /* Write-protect the system image */
2351 MiWriteProtectSystemImage(LdrEntry->DllBase);
2352
2353 /* Check if notifications are enabled */
2354 if (PsImageNotifyEnabled)
2355 {
2356 /* Fill out the notification data */
2357 ImageInfo.Properties = 0;
2358 ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
2359 ImageInfo.SystemModeImage = TRUE;
2360 ImageInfo.ImageSize = LdrEntry->SizeOfImage;
2361 ImageInfo.ImageBase = LdrEntry->DllBase;
2362 ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
2363
2364 /* Send the notification */
2365 PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
2366 }
2367
2368 #if defined(KDBG) || defined(_WINKD_)
2369 /* MiCacheImageSymbols doesn't detect rossym */
2370 if (TRUE)
2371 #else
2372 /* Check if there's symbols */
2373 if (MiCacheImageSymbols(LdrEntry->DllBase))
2374 #endif
2375 {
2376 /* Check if the system root is present */
2377 if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
2378 !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
2379 {
2380 /* Add the system root */
2381 UnicodeTemp = PrefixName;
2382 UnicodeTemp.Buffer += 11;
2383 UnicodeTemp.Length -= (11 * sizeof(WCHAR));
2384 sprintf_nt(Buffer,
2385 "%ws%wZ",
2386 &SharedUserData->NtSystemRoot[2],
2387 &UnicodeTemp);
2388 }
2389 else
2390 {
2391 /* Build the name */
2392 sprintf_nt(Buffer, "%wZ", &BaseName);
2393 }
2394
2395 /* Setup the ansi string */
2396 RtlInitString(&AnsiTemp, Buffer);
2397
2398 /* Notify the debugger */
2399 DbgLoadImageSymbols(&AnsiTemp,
2400 LdrEntry->DllBase,
2401 (ULONG_PTR)ZwCurrentProcess());
2402 LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
2403 }
2404
2405 /* Page the driver */
2406 ASSERT(Section == NULL);
2407 MiEnablePagingOfDriver(LdrEntry);
2408
2409 /* Return pointers */
2410 *ModuleObject = LdrEntry;
2411 *ImageBaseAddress = LdrEntry->DllBase;
2412
2413 Quickie:
2414 /* Check if we have the lock acquired */
2415 if (LockOwned)
2416 {
2417 /* Release the lock */
2418 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
2419 KeLeaveCriticalRegion();
2420 LockOwned = FALSE;
2421 }
2422
2423 /* If we have a file handle, close it */
2424 if (FileHandle) ZwClose(FileHandle);
2425
2426 /* Check if we had a prefix */
2427 if (NamePrefix) ExFreePool(PrefixName.Buffer);
2428
2429 /* Free the name buffer and return status */
2430 ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
2431 return Status;
2432 }
2433
2434 PLDR_DATA_TABLE_ENTRY
2435 NTAPI
2436 MiLookupDataTableEntry(IN PVOID Address)
2437 {
2438 PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
2439 PLIST_ENTRY NextEntry;
2440 PAGED_CODE();
2441
2442 /* Loop entries */
2443 NextEntry = PsLoadedModuleList.Flink;
2444 do
2445 {
2446 /* Get the loader entry */
2447 LdrEntry = CONTAINING_RECORD(NextEntry,
2448 LDR_DATA_TABLE_ENTRY,
2449 InLoadOrderLinks);
2450
2451 /* Check if the address matches */
2452 if ((Address >= LdrEntry->DllBase) &&
2453 (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
2454 LdrEntry->SizeOfImage)))
2455 {
2456 /* Found a match */
2457 FoundEntry = LdrEntry;
2458 break;
2459 }
2460
2461 /* Move on */
2462 NextEntry = NextEntry->Flink;
2463 } while(NextEntry != &PsLoadedModuleList);
2464
2465 /* Return the entry */
2466 return FoundEntry;
2467 }
2468
2469 /* PUBLIC FUNCTIONS ***********************************************************/
2470
2471 /*
2472 * @implemented
2473 */
2474 PVOID
2475 NTAPI
2476 MmPageEntireDriver(IN PVOID AddressWithinSection)
2477 {
2478 PMMPTE StartPte, EndPte;
2479 PLDR_DATA_TABLE_ENTRY LdrEntry;
2480 PAGED_CODE();
2481
2482 /* Get the loader entry */
2483 LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
2484 if (!LdrEntry) return NULL;
2485
2486 /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
2487 if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
2488 {
2489 /* Don't do anything, just return the base address */
2490 return LdrEntry->DllBase;
2491 }
2492
2493 /* Wait for active DPCs to finish before we page out the driver */
2494 KeFlushQueuedDpcs();
2495
2496 /* Get the PTE range for the whole driver image */
2497 StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
2498 EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
2499
2500 /* Enable paging for the PTE range */
2501 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
2502 MiSetPagingOfDriver(StartPte, EndPte);
2503
2504 /* Return the base address */
2505 return LdrEntry->DllBase;
2506 }
2507
2508 /*
2509 * @unimplemented
2510 */
2511 VOID
2512 NTAPI
2513 MmResetDriverPaging(IN PVOID AddressWithinSection)
2514 {
2515 UNIMPLEMENTED;
2516 }
2517
2518 /*
2519 * @implemented
2520 */
2521 PVOID
2522 NTAPI
2523 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
2524 {
2525 PVOID ProcAddress = NULL;
2526 ANSI_STRING AnsiRoutineName;
2527 NTSTATUS Status;
2528 PLIST_ENTRY NextEntry;
2529 PLDR_DATA_TABLE_ENTRY LdrEntry;
2530 BOOLEAN Found = FALSE;
2531 UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
2532 UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
2533 ULONG Modules = 0;
2534
2535 /* Convert routine to ansi name */
2536 Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
2537 SystemRoutineName,
2538 TRUE);
2539 if (!NT_SUCCESS(Status)) return NULL;
2540
2541 /* Lock the list */
2542 KeEnterCriticalRegion();
2543 ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
2544
2545 /* Loop the loaded module list */
2546 NextEntry = PsLoadedModuleList.Flink;
2547 while (NextEntry != &PsLoadedModuleList)
2548 {
2549 /* Get the entry */
2550 LdrEntry = CONTAINING_RECORD(NextEntry,
2551 LDR_DATA_TABLE_ENTRY,
2552 InLoadOrderLinks);
2553
2554 /* Check if it's the kernel or HAL */
2555 if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
2556 {
2557 /* Found it */
2558 Found = TRUE;
2559 Modules++;
2560 }
2561 else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
2562 {
2563 /* Found it */
2564 Found = TRUE;
2565 Modules++;
2566 }
2567
2568 /* Check if we found a valid binary */
2569 if (Found)
2570 {
2571 /* Find the procedure name */
2572 ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
2573 &AnsiRoutineName);
2574
2575 /* Break out if we found it or if we already tried both modules */
2576 if (ProcAddress) break;
2577 if (Modules == 2) break;
2578 }
2579
2580 /* Keep looping */
2581 NextEntry = NextEntry->Flink;
2582 }
2583
2584 /* Release the lock */
2585 ExReleaseResourceLite(&PsLoadedModuleResource);
2586 KeLeaveCriticalRegion();
2587
2588 /* Free the string and return */
2589 RtlFreeAnsiString(&AnsiRoutineName);
2590 return ProcAddress;
2591 }
2592