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