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