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