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