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