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