Send correct data to MiDereferenceImports()
[reactos.git] / reactos / ntoskrnl / mm / sysldr.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/mm/sysldr.c
5 * PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GCC's incompetence strikes again */
16 FORCEINLINE
17 VOID
18 sprintf_nt(IN PCHAR Buffer,
19 IN PCHAR Format,
20 IN ...)
21 {
22 va_list ap;
23 va_start(ap, Format);
24 vsprintf(Buffer, Format, ap);
25 va_end(ap);
26 }
27
28 /* GLOBALS *******************************************************************/
29
30 LIST_ENTRY PsLoadedModuleList;
31 KSPIN_LOCK PsLoadedModuleSpinLock;
32 ULONG PsNtosImageBase;
33 KMUTANT MmSystemLoadLock;
34 extern ULONG NtGlobalFlag;
35
36 /* FUNCTIONS *****************************************************************/
37
38 PVOID
39 NTAPI
40 MiCacheImageSymbols(IN PVOID BaseAddress)
41 {
42 ULONG DebugSize;
43 PVOID DebugDirectory = NULL;
44 PAGED_CODE();
45
46 /* Make sure it's safe to access the image */
47 _SEH_TRY
48 {
49 /* Get the debug directory */
50 DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
51 TRUE,
52 IMAGE_DIRECTORY_ENTRY_DEBUG,
53 &DebugSize);
54 }
55 _SEH_HANDLE
56 {
57 /* Nothing */
58 }
59 _SEH_END;
60
61 /* Return the directory */
62 return DebugDirectory;
63 }
64
65 VOID
66 NTAPI
67 MiFreeBootDriverMemory(PVOID BaseAddress,
68 ULONG Length)
69 {
70 ULONG i;
71
72 /* Loop each page */
73 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
74 {
75 /* Free the page */
76 MmDeleteVirtualMapping(NULL,
77 (PVOID)((ULONG_PTR)BaseAddress + i * PAGE_SIZE),
78 TRUE,
79 NULL,
80 NULL);
81 }
82 }
83
84 NTSTATUS
85 NTAPI
86 MiLoadImageSection(IN OUT PVOID *SectionPtr,
87 OUT PVOID *ImageBase,
88 IN PUNICODE_STRING FileName,
89 IN BOOLEAN SessionLoad,
90 IN PLDR_DATA_TABLE_ENTRY LdrEntry)
91 {
92 PROS_SECTION_OBJECT Section = *SectionPtr;
93 NTSTATUS Status;
94 PEPROCESS Process;
95 PVOID Base = NULL;
96 SIZE_T ViewSize = 0;
97 KAPC_STATE ApcState;
98 LARGE_INTEGER SectionOffset = {{0}};
99 BOOLEAN LoadSymbols = FALSE;
100 ULONG DriverSize;
101 PVOID DriverBase;
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 KeUnstackDetachProcess(&ApcState);
154 return Status;
155 }
156
157 /* Get the driver size */
158 DriverSize = Section->ImageSection->ImageSize;
159
160 /* Allocate a virtual section for the module */
161 DriverBase = MmAllocateSection(DriverSize, NULL);
162 *ImageBase = DriverBase;
163
164 /* Copy the image */
165 RtlCopyMemory(DriverBase, Base, DriverSize);
166
167 /* Now unmap the view */
168 Status = MmUnmapViewOfSection(Process, Base);
169 ASSERT(NT_SUCCESS(Status));
170
171 /* Detach and return status */
172 KeUnstackDetachProcess(&ApcState);
173 return Status;
174 }
175
176 NTSTATUS
177 NTAPI
178 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
179 {
180 SIZE_T i;
181
182 /* Check if there's no imports or if we're a boot driver */
183 if ((ImportList == (PVOID)-1) ||
184 (ImportList == (PVOID)-2) ||
185 (ImportList->Count == 0))
186 {
187 /* Then there's nothing to do */
188 return STATUS_SUCCESS;
189 }
190
191 /* Otherwise, FIXME */
192 DPRINT1("%u imports not dereferenced!\n", ImportList->Count);
193 for (i = 0; i < ImportList->Count; i++)
194 {
195 DPRINT1("%wZ <%wZ>\n", &ImportList->Entry[i]->FullDllName, &ImportList->Entry[i]->BaseDllName);
196 }
197 return STATUS_UNSUCCESSFUL;
198 }
199
200 VOID
201 NTAPI
202 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
203 {
204 PAGED_CODE();
205
206 /* Check if there's no imports or we're a boot driver or only one entry */
207 if ((LdrEntry->LoadedImports == (PVOID)-1) ||
208 (LdrEntry->LoadedImports == (PVOID)-2) ||
209 ((ULONG_PTR)LdrEntry->LoadedImports & 1))
210 {
211 /* Nothing to do */
212 return;
213 }
214
215 /* Otherwise, free the import list */
216 ExFreePool(LdrEntry->LoadedImports);
217 }
218
219 PVOID
220 NTAPI
221 MiFindExportedRoutineByName(IN PVOID DllBase,
222 IN PANSI_STRING ExportName)
223 {
224 PULONG NameTable;
225 PUSHORT OrdinalTable;
226 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
227 LONG Low = 0, Mid = 0, High, Ret;
228 USHORT Ordinal;
229 PVOID Function;
230 ULONG ExportSize;
231 PULONG ExportTable;
232 PAGED_CODE();
233
234 /* Get the export directory */
235 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
236 TRUE,
237 IMAGE_DIRECTORY_ENTRY_EXPORT,
238 &ExportSize);
239 if (!ExportDirectory) return NULL;
240
241 /* Setup name tables */
242 NameTable = (PULONG)((ULONG_PTR)DllBase +
243 ExportDirectory->AddressOfNames);
244 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
245 ExportDirectory->AddressOfNameOrdinals);
246
247 /* Do a binary search */
248 High = ExportDirectory->NumberOfNames - 1;
249 while (High >= Low)
250 {
251 /* Get new middle value */
252 Mid = (Low + High) >> 1;
253
254 /* Compare name */
255 Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
256 if (Ret < 0)
257 {
258 /* Update high */
259 High = Mid - 1;
260 }
261 else if (Ret > 0)
262 {
263 /* Update low */
264 Low = Mid + 1;
265 }
266 else
267 {
268 /* We got it */
269 break;
270 }
271 }
272
273 /* Check if we couldn't find it */
274 if (High < Low) return NULL;
275
276 /* Otherwise, this is the ordinal */
277 Ordinal = OrdinalTable[Mid];
278
279 /* Resolve the address and write it */
280 ExportTable = (PULONG)((ULONG_PTR)DllBase +
281 ExportDirectory->AddressOfFunctions);
282 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
283
284 /* We found it! */
285 ASSERT((Function > (PVOID)ExportDirectory) &&
286 (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
287 return Function;
288 }
289
290 PVOID
291 NTAPI
292 MiLocateExportName(IN PVOID DllBase,
293 IN PCHAR ExportName)
294 {
295 PULONG NameTable;
296 PUSHORT OrdinalTable;
297 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
298 LONG Low = 0, Mid = 0, High, Ret;
299 USHORT Ordinal;
300 PVOID Function;
301 ULONG ExportSize;
302 PULONG ExportTable;
303 PAGED_CODE();
304
305 /* Get the export directory */
306 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
307 TRUE,
308 IMAGE_DIRECTORY_ENTRY_EXPORT,
309 &ExportSize);
310 if (!ExportDirectory) return NULL;
311
312 /* Setup name tables */
313 NameTable = (PULONG)((ULONG_PTR)DllBase +
314 ExportDirectory->AddressOfNames);
315 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
316 ExportDirectory->AddressOfNameOrdinals);
317
318 /* Do a binary search */
319 High = ExportDirectory->NumberOfNames - 1;
320 while (High >= Low)
321 {
322 /* Get new middle value */
323 Mid = (Low + High) >> 1;
324
325 /* Compare name */
326 Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
327 if (Ret < 0)
328 {
329 /* Update high */
330 High = Mid - 1;
331 }
332 else if (Ret > 0)
333 {
334 /* Update low */
335 Low = Mid + 1;
336 }
337 else
338 {
339 /* We got it */
340 break;
341 }
342 }
343
344 /* Check if we couldn't find it */
345 if (High < Low) return NULL;
346
347 /* Otherwise, this is the ordinal */
348 Ordinal = OrdinalTable[Mid];
349
350 /* Resolve the address and write it */
351 ExportTable = (PULONG)((ULONG_PTR)DllBase +
352 ExportDirectory->AddressOfFunctions);
353 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
354
355 /* Check if the function is actually a forwarder */
356 if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
357 ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
358 {
359 /* It is, fail */
360 return NULL;
361 }
362
363 /* We found it */
364 return Function;
365 }
366
367 NTSTATUS
368 NTAPI
369 MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
370 IN PLIST_ENTRY ListHead)
371 {
372 PMM_DLL_INITIALIZE DllInit;
373
374 /* Try to see if the image exports a DllInitialize routine */
375 DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
376 "DllInitialize");
377 if (!DllInit) return STATUS_SUCCESS;
378
379 /* FIXME: TODO */
380 DPRINT1("DllInitialize not called!\n");
381 return STATUS_UNSUCCESSFUL;
382 }
383
384 VOID
385 NTAPI
386 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
387 IN BOOLEAN Insert)
388 {
389 KIRQL OldIrql;
390
391 /* Acquire the lock */
392 KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
393
394 /* Insert or remove from the list */
395 Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
396 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
397
398 /* Release the lock */
399 KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
400 }
401
402 VOID
403 NTAPI
404 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
405 IN PVOID OldBase,
406 IN PVOID NewBase,
407 IN ULONG Size)
408 {
409 ULONG_PTR OldBaseTop, Delta;
410 PLDR_DATA_TABLE_ENTRY LdrEntry;
411 PLIST_ENTRY NextEntry;
412 ULONG ImportSize;
413 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
414 PULONG ImageThunk;
415
416 /* Calculate the top and delta */
417 OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
418 Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
419
420 /* Loop the loader block */
421 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
422 NextEntry != &LoaderBlock->LoadOrderListHead;
423 NextEntry = NextEntry->Flink)
424 {
425 /* Get the loader entry */
426 LdrEntry = CONTAINING_RECORD(NextEntry,
427 LDR_DATA_TABLE_ENTRY,
428 InLoadOrderLinks);
429
430 /* Get the import table */
431 ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
432 TRUE,
433 IMAGE_DIRECTORY_ENTRY_IMPORT,
434 &ImportSize);
435 if (!ImportDescriptor) continue;
436
437 /* Make sure we have an IAT */
438 DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
439 while ((ImportDescriptor->Name) &&
440 (ImportDescriptor->OriginalFirstThunk))
441 {
442 /* Get the image thunk */
443 ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
444 ImportDescriptor->FirstThunk);
445 while (*ImageThunk)
446 {
447 /* Check if it's within this module */
448 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
449 {
450 /* Relocate it */
451 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
452 ImageThunk, *ImageThunk, *ImageThunk + Delta);
453 *ImageThunk += Delta;
454 }
455
456 /* Go to the next thunk */
457 ImageThunk++;
458 }
459
460 /* Go to the next import */
461 ImportDescriptor++;
462 }
463 }
464 }
465
466 NTSTATUS
467 NTAPI
468 MiSnapThunk(IN PVOID DllBase,
469 IN PVOID ImageBase,
470 IN PIMAGE_THUNK_DATA Name,
471 IN PIMAGE_THUNK_DATA Address,
472 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
473 IN ULONG ExportSize,
474 IN BOOLEAN SnapForwarder,
475 OUT PCHAR *MissingApi)
476 {
477 BOOLEAN IsOrdinal;
478 USHORT Ordinal;
479 PULONG NameTable;
480 PUSHORT OrdinalTable;
481 PIMAGE_IMPORT_BY_NAME NameImport;
482 USHORT Hint;
483 ULONG Low = 0, Mid = 0, High;
484 LONG Ret;
485 NTSTATUS Status;
486 PCHAR MissingForwarder;
487 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
488 PULONG ExportTable;
489 ANSI_STRING DllName;
490 UNICODE_STRING ForwarderName;
491 PLIST_ENTRY NextEntry;
492 PLDR_DATA_TABLE_ENTRY LdrEntry;
493 ULONG ForwardExportSize;
494 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
495 PIMAGE_IMPORT_BY_NAME ForwardName;
496 ULONG ForwardLength;
497 IMAGE_THUNK_DATA ForwardThunk;
498 PAGED_CODE();
499
500 /* Check if this is an ordinal */
501 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
502 if ((IsOrdinal) && !(SnapForwarder))
503 {
504 /* Get the ordinal number and set it as missing */
505 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
506 ExportDirectory->Base);
507 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
508 }
509 else
510 {
511 /* Get the VA if we don't have to snap */
512 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
513 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
514
515 /* Copy the procedure name */
516 strncpy(*MissingApi,
517 (PCHAR)&NameImport->Name[0],
518 MAXIMUM_FILENAME_LENGTH - 1);
519
520 /* Setup name tables */
521 DPRINT("Import name: %s\n", NameImport->Name);
522 NameTable = (PULONG)((ULONG_PTR)DllBase +
523 ExportDirectory->AddressOfNames);
524 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
525 ExportDirectory->AddressOfNameOrdinals);
526
527 /* Get the hint and check if it's valid */
528 Hint = NameImport->Hint;
529 if ((Hint < ExportDirectory->NumberOfNames) &&
530 !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
531 {
532 /* We have a match, get the ordinal number from here */
533 Ordinal = OrdinalTable[Hint];
534 }
535 else
536 {
537 /* Do a binary search */
538 High = ExportDirectory->NumberOfNames - 1;
539 while (High >= Low)
540 {
541 /* Get new middle value */
542 Mid = (Low + High) >> 1;
543
544 /* Compare name */
545 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
546 if (Ret < 0)
547 {
548 /* Update high */
549 High = Mid - 1;
550 }
551 else if (Ret > 0)
552 {
553 /* Update low */
554 Low = Mid + 1;
555 }
556 else
557 {
558 /* We got it */
559 break;
560 }
561 }
562
563 /* Check if we couldn't find it */
564 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
565
566 /* Otherwise, this is the ordinal */
567 Ordinal = OrdinalTable[Mid];
568 }
569 }
570
571 /* Check if the ordinal is invalid */
572 if (Ordinal >= ExportDirectory->NumberOfFunctions)
573 {
574 /* Fail */
575 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
576 }
577 else
578 {
579 /* In case the forwarder is missing */
580 MissingForwarder = NameBuffer;
581
582 /* Resolve the address and write it */
583 ExportTable = (PULONG)((ULONG_PTR)DllBase +
584 ExportDirectory->AddressOfFunctions);
585 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
586
587 /* Assume success from now on */
588 Status = STATUS_SUCCESS;
589
590 /* Check if the function is actually a forwarder */
591 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
592 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
593 {
594 /* Now assume failure in case the forwarder doesn't exist */
595 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
596
597 /* Build the forwarder name */
598 DllName.Buffer = (PCHAR)Address->u1.Function;
599 DllName.Length = strchr(DllName.Buffer, '.') -
600 DllName.Buffer +
601 sizeof(ANSI_NULL);
602 DllName.MaximumLength = DllName.Length;
603
604 /* Convert it */
605 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
606 &DllName,
607 TRUE)))
608 {
609 /* We failed, just return an error */
610 return Status;
611 }
612
613 /* Loop the module list */
614 NextEntry = PsLoadedModuleList.Flink;
615 while (NextEntry != &PsLoadedModuleList)
616 {
617 /* Get the loader entry */
618 LdrEntry = CONTAINING_RECORD(NextEntry,
619 LDR_DATA_TABLE_ENTRY,
620 InLoadOrderLinks);
621
622 /* Check if it matches */
623 if (RtlPrefixString((PSTRING)&ForwarderName,
624 (PSTRING)&LdrEntry->BaseDllName,
625 TRUE))
626 {
627 /* Get the forwarder export directory */
628 ForwardExportDirectory =
629 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
630 TRUE,
631 IMAGE_DIRECTORY_ENTRY_EXPORT,
632 &ForwardExportSize);
633 if (!ForwardExportDirectory) break;
634
635 /* Allocate a name entry */
636 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
637 sizeof(ANSI_NULL);
638 ForwardName = ExAllocatePoolWithTag(PagedPool,
639 sizeof(*ForwardName) +
640 ForwardLength,
641 TAG_LDR_WSTR);
642 if (!ForwardName) break;
643
644 /* Copy the data */
645 RtlCopyMemory(&ForwardName->Name[0],
646 DllName.Buffer + DllName.Length,
647 ForwardLength);
648 ForwardName->Hint = 0;
649
650 /* Set the new address */
651 *(PULONG)&ForwardThunk.u1.AddressOfData = (ULONG)ForwardName;
652
653 /* Snap the forwarder */
654 Status = MiSnapThunk(LdrEntry->DllBase,
655 ImageBase,
656 &ForwardThunk,
657 &ForwardThunk,
658 ForwardExportDirectory,
659 ForwardExportSize,
660 TRUE,
661 &MissingForwarder);
662
663 /* Free the forwarder name and set the thunk */
664 ExFreePool(ForwardName);
665 Address->u1 = ForwardThunk.u1;
666 break;
667 }
668
669 /* Go to the next entry */
670 NextEntry = NextEntry->Flink;
671 }
672
673 /* Free the name */
674 RtlFreeUnicodeString(&ForwarderName);
675 }
676 }
677
678 /* Return status */
679 return Status;
680 }
681
682 NTSTATUS
683 NTAPI
684 MmUnloadSystemImage(IN PVOID ImageHandle)
685 {
686 PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
687 PVOID BaseAddress = LdrEntry->DllBase;
688 NTSTATUS Status;
689 ANSI_STRING TempName;
690 BOOLEAN HadEntry = FALSE;
691
692 /* Acquire the loader lock */
693 KeEnterCriticalRegion();
694 KeWaitForSingleObject(&MmSystemLoadLock,
695 WrVirtualMemory,
696 KernelMode,
697 FALSE,
698 NULL);
699
700 /* Check if this driver was loaded at boot and didn't get imports parsed */
701 if (LdrEntry->LoadedImports == (PVOID)-1) goto Done;
702
703 /* We should still be alive */
704 ASSERT(LdrEntry->LoadCount != 0);
705 LdrEntry->LoadCount--;
706
707 /* Check if we're still loaded */
708 if (LdrEntry->LoadCount) goto Done;
709
710 /* We should cleanup... are symbols loaded */
711 if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
712 {
713 /* Create the ANSI name */
714 Status = RtlUnicodeStringToAnsiString(&TempName,
715 &LdrEntry->BaseDllName,
716 TRUE);
717 if (NT_SUCCESS(Status))
718 {
719 /* Unload the symbols */
720 DbgUnLoadImageSymbols(&TempName, BaseAddress, -1);
721 RtlFreeAnsiString(&TempName);
722 }
723 }
724
725 /* FIXME: Free the driver */
726 //MmFreeSection(LdrEntry->DllBase);
727
728 /* Check if we're linked in */
729 if (LdrEntry->InLoadOrderLinks.Flink)
730 {
731 /* Remove us */
732 MiProcessLoaderEntry(LdrEntry, FALSE);
733 HadEntry = TRUE;
734 }
735
736 /* Dereference and clear the imports */
737 MiDereferenceImports(LdrEntry->LoadedImports);
738 MiClearImports(LdrEntry);
739
740 /* Check if the entry needs to go away */
741 if (HadEntry)
742 {
743 /* Check if it had a name */
744 if (LdrEntry->FullDllName.Buffer)
745 {
746 /* Free it */
747 ExFreePool(LdrEntry->FullDllName.Buffer);
748 }
749
750 /* Check if we had a section */
751 if (LdrEntry->SectionPointer)
752 {
753 /* Dereference it */
754 ObDereferenceObject(LdrEntry->SectionPointer);
755 }
756
757 /* Free the entry */
758 ExFreePool(LdrEntry);
759 }
760
761 /* Release the system lock and return */
762 Done:
763 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
764 KeLeaveCriticalRegion();
765 return STATUS_SUCCESS;
766 }
767
768 NTSTATUS
769 NTAPI
770 MiResolveImageReferences(IN PVOID ImageBase,
771 IN PUNICODE_STRING ImageFileDirectory,
772 IN PUNICODE_STRING NamePrefix OPTIONAL,
773 OUT PCHAR *MissingApi,
774 OUT PWCHAR *MissingDriver,
775 OUT PLOAD_IMPORTS *LoadImports)
776 {
777 PCHAR MissingApiBuffer = *MissingApi, ImportName;
778 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
779 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
780 PLOAD_IMPORTS LoadedImports, NewImports;
781 ULONG GdiLink, NormalLink, i;
782 BOOLEAN ReferenceNeeded, Loaded;
783 ANSI_STRING TempString;
784 UNICODE_STRING NameString, DllName;
785 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
786 PVOID ImportBase, DllBase;
787 PLIST_ENTRY NextEntry;
788 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
789 NTSTATUS Status;
790 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
791 PAGED_CODE();
792 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
793 __FUNCTION__, ImageBase, ImageFileDirectory);
794
795 /* Assume no imports */
796 *LoadImports = (PVOID)-2;
797
798 /* Get the import descriptor */
799 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
800 TRUE,
801 IMAGE_DIRECTORY_ENTRY_IMPORT,
802 &ImportSize);
803 if (!ImportDescriptor) return STATUS_SUCCESS;
804
805 /* Loop all imports to count them */
806 for (CurrentImport = ImportDescriptor;
807 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
808 CurrentImport++)
809 {
810 /* One more */
811 ImportCount++;
812 }
813
814 /* Make sure we have non-zero imports */
815 if (ImportCount)
816 {
817 /* Calculate and allocate the list we'll need */
818 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
819 LoadedImports = ExAllocatePoolWithTag(PagedPool,
820 LoadedImportsSize,
821 TAG_LDR_WSTR);
822 if (LoadedImports)
823 {
824 /* Zero it */
825 RtlZeroMemory(LoadedImports, LoadedImportsSize);
826 }
827 }
828 else
829 {
830 /* No table */
831 LoadedImports = NULL;
832 }
833
834 /* Reset the import count and loop descriptors again */
835 ImportCount = GdiLink = NormalLink = 0;
836 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
837 {
838 /* Get the name */
839 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
840
841 /* Check if this is a GDI driver */
842 GdiLink = GdiLink |
843 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
844
845 /* We can also allow dxapi */
846 NormalLink = NormalLink |
847 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
848 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
849
850 /* Check if this is a valid GDI driver */
851 if ((GdiLink) && (NormalLink))
852 {
853 /* It's not, it's importing stuff it shouldn't be! */
854 MiDereferenceImports(LoadedImports);
855 if (LoadedImports) ExFreePool(LoadedImports);
856 return STATUS_PROCEDURE_NOT_FOUND;
857 }
858
859 /* Check if this is a "core" import, which doesn't get referenced */
860 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
861 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
862 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
863 {
864 /* Don't reference this */
865 ReferenceNeeded = FALSE;
866 }
867 else
868 {
869 /* Reference these modules */
870 ReferenceNeeded = TRUE;
871 }
872
873 /* Now setup a unicode string for the import */
874 RtlInitAnsiString(&TempString, ImportName);
875 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
876 if (!NT_SUCCESS(Status))
877 {
878 /* Failed */
879 MiDereferenceImports(LoadedImports);
880 if (LoadedImports) ExFreePool(LoadedImports);
881 return Status;
882 }
883
884 /* We don't support name prefixes yet */
885 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
886
887 /* Remember that we haven't loaded the import at this point */
888 CheckDllState:
889 Loaded = FALSE;
890 ImportBase = NULL;
891
892 /* Loop the driver list */
893 NextEntry = PsLoadedModuleList.Flink;
894 while (NextEntry != &PsLoadedModuleList)
895 {
896 /* Get the loader entry and compare the name */
897 LdrEntry = CONTAINING_RECORD(NextEntry,
898 LDR_DATA_TABLE_ENTRY,
899 InLoadOrderLinks);
900 if (RtlEqualUnicodeString(&NameString,
901 &LdrEntry->BaseDllName,
902 TRUE))
903 {
904 /* Get the base address */
905 ImportBase = LdrEntry->DllBase;
906
907 /* Check if we haven't loaded yet, and we need references */
908 if (!(Loaded) && (ReferenceNeeded))
909 {
910 /* Make sure we're not already loading */
911 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
912 {
913 /* Increase the load count */
914 LdrEntry->LoadCount++;
915 }
916 }
917
918 /* Done, break out */
919 break;
920 }
921
922 /* Go to the next entry */
923 NextEntry = NextEntry->Flink;
924 }
925
926 /* Check if we haven't loaded the import yet */
927 if (!ImportBase)
928 {
929 /* Setup the import DLL name */
930 DllName.MaximumLength = NameString.Length +
931 ImageFileDirectory->Length +
932 sizeof(UNICODE_NULL);
933 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
934 DllName.MaximumLength,
935 TAG_LDR_WSTR);
936 if (DllName.Buffer)
937 {
938 /* Setup the base length and copy it */
939 DllName.Length = ImageFileDirectory->Length;
940 RtlCopyMemory(DllName.Buffer,
941 ImageFileDirectory->Buffer,
942 ImageFileDirectory->Length);
943
944 /* Now add the import name and null-terminate it */
945 RtlAppendStringToString((PSTRING)&DllName,
946 (PSTRING)&NameString);
947 DllName.Buffer[(DllName.MaximumLength - 1) / 2] = UNICODE_NULL;
948
949 /* Load the image */
950 Status = MmLoadSystemImage(&DllName,
951 NamePrefix,
952 NULL,
953 0,
954 (PVOID)&DllEntry,
955 &DllBase);
956 if (NT_SUCCESS(Status))
957 {
958 /* We can free the DLL Name */
959 ExFreePool(DllName.Buffer);
960 }
961 else
962 {
963 /* Fill out the information for the error */
964 *MissingDriver = DllName.Buffer;
965 *(PULONG)MissingDriver |= 1;
966 *MissingApi = NULL;
967 }
968 }
969 else
970 {
971 /* We're out of resources */
972 Status = STATUS_INSUFFICIENT_RESOURCES;
973 }
974
975 /* Check if we're OK until now */
976 if (NT_SUCCESS(Status))
977 {
978 /* We're now loaded */
979 Loaded = TRUE;
980
981 /* Sanity check */
982 ASSERT(DllBase = DllEntry->DllBase);
983
984 /* Call the initialization routines */
985 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
986 if (!NT_SUCCESS(Status))
987 {
988 /* We failed, unload the image */
989 MmUnloadSystemImage(DllEntry);
990 while (TRUE);
991 Loaded = FALSE;
992 }
993 }
994
995 /* Check if we failed by here */
996 if (!NT_SUCCESS(Status))
997 {
998 /* Cleanup and return */
999 RtlFreeUnicodeString(&NameString);
1000 MiDereferenceImports(LoadedImports);
1001 if (LoadedImports) ExFreePool(LoadedImports);
1002 return Status;
1003 }
1004
1005 /* Loop again to make sure that everything is OK */
1006 goto CheckDllState;
1007 }
1008
1009 /* Check if we're support to reference this import */
1010 if ((ReferenceNeeded) && (LoadedImports))
1011 {
1012 /* Make sure we're not already loading */
1013 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1014 {
1015 /* Add the entry */
1016 LoadedImports->Entry[LoadedImports->Count] = LdrEntry;
1017 LoadedImports->Count++;
1018 }
1019 }
1020
1021 /* Free the import name */
1022 RtlFreeUnicodeString(&NameString);
1023
1024 /* Set the missing driver name and get the export directory */
1025 *MissingDriver = LdrEntry->BaseDllName.Buffer;
1026 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
1027 TRUE,
1028 IMAGE_DIRECTORY_ENTRY_EXPORT,
1029 &ExportSize);
1030 if (!ExportDirectory)
1031 {
1032 /* Cleanup and return */
1033 MiDereferenceImports(LoadedImports);
1034 if (LoadedImports) ExFreePool(LoadedImports);
1035 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
1036 }
1037
1038 /* Make sure we have an IAT */
1039 if (ImportDescriptor->OriginalFirstThunk)
1040 {
1041 /* Get the first thunks */
1042 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
1043 ImportDescriptor->OriginalFirstThunk);
1044 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
1045 ImportDescriptor->FirstThunk);
1046
1047 /* Loop the IAT */
1048 while (OrigThunk->u1.AddressOfData)
1049 {
1050 /* Snap thunk */
1051 Status = MiSnapThunk(ImportBase,
1052 ImageBase,
1053 OrigThunk++,
1054 FirstThunk++,
1055 ExportDirectory,
1056 ExportSize,
1057 FALSE,
1058 MissingApi);
1059 if (!NT_SUCCESS(Status))
1060 {
1061 /* Cleanup and return */
1062 MiDereferenceImports(LoadedImports);
1063 if (LoadedImports) ExFreePool(LoadedImports);
1064 return Status;
1065 }
1066
1067 /* Reset the buffer */
1068 *MissingApi = MissingApiBuffer;
1069 }
1070 }
1071
1072 /* Go to the next import */
1073 ImportDescriptor++;
1074 }
1075
1076 /* Check if we have an import list */
1077 if (LoadedImports)
1078 {
1079 /* Reset the count again, and loop entries*/
1080 ImportCount = 0;
1081 for (i = 0; i < LoadedImports->Count; i++)
1082 {
1083 if (LoadedImports->Entry[i])
1084 {
1085 /* Got an entry, OR it with 1 in case it's the single entry */
1086 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
1087 ImportCount++;
1088 }
1089 }
1090
1091 /* Check if we had no imports */
1092 if (!ImportCount)
1093 {
1094 /* Free the list and set it to no imports */
1095 ExFreePool(LoadedImports);
1096 LoadedImports = (PVOID)-2;
1097 }
1098 else if (ImportCount == 1)
1099 {
1100 /* Just one entry, we can free the table and only use our entry */
1101 ExFreePool(LoadedImports);
1102 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1103 }
1104 else if (ImportCount != LoadedImports->Count)
1105 {
1106 /* Allocate a new list */
1107 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1108 NewImports = ExAllocatePoolWithTag(PagedPool,
1109 LoadedImportsSize,
1110 TAG_LDR_WSTR);
1111 if (NewImports)
1112 {
1113 /* Set count */
1114 NewImports->Count = 0;
1115
1116 /* Loop all the imports */
1117 for (i = 0; i < LoadedImports->Count; i++)
1118 {
1119 /* Make sure it's valid */
1120 if (LoadedImports->Entry[i])
1121 {
1122 /* Copy it */
1123 NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
1124 NewImports->Count++;
1125 }
1126 }
1127
1128 /* Free the old copy */
1129 ExFreePool(LoadedImports);
1130 LoadedImports = NewImports;
1131 }
1132 }
1133
1134 /* Return the list */
1135 *LoadImports = LoadedImports;
1136 }
1137
1138 /* Return success */
1139 return STATUS_SUCCESS;
1140 }
1141
1142 VOID
1143 NTAPI
1144 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1145 {
1146 PLIST_ENTRY NextEntry;
1147 ULONG i = 0;
1148 PIMAGE_NT_HEADERS NtHeader;
1149 PLDR_DATA_TABLE_ENTRY LdrEntry;
1150 PIMAGE_FILE_HEADER FileHeader;
1151 BOOLEAN ValidRelocs;
1152 PIMAGE_DATA_DIRECTORY DataDirectory;
1153 PVOID DllBase, NewImageAddress;
1154 NTSTATUS Status;
1155
1156 /* Loop driver list */
1157 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1158 NextEntry != &LoaderBlock->LoadOrderListHead;
1159 NextEntry = NextEntry->Flink)
1160 {
1161 /* Get the loader entry and NT header */
1162 LdrEntry = CONTAINING_RECORD(NextEntry,
1163 LDR_DATA_TABLE_ENTRY,
1164 InLoadOrderLinks);
1165 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1166
1167 /* Debug info */
1168 DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1169 LdrEntry->DllBase,
1170 (ULONG_PTR)LdrEntry->DllBase+ LdrEntry->SizeOfImage,
1171 &LdrEntry->FullDllName);
1172
1173 /* Skip kernel and HAL */
1174 /* ROS HACK: Skip BOOTVID/KDCOM too */
1175 i++;
1176 if (i <= 4) continue;
1177
1178 /* Skip non-drivers */
1179 if (!NtHeader) continue;
1180
1181 /* Get the file header and make sure we can relocate */
1182 FileHeader = &NtHeader->FileHeader;
1183 if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1184 if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1185 IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1186
1187 /* Everything made sense until now, check the relocation section too */
1188 DataDirectory = &NtHeader->OptionalHeader.
1189 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1190 if (!DataDirectory->VirtualAddress)
1191 {
1192 /* We don't really have relocations */
1193 ValidRelocs = FALSE;
1194 }
1195 else
1196 {
1197 /* Make sure the size is valid */
1198 if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1199 LdrEntry->SizeOfImage)
1200 {
1201 /* They're not, skip */
1202 continue;
1203 }
1204
1205 /* We have relocations */
1206 ValidRelocs = TRUE;
1207 }
1208
1209 /* Remember the original address */
1210 DllBase = LdrEntry->DllBase;
1211
1212 /* Allocate a virtual section for the module */
1213 NewImageAddress = MmAllocateSection(LdrEntry->SizeOfImage, NULL);
1214 if (!NewImageAddress)
1215 {
1216 /* Shouldn't happen */
1217 DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
1218 while (TRUE);
1219 }
1220
1221 /* Sanity check */
1222 DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1223 ASSERT(ExpInitializationPhase == 0);
1224
1225 /* Now copy the entire driver over */
1226 RtlCopyMemory(NewImageAddress, DllBase, LdrEntry->SizeOfImage);
1227
1228 /* Sanity check */
1229 ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1230
1231 /* Set the image base to the address where the loader put it */
1232 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1233 NtHeader = RtlImageNtHeader(NewImageAddress);
1234 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1235
1236 /* Check if we had relocations */
1237 if (ValidRelocs)
1238 {
1239 /* Relocate the image */
1240 Status = LdrRelocateImageWithBias(NewImageAddress,
1241 0,
1242 "SYSLDR",
1243 STATUS_SUCCESS,
1244 STATUS_CONFLICTING_ADDRESSES,
1245 STATUS_INVALID_IMAGE_FORMAT);
1246 if (!NT_SUCCESS(Status))
1247 {
1248 /* This shouldn't happen */
1249 DPRINT1("Relocations failed!\n");
1250 while (TRUE);
1251 }
1252 }
1253
1254 /* Update the loader entry */
1255 LdrEntry->DllBase = NewImageAddress;
1256
1257 /* Update the thunks */
1258 DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1259 MiUpdateThunks(LoaderBlock,
1260 DllBase,
1261 NewImageAddress,
1262 LdrEntry->SizeOfImage);
1263
1264 /* Update the loader entry */
1265 LdrEntry->Flags |= 0x01000000;
1266 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1267 NtHeader->OptionalHeader.AddressOfEntryPoint);
1268 LdrEntry->SizeOfImage = LdrEntry->SizeOfImage;
1269
1270 /* Free the old copy */
1271 MiFreeBootDriverMemory(DllBase, LdrEntry->SizeOfImage);
1272 }
1273 }
1274
1275 BOOLEAN
1276 NTAPI
1277 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1278 {
1279 PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
1280 PLIST_ENTRY ListHead, NextEntry;
1281 ULONG EntrySize;
1282
1283 /* Setup the loaded module list and lock */
1284 KeInitializeSpinLock(&PsLoadedModuleSpinLock);
1285 InitializeListHead(&PsLoadedModuleList);
1286
1287 /* Get loop variables and the kernel entry */
1288 ListHead = &LoaderBlock->LoadOrderListHead;
1289 NextEntry = ListHead->Flink;
1290 LdrEntry = CONTAINING_RECORD(NextEntry,
1291 LDR_DATA_TABLE_ENTRY,
1292 InLoadOrderLinks);
1293 PsNtosImageBase = (ULONG)LdrEntry->DllBase;
1294
1295 /* Loop the loader block */
1296 while (NextEntry != ListHead)
1297 {
1298 /* Get the loader entry */
1299 LdrEntry = CONTAINING_RECORD(NextEntry,
1300 LDR_DATA_TABLE_ENTRY,
1301 InLoadOrderLinks);
1302
1303 /* FIXME: ROS HACK. Make sure this is a driver */
1304 if (!RtlImageNtHeader(LdrEntry->DllBase))
1305 {
1306 /* Skip this entry */
1307 NextEntry= NextEntry->Flink;
1308 continue;
1309 }
1310
1311 /* Calculate the size we'll need and allocate a copy */
1312 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1313 LdrEntry->BaseDllName.MaximumLength +
1314 sizeof(UNICODE_NULL);
1315 NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
1316 if (!NewEntry) return FALSE;
1317
1318 /* Copy the entry over */
1319 *NewEntry = *LdrEntry;
1320
1321 /* Allocate the name */
1322 NewEntry->FullDllName.Buffer =
1323 ExAllocatePoolWithTag(PagedPool,
1324 LdrEntry->FullDllName.MaximumLength +
1325 sizeof(UNICODE_NULL),
1326 TAG_LDR_WSTR);
1327 if (!NewEntry->FullDllName.Buffer) return FALSE;
1328
1329 /* Set the base name */
1330 NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
1331
1332 /* Copy the full and base name */
1333 RtlCopyMemory(NewEntry->FullDllName.Buffer,
1334 LdrEntry->FullDllName.Buffer,
1335 LdrEntry->FullDllName.MaximumLength);
1336 RtlCopyMemory(NewEntry->BaseDllName.Buffer,
1337 LdrEntry->BaseDllName.Buffer,
1338 LdrEntry->BaseDllName.MaximumLength);
1339
1340 /* Null-terminate the base name */
1341 NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
1342 sizeof(WCHAR)] = UNICODE_NULL;
1343
1344 /* Insert the entry into the list */
1345 InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
1346 NextEntry = NextEntry->Flink;
1347 }
1348
1349 /* Build the import lists for the boot drivers */
1350 //MiBuildImportsForBootDrivers();
1351
1352 /* We're done */
1353 return TRUE;
1354 }
1355
1356 BOOLEAN
1357 NTAPI
1358 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
1359 {
1360 PIMAGE_NT_HEADERS NtHeader;
1361 PAGED_CODE();
1362
1363 /* Get NT Headers */
1364 NtHeader = RtlImageNtHeader(BaseAddress);
1365 if (NtHeader)
1366 {
1367 /* Check if this image is only safe for UP while we have 2+ CPUs */
1368 if ((KeNumberProcessors > 1) &&
1369 (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
1370 {
1371 /* Fail */
1372 return FALSE;
1373 }
1374 }
1375
1376 /* Otherwise, it's safe */
1377 return TRUE;
1378 }
1379
1380 NTSTATUS
1381 NTAPI
1382 MmCheckSystemImage(IN HANDLE ImageHandle,
1383 IN BOOLEAN PurgeSection)
1384 {
1385 NTSTATUS Status;
1386 HANDLE SectionHandle;
1387 PVOID ViewBase = NULL;
1388 SIZE_T ViewSize = 0;
1389 IO_STATUS_BLOCK IoStatusBlock;
1390 FILE_STANDARD_INFORMATION FileStandardInfo;
1391 KAPC_STATE ApcState;
1392 PAGED_CODE();
1393
1394 /* Create a section for the DLL */
1395 Status = ZwCreateSection(&SectionHandle,
1396 SECTION_MAP_EXECUTE,
1397 NULL,
1398 NULL,
1399 PAGE_EXECUTE,
1400 SEC_COMMIT,
1401 ImageHandle);
1402 if (!NT_SUCCESS(Status)) return Status;
1403
1404 /* Make sure we're in the system process */
1405 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
1406
1407 /* Map it */
1408 Status = ZwMapViewOfSection(SectionHandle,
1409 NtCurrentProcess(),
1410 &ViewBase,
1411 0,
1412 0,
1413 NULL,
1414 &ViewSize,
1415 ViewShare,
1416 0,
1417 PAGE_EXECUTE);
1418 if (!NT_SUCCESS(Status))
1419 {
1420 /* We failed, close the handle and return */
1421 KeUnstackDetachProcess(&ApcState);
1422 ZwClose(SectionHandle);
1423 return Status;
1424 }
1425
1426 /* Now query image information */
1427 Status = ZwQueryInformationFile(ImageHandle,
1428 &IoStatusBlock,
1429 &FileStandardInfo,
1430 sizeof(FileStandardInfo),
1431 FileStandardInformation);
1432 if ( NT_SUCCESS(Status) )
1433 {
1434 /* First, verify the checksum */
1435 if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
1436 FileStandardInfo.
1437 EndOfFile.LowPart,
1438 FileStandardInfo.
1439 EndOfFile.LowPart))
1440 {
1441 /* Set checksum failure */
1442 Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1443 }
1444
1445 /* Check that it's a valid SMP image if we have more then one CPU */
1446 if (!MmVerifyImageIsOkForMpUse(ViewBase))
1447 {
1448 /* Otherwise it's not the right image */
1449 Status = STATUS_IMAGE_MP_UP_MISMATCH;
1450 }
1451 }
1452
1453 /* Unmap the section, close the handle, and return status */
1454 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1455 KeUnstackDetachProcess(&ApcState);
1456 ZwClose(SectionHandle);
1457 return Status;
1458 }
1459
1460 NTSTATUS
1461 NTAPI
1462 MmLoadSystemImage(IN PUNICODE_STRING FileName,
1463 IN PUNICODE_STRING NamePrefix OPTIONAL,
1464 IN PUNICODE_STRING LoadedName OPTIONAL,
1465 IN ULONG Flags,
1466 OUT PVOID *ModuleObject,
1467 OUT PVOID *ImageBaseAddress)
1468 {
1469 PVOID ModuleLoadBase = NULL;
1470 NTSTATUS Status;
1471 HANDLE FileHandle = NULL;
1472 OBJECT_ATTRIBUTES ObjectAttributes;
1473 IO_STATUS_BLOCK IoStatusBlock;
1474 PIMAGE_NT_HEADERS NtHeader;
1475 UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
1476 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
1477 ULONG EntrySize, DriverSize;
1478 PLOAD_IMPORTS LoadedImports = (PVOID)-2;
1479 PCHAR MissingApiName, Buffer;
1480 PWCHAR MissingDriverName;
1481 HANDLE SectionHandle;
1482 ACCESS_MASK DesiredAccess;
1483 PVOID Section = NULL;
1484 BOOLEAN LockOwned = FALSE;
1485 PLIST_ENTRY NextEntry;
1486 IMAGE_INFO ImageInfo;
1487 ANSI_STRING AnsiTemp;
1488 PAGED_CODE();
1489
1490 /* Detect session-load */
1491 if (Flags)
1492 {
1493 /* Sanity checks */
1494 ASSERT(NamePrefix == NULL);
1495 ASSERT(LoadedName == NULL);
1496
1497 /* Make sure the process is in session too */
1498 if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
1499 }
1500
1501 if (ModuleObject) *ModuleObject = NULL;
1502 if (ImageBaseAddress) *ImageBaseAddress = NULL;
1503
1504 /* Allocate a buffer we'll use for names */
1505 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
1506 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1507
1508 /* Check for a separator */
1509 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1510 {
1511 PWCHAR p;
1512 ULONG BaseLength;
1513
1514 /* Loop the path until we get to the base name */
1515 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
1516 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
1517
1518 /* Get the length */
1519 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
1520 BaseLength *= sizeof(WCHAR);
1521
1522 /* Setup the string */
1523 BaseName.Length = (USHORT)BaseLength;
1524 BaseName.Buffer = p;
1525 }
1526 else
1527 {
1528 /* Otherwise, we already have a base name */
1529 BaseName.Length = FileName->Length;
1530 BaseName.Buffer = FileName->Buffer;
1531 }
1532
1533 /* Setup the maximum length */
1534 BaseName.MaximumLength = BaseName.Length;
1535
1536 /* Now compute the base directory */
1537 BaseDirectory = *FileName;
1538 BaseDirectory.Length -= BaseName.Length;
1539 BaseDirectory.MaximumLength = BaseDirectory.Length;
1540
1541 /* And the prefix, which for now is just the name itself */
1542 PrefixName = *FileName;
1543
1544 /* Check if we have a prefix */
1545 if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
1546
1547 /* Check if we already have a name, use it instead */
1548 if (LoadedName) BaseName = *LoadedName;
1549
1550 /* Acquire the load lock */
1551 LoaderScan:
1552 ASSERT(LockOwned == FALSE);
1553 LockOwned = TRUE;
1554 KeEnterCriticalRegion();
1555 KeWaitForSingleObject(&MmSystemLoadLock,
1556 WrVirtualMemory,
1557 KernelMode,
1558 FALSE,
1559 NULL);
1560
1561 /* Scan the module list */
1562 NextEntry = PsLoadedModuleList.Flink;
1563 while (NextEntry != &PsLoadedModuleList)
1564 {
1565 /* Get the entry and compare the names */
1566 LdrEntry = CONTAINING_RECORD(NextEntry,
1567 LDR_DATA_TABLE_ENTRY,
1568 InLoadOrderLinks);
1569 if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
1570 {
1571 /* Found it, break out */
1572 break;
1573 }
1574
1575 /* Keep scanning */
1576 NextEntry = NextEntry->Flink;
1577 }
1578
1579 /* Check if we found the image */
1580 if (NextEntry != &PsLoadedModuleList)
1581 {
1582 /* Check if we had already mapped a section */
1583 if (Section)
1584 {
1585 /* Dereference and clear */
1586 ObDereferenceObject(Section);
1587 Section = NULL;
1588 }
1589
1590 /* Check if this was supposed to be a session load */
1591 if (!Flags)
1592 {
1593 /* It wasn't, so just return the data */
1594 if (ModuleObject) *ModuleObject = LdrEntry;
1595 if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
1596 Status = STATUS_IMAGE_ALREADY_LOADED;
1597 }
1598 else
1599 {
1600 /* We don't support session loading yet */
1601 DPRINT1("Unsupported Session-Load!\n");
1602 while (TRUE);
1603 }
1604
1605 /* Do cleanup */
1606 goto Quickie;
1607 }
1608 else if (!Section)
1609 {
1610 /* It wasn't loaded, and we didn't have a previous attempt */
1611 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1612 KeLeaveCriticalRegion();
1613 LockOwned = FALSE;
1614
1615 /* Check if KD is enabled */
1616 if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
1617 {
1618 /* FIXME: Attempt to get image from KD */
1619 }
1620
1621 /* We don't have a valid entry */
1622 LdrEntry = NULL;
1623
1624 /* Setup image attributes */
1625 InitializeObjectAttributes(&ObjectAttributes,
1626 FileName,
1627 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1628 NULL,
1629 NULL);
1630
1631 /* Open the image */
1632 Status = ZwOpenFile(&FileHandle,
1633 FILE_EXECUTE,
1634 &ObjectAttributes,
1635 &IoStatusBlock,
1636 FILE_SHARE_READ | FILE_SHARE_DELETE,
1637 0);
1638 if (!NT_SUCCESS(Status)) goto Quickie;
1639
1640 /* Validate it */
1641 Status = MmCheckSystemImage(FileHandle, FALSE);
1642 if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
1643 (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
1644 (Status == STATUS_INVALID_IMAGE_FORMAT))
1645 {
1646 /* Fail loading */
1647 goto Quickie;
1648 }
1649
1650 /* Check if this is a session-load */
1651 if (Flags)
1652 {
1653 /* Then we only need read and execute */
1654 DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
1655 }
1656 else
1657 {
1658 /* Otherwise, we can allow write access */
1659 DesiredAccess = SECTION_ALL_ACCESS;
1660 }
1661
1662 /* Initialize the attributes for the section */
1663 InitializeObjectAttributes(&ObjectAttributes,
1664 NULL,
1665 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1666 NULL,
1667 NULL);
1668
1669 /* Create the section */
1670 Status = ZwCreateSection(&SectionHandle,
1671 DesiredAccess,
1672 &ObjectAttributes,
1673 NULL,
1674 PAGE_EXECUTE,
1675 SEC_IMAGE,
1676 FileHandle);
1677 if (!NT_SUCCESS(Status)) goto Quickie;
1678
1679 /* Now get the section pointer */
1680 Status = ObReferenceObjectByHandle(SectionHandle,
1681 SECTION_MAP_EXECUTE,
1682 MmSectionObjectType,
1683 KernelMode,
1684 &Section,
1685 NULL);
1686 ZwClose(SectionHandle);
1687 if (!NT_SUCCESS(Status)) goto Quickie;
1688
1689 /* Check if this was supposed to be a session-load */
1690 if (Flags)
1691 {
1692 /* We don't support session loading yet */
1693 DPRINT1("Unsupported Session-Load!\n");
1694 while (TRUE);
1695 }
1696
1697 /* Check the loader list again, we should end up in the path below */
1698 goto LoaderScan;
1699 }
1700 else
1701 {
1702 /* We don't have a valid entry */
1703 LdrEntry = NULL;
1704 }
1705
1706 /* Load the image */
1707 Status = MiLoadImageSection(&Section,
1708 &ModuleLoadBase,
1709 FileName,
1710 FALSE,
1711 NULL);
1712 ASSERT(Status != STATUS_ALREADY_COMMITTED);
1713
1714 /* Get the size of the driver */
1715 DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
1716
1717 /* Make sure we're not being loaded into session space */
1718 if (!Flags)
1719 {
1720 /* Check for success */
1721 if (NT_SUCCESS(Status))
1722 {
1723 /* FIXME: Support large pages for drivers */
1724 }
1725
1726 /* Dereference the section */
1727 ObDereferenceObject(Section);
1728 Section = NULL;
1729 }
1730
1731 /* Get the NT Header */
1732 NtHeader = RtlImageNtHeader(ModuleLoadBase);
1733
1734 /* Relocate the driver */
1735 Status = LdrRelocateImageWithBias(ModuleLoadBase,
1736 0,
1737 "SYSLDR",
1738 STATUS_SUCCESS,
1739 STATUS_CONFLICTING_ADDRESSES,
1740 STATUS_INVALID_IMAGE_FORMAT);
1741 if (!NT_SUCCESS(Status)) goto Quickie;
1742
1743 /* Calculate the size we'll need for the entry and allocate it */
1744 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1745 BaseName.Length +
1746 sizeof(UNICODE_NULL);
1747
1748 /* Allocate the entry */
1749 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
1750 if (!LdrEntry)
1751 {
1752 /* Fail */
1753 Status = STATUS_INSUFFICIENT_RESOURCES;
1754 goto Quickie;
1755 }
1756
1757 /* Setup the entry */
1758 LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
1759 LdrEntry->LoadCount = 1;
1760 LdrEntry->LoadedImports = LoadedImports;
1761 LdrEntry->PatchInformation = NULL;
1762
1763 /* Check the version */
1764 if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
1765 (NtHeader->OptionalHeader.MajorImageVersion >= 5))
1766 {
1767 /* Mark this image as a native image */
1768 LdrEntry->Flags |= 0x80000000;
1769 }
1770
1771 /* Setup the rest of the entry */
1772 LdrEntry->DllBase = ModuleLoadBase;
1773 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
1774 NtHeader->OptionalHeader.AddressOfEntryPoint);
1775 LdrEntry->SizeOfImage = DriverSize;
1776 LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
1777 LdrEntry->SectionPointer = Section;
1778
1779 /* Now write the DLL name */
1780 LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
1781 LdrEntry->BaseDllName.Length = BaseName.Length;
1782 LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
1783
1784 /* Copy and null-terminate it */
1785 RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
1786 BaseName.Buffer,
1787 BaseName.Length);
1788 LdrEntry->BaseDllName.Buffer[BaseName.Length / 2] = UNICODE_NULL;
1789
1790 /* Now allocate the full name */
1791 LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
1792 PrefixName.Length +
1793 sizeof(UNICODE_NULL),
1794 TAG_LDR_WSTR);
1795 if (!LdrEntry->FullDllName.Buffer)
1796 {
1797 /* Don't fail, just set it to zero */
1798 LdrEntry->FullDllName.Length = 0;
1799 LdrEntry->FullDllName.MaximumLength = 0;
1800 }
1801 else
1802 {
1803 /* Set it up */
1804 LdrEntry->FullDllName.Length = PrefixName.Length;
1805 LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
1806
1807 /* Copy and null-terminate */
1808 RtlCopyMemory(LdrEntry->FullDllName.Buffer,
1809 PrefixName.Buffer,
1810 PrefixName.Length);
1811 LdrEntry->FullDllName.Buffer[PrefixName.Length / 2] = UNICODE_NULL;
1812 }
1813
1814 /* Add the entry */
1815 MiProcessLoaderEntry(LdrEntry, TRUE);
1816
1817 /* Resolve imports */
1818 MissingApiName = Buffer;
1819 Status = MiResolveImageReferences(ModuleLoadBase,
1820 &BaseDirectory,
1821 NULL,
1822 &MissingApiName,
1823 &MissingDriverName,
1824 &LoadedImports);
1825 if (!NT_SUCCESS(Status))
1826 {
1827 /* Fail */
1828 MiProcessLoaderEntry(LdrEntry, FALSE);
1829
1830 /* Check if we need to free the name */
1831 if (LdrEntry->FullDllName.Buffer)
1832 {
1833 /* Free it */
1834 ExFreePool(LdrEntry->FullDllName.Buffer);
1835 }
1836
1837 /* Free the entry itself */
1838 ExFreePool(LdrEntry);
1839 LdrEntry = NULL;
1840 goto Quickie;
1841 }
1842
1843 /* Update the loader entry */
1844 LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
1845 LDRP_ENTRY_PROCESSED |
1846 LDRP_MM_LOADED);
1847 LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
1848 LdrEntry->LoadedImports = LoadedImports;
1849
1850 /* FIXME: Apply driver verifier */
1851
1852 /* FIXME: Write-protect the system image */
1853
1854 /* Check if notifications are enabled */
1855 if (PsImageNotifyEnabled)
1856 {
1857 /* Fill out the notification data */
1858 ImageInfo.Properties = 0;
1859 ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
1860 ImageInfo.SystemModeImage = TRUE;
1861 ImageInfo.ImageSize = LdrEntry->SizeOfImage;
1862 ImageInfo.ImageBase = LdrEntry->DllBase;
1863 ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
1864
1865 /* Send the notification */
1866 PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
1867 }
1868
1869 /* Check if there's symbols */
1870 #ifdef KDBG
1871 /* If KDBG is defined, then we always have symbols */
1872 if (TRUE)
1873 #else
1874 if (MiCacheImageSymbols(LdrEntry->DllBase))
1875 #endif
1876 {
1877 /* Check if the system root is present */
1878 if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
1879 !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
1880 {
1881 /* Add the system root */
1882 UnicodeTemp = PrefixName;
1883 UnicodeTemp.Buffer += 11;
1884 UnicodeTemp.Length -= (11 * sizeof(WCHAR));
1885 sprintf_nt(Buffer,
1886 "%ws%wZ",
1887 &SharedUserData->NtSystemRoot[2],
1888 &UnicodeTemp);
1889 }
1890 else
1891 {
1892 /* Build the name */
1893 sprintf_nt(Buffer, "%wZ", &BaseName);
1894 }
1895
1896 /* Setup the ansi string */
1897 RtlInitString(&AnsiTemp, Buffer);
1898
1899 /* Notify the debugger */
1900 DbgLoadImageSymbols(&AnsiTemp, LdrEntry->DllBase, -1);
1901 LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
1902 }
1903
1904 /* FIXME: Page the driver */
1905 ASSERT(Section == NULL);
1906
1907 /* Return pointers */
1908 if (ModuleObject) *ModuleObject = LdrEntry;
1909 if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
1910
1911 Quickie:
1912 /* If we have a file handle, close it */
1913 if (FileHandle) ZwClose(FileHandle);
1914
1915 /* Check if we have the lock acquired */
1916 if (LockOwned)
1917 {
1918 /* Release the lock */
1919 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1920 KeLeaveCriticalRegion();
1921 LockOwned = FALSE;
1922 }
1923
1924 /* Check if we had a prefix */
1925 if (NamePrefix) ExFreePool(PrefixName.Buffer);
1926
1927 /* Free the name buffer and return status */
1928 ExFreePool(Buffer);
1929 return Status;
1930 }
1931
1932 /*
1933 * @implemented
1934 */
1935 PVOID
1936 NTAPI
1937 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
1938 {
1939 PVOID ProcAddress = NULL;
1940 ANSI_STRING AnsiRoutineName;
1941 NTSTATUS Status;
1942 PLIST_ENTRY NextEntry;
1943 extern LIST_ENTRY PsLoadedModuleList;
1944 PLDR_DATA_TABLE_ENTRY LdrEntry;
1945 BOOLEAN Found = FALSE;
1946 UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
1947 UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
1948 ULONG Modules = 0;
1949
1950 /* Convert routine to ansi name */
1951 Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
1952 SystemRoutineName,
1953 TRUE);
1954 if (!NT_SUCCESS(Status)) return NULL;
1955
1956 /* Lock the list */
1957 KeEnterCriticalRegion();
1958
1959 /* Loop the loaded module list */
1960 NextEntry = PsLoadedModuleList.Flink;
1961 while (NextEntry != &PsLoadedModuleList)
1962 {
1963 /* Get the entry */
1964 LdrEntry = CONTAINING_RECORD(NextEntry,
1965 LDR_DATA_TABLE_ENTRY,
1966 InLoadOrderLinks);
1967
1968 /* Check if it's the kernel or HAL */
1969 if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
1970 {
1971 /* Found it */
1972 Found = TRUE;
1973 Modules++;
1974 }
1975 else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
1976 {
1977 /* Found it */
1978 Found = TRUE;
1979 Modules++;
1980 }
1981
1982 /* Check if we found a valid binary */
1983 if (Found)
1984 {
1985 /* Find the procedure name */
1986 ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
1987 &AnsiRoutineName);
1988
1989 /* Break out if we found it or if we already tried both modules */
1990 if (ProcAddress) break;
1991 if (Modules == 2) break;
1992 }
1993
1994 /* Keep looping */
1995 NextEntry = NextEntry->Flink;
1996 }
1997
1998 /* Release the lock */
1999 KeLeaveCriticalRegion();
2000
2001 /* Free the string and return */
2002 RtlFreeAnsiString(&AnsiRoutineName);
2003 return ProcAddress;
2004 }
2005