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