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