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