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