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