- Rename ModuleListHead to PsLoadedModulesList.
[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 /* FUNCTIONS *****************************************************************/
16
17 NTSTATUS
18 NTAPI
19 MiSnapThunk(IN PVOID DllBase,
20 IN PVOID ImageBase,
21 IN PIMAGE_THUNK_DATA Name,
22 IN PIMAGE_THUNK_DATA Address,
23 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
24 IN ULONG ExportSize,
25 IN BOOLEAN SnapForwarder,
26 OUT PCHAR *MissingApi)
27 {
28 BOOLEAN IsOrdinal;
29 USHORT Ordinal;
30 PULONG NameTable;
31 PUSHORT OrdinalTable;
32 PIMAGE_IMPORT_BY_NAME NameImport;
33 USHORT Hint;
34 ULONG Low = 0, Mid = 0, High;
35 LONG Ret;
36 NTSTATUS Status;
37 PCHAR MissingForwarder;
38 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
39 PULONG ExportTable;
40 ANSI_STRING DllName;
41 UNICODE_STRING ForwarderName;
42 PLIST_ENTRY NextEntry;
43 PLDR_DATA_TABLE_ENTRY LdrEntry;
44 ULONG ForwardExportSize;
45 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
46 PIMAGE_IMPORT_BY_NAME ForwardName;
47 ULONG ForwardLength;
48 IMAGE_THUNK_DATA ForwardThunk;
49 PAGED_CODE();
50
51 /* Check if this is an ordinal */
52 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
53 if ((IsOrdinal) && !(SnapForwarder))
54 {
55 /* Get the ordinal number and set it as missing */
56 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
57 ExportDirectory->Base);
58 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
59 }
60 else
61 {
62 /* Get the VA if we don't have to snap */
63 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
64 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
65
66 /* Copy the procedure name */
67 strncpy(*MissingApi,
68 (PCHAR)&NameImport->Name[0],
69 MAXIMUM_FILENAME_LENGTH - 1);
70
71 /* Setup name tables */
72 DPRINT("Import name: %s\n", NameImport->Name);
73 NameTable = (PULONG)((ULONG_PTR)DllBase +
74 ExportDirectory->AddressOfNames);
75 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
76 ExportDirectory->AddressOfNameOrdinals);
77
78 /* Get the hint and check if it's valid */
79 Hint = NameImport->Hint;
80 if ((Hint < ExportDirectory->NumberOfNames) &&
81 !(strcmp(NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
82 {
83 /* We have a match, get the ordinal number from here */
84 Ordinal = OrdinalTable[Hint];
85 }
86 else
87 {
88 /* Do a binary search */
89 High = ExportDirectory->NumberOfNames - 1;
90 while (High >= Low)
91 {
92 /* Get new middle value */
93 Mid = (Low + High) >> 1;
94
95 /* Compare name */
96 Ret = strcmp(NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
97 if (Ret < 0)
98 {
99 /* Update high */
100 High = Mid - 1;
101 }
102 else if (Ret > 0)
103 {
104 /* Update low */
105 Low = Mid + 1;
106 }
107 else
108 {
109 /* We got it */
110 break;
111 }
112 }
113
114 /* Check if we couldn't find it */
115 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
116
117 /* Otherwise, this is the ordinal */
118 Ordinal = OrdinalTable[Mid];
119 }
120 }
121
122 /* Check if the ordinal is invalid */
123 if (Ordinal >= ExportDirectory->NumberOfFunctions)
124 {
125 /* Fail */
126 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
127 }
128 else
129 {
130 /* In case the forwarder is missing */
131 MissingForwarder = NameBuffer;
132
133 /* Resolve the address and write it */
134 ExportTable = (PULONG)((ULONG_PTR)DllBase +
135 ExportDirectory->AddressOfFunctions);
136 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
137
138 /* Assume success from now on */
139 Status = STATUS_SUCCESS;
140
141 /* Check if the function is actually a forwarder */
142 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
143 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
144 {
145 /* Now assume failure in case the forwarder doesn't exist */
146 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
147
148 /* Build the forwarder name */
149 DllName.Buffer = (PCHAR)Address->u1.Function;
150 DllName.Length = strchr(DllName.Buffer, '.') -
151 DllName.Buffer +
152 sizeof(ANSI_NULL);
153 DllName.MaximumLength = DllName.Length;
154
155 /* Convert it */
156 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
157 &DllName,
158 TRUE)))
159 {
160 /* We failed, just return an error */
161 return Status;
162 }
163
164 /* Loop the module list */
165 NextEntry = PsLoadedModuleList.Flink;
166 while (NextEntry != &PsLoadedModuleList)
167 {
168 /* Get the loader entry */
169 LdrEntry = CONTAINING_RECORD(NextEntry,
170 LDR_DATA_TABLE_ENTRY,
171 InLoadOrderLinks);
172
173 /* Check if it matches */
174 if (RtlPrefixString((PSTRING)&ForwarderName,
175 (PSTRING)&LdrEntry->BaseDllName,
176 TRUE))
177 {
178 /* Get the forwarder export directory */
179 ForwardExportDirectory =
180 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
181 TRUE,
182 IMAGE_DIRECTORY_ENTRY_EXPORT,
183 &ForwardExportSize);
184 if (!ForwardExportDirectory) break;
185
186 /* Allocate a name entry */
187 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
188 sizeof(ANSI_NULL);
189 ForwardName = ExAllocatePoolWithTag(PagedPool,
190 sizeof(*ForwardName) +
191 ForwardLength,
192 TAG_LDR_WSTR);
193 if (!ForwardName) break;
194
195 /* Copy the data */
196 RtlCopyMemory(&ForwardName->Name[0],
197 DllName.Buffer + DllName.Length,
198 ForwardLength);
199 ForwardName->Hint = 0;
200
201 /* Set the new address */
202 *(PULONG)&ForwardThunk.u1.AddressOfData = (ULONG)ForwardName;
203
204 /* Snap the forwarder */
205 Status = MiSnapThunk(LdrEntry->DllBase,
206 ImageBase,
207 &ForwardThunk,
208 &ForwardThunk,
209 ForwardExportDirectory,
210 ForwardExportSize,
211 TRUE,
212 &MissingForwarder);
213
214 /* Free the forwarder name and set the thunk */
215 ExFreePool(ForwardName);
216 Address->u1 = ForwardThunk.u1;
217 break;
218 }
219
220 /* Go to the next entry */
221 NextEntry = NextEntry->Flink;
222 }
223
224 /* Free the name */
225 RtlFreeUnicodeString(&ForwarderName);
226 }
227 }
228
229 /* Return status */
230 return Status;
231 }
232
233 NTSTATUS
234 NTAPI
235 MiResolveImageReferences(IN PVOID ImageBase,
236 IN PUNICODE_STRING ImageFileDirectory,
237 IN PUNICODE_STRING NamePrefix OPTIONAL,
238 OUT PCHAR *MissingApi,
239 OUT PWCHAR *MissingDriver,
240 OUT PLOAD_IMPORTS *LoadImports)
241 {
242 PCHAR MissingApiBuffer = *MissingApi, ImportName;
243 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
244 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
245 PLOAD_IMPORTS LoadedImports;
246 ULONG GdiLink, NormalLink, i;
247 BOOLEAN ReferenceNeeded, Loaded;
248 ANSI_STRING TempString;
249 UNICODE_STRING NameString, DllName;
250 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
251 PVOID ImportBase, DllBase;
252 PLIST_ENTRY NextEntry;
253 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
254 NTSTATUS Status;
255 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
256 PAGED_CODE();
257 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
258 __FUNCTION__, ImageBase, ImageFileDirectory);
259
260 /* Assume no imports */
261 *LoadImports = (PVOID)-2;
262
263 /* Get the import descriptor */
264 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
265 TRUE,
266 IMAGE_DIRECTORY_ENTRY_IMPORT,
267 &ImportSize);
268 if (!ImportDescriptor) return STATUS_SUCCESS;
269
270 /* Loop all imports to count them */
271 for (CurrentImport = ImportDescriptor;
272 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
273 CurrentImport++)
274 {
275 /* One more */
276 ImportCount++;
277 }
278
279 /* Make sure we have non-zero imports */
280 if (ImportCount)
281 {
282 /* Calculate and allocate the list we'll need */
283 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
284 LoadedImports = ExAllocatePoolWithTag(PagedPool,
285 LoadedImportsSize,
286 TAG_LDR_WSTR);
287 if (LoadedImports)
288 {
289 /* Zero it and set the count */
290 RtlZeroMemory(LoadedImports, LoadedImportsSize);
291 LoadedImports->Count = ImportCount;
292 }
293 }
294 else
295 {
296 /* No table */
297 LoadedImports = NULL;
298 }
299
300 /* Reset the import count and loop descriptors again */
301 ImportCount = GdiLink = NormalLink = 0;
302 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
303 {
304 /* Get the name */
305 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
306
307 /* Check if this is a GDI driver */
308 GdiLink = GdiLink |
309 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
310
311 /* We can also allow dxapi */
312 NormalLink = NormalLink |
313 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
314 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
315
316 /* Check if this is a valid GDI driver */
317 if ((GdiLink) && (NormalLink))
318 {
319 /* It's not, it's importing stuff it shouldn't be! */
320 DPRINT1("Invalid driver!\n");
321 //MiDereferenceImports(LoadedImports);
322 if (LoadedImports) ExFreePool(LoadedImports);
323 return STATUS_PROCEDURE_NOT_FOUND;
324 }
325
326 /* Check if this is a "core" import, which doesn't get referenced */
327 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
328 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
329 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
330 {
331 /* Don't reference this */
332 ReferenceNeeded = FALSE;
333 }
334 else
335 {
336 /* Reference these modules */
337 ReferenceNeeded = TRUE;
338 }
339
340 /* Now setup a unicode string for the import */
341 RtlInitAnsiString(&TempString, ImportName);
342 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
343 if (!NT_SUCCESS(Status))
344 {
345 /* Failed */
346 //MiDereferenceImports(LoadedImports);
347 if (LoadedImports) ExFreePool(LoadedImports);
348 return Status;
349 }
350
351 /* We don't support name prefixes yet */
352 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
353
354 /* Remember that we haven't loaded the import at this point */
355 CheckDllState:
356 Loaded = FALSE;
357 ImportBase = NULL;
358
359 /* Loop the driver list */
360 NextEntry = PsLoadedModuleList.Flink;
361 while (NextEntry != &PsLoadedModuleList)
362 {
363 /* Get the loader entry and compare the name */
364 LdrEntry = CONTAINING_RECORD(NextEntry,
365 LDR_DATA_TABLE_ENTRY,
366 InLoadOrderLinks);
367 if (RtlEqualUnicodeString(&NameString,
368 &LdrEntry->BaseDllName,
369 TRUE))
370 {
371 /* Get the base address */
372 ImportBase = LdrEntry->DllBase;
373
374 /* Check if we haven't loaded yet, and we need references */
375 if (!(Loaded) && (ReferenceNeeded))
376 {
377 /* Make sure we're not already loading */
378 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
379 {
380 /* Increase the load count */
381 LdrEntry->LoadCount++;
382 }
383 }
384
385 /* Done, break out */
386 break;
387 }
388
389 /* Go to the next entry */
390 NextEntry = NextEntry->Flink;
391 }
392
393 /* Check if we haven't loaded the import yet */
394 if (!ImportBase)
395 {
396 /* Setup the import DLL name */
397 DllName.MaximumLength = NameString.Length +
398 ImageFileDirectory->Length +
399 sizeof(UNICODE_NULL);
400 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
401 DllName.MaximumLength,
402 TAG_LDR_WSTR);
403 if (DllName.Buffer)
404 {
405 /* Setup the base length and copy it */
406 DllName.Length = ImageFileDirectory->Length;
407 RtlCopyMemory(DllName.Buffer,
408 ImageFileDirectory->Buffer,
409 ImageFileDirectory->Length);
410
411 /* Now add the import name and null-terminate it */
412 RtlAppendStringToString((PSTRING)&DllName,
413 (PSTRING)&NameString);
414 DllName.Buffer[(DllName.MaximumLength - 1) / 2] = UNICODE_NULL;
415
416 /* Load the image */
417 Status = LdrLoadModule(&DllName, &DllEntry);
418 if (NT_SUCCESS(Status))
419 {
420 /* We can free the DLL Name */
421 ExFreePool(DllName.Buffer);
422 }
423 else
424 {
425 /* Fill out the information for the error */
426 DPRINT1("Failed to import: %S\n", DllName.Buffer);
427 *MissingDriver = DllName.Buffer;
428 *(PULONG)MissingDriver |= 1;
429 *MissingApi = NULL;
430 }
431 }
432 else
433 {
434 /* We're out of resources */
435 Status = STATUS_INSUFFICIENT_RESOURCES;
436 }
437
438 /* Check if we're OK until now */
439 if (NT_SUCCESS(Status))
440 {
441 /* We're now loaded */
442 Loaded = TRUE;
443
444 /* Get the base address and other information */
445 DllBase = DllEntry->DllBase;
446 ASSERT(DllBase = DllEntry->DllBase);
447
448 /* Call the initialization routines */
449 #if 0
450 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
451 if (!NT_SUCCESS(Status))
452 {
453 /* We failed, unload the image */
454 MmUnloadSystemImage(DllEntry);
455 while (TRUE);
456 Loaded = FALSE;
457 }
458 #endif
459 }
460
461 /* Check if we failed by here */
462 if (!NT_SUCCESS(Status))
463 {
464 /* Cleanup and return */
465 DPRINT1("Failed loading import\n");
466 RtlFreeUnicodeString(&NameString);
467 //MiDereferenceImports(LoadedImports);
468 if (LoadedImports) ExFreePool(LoadedImports);
469 return Status;
470 }
471
472 /* Loop again to make sure that everything is OK */
473 goto CheckDllState;
474 }
475
476 /* Check if we're support to reference this import */
477 if ((ReferenceNeeded) && (LoadedImports))
478 {
479 /* Make sure we're not already loading */
480 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
481 {
482 /* Add the entry */
483 LoadedImports->Entry[ImportCount] = LdrEntry;
484 ImportCount++;
485 }
486 }
487
488 /* Free the import name */
489 RtlFreeUnicodeString(&NameString);
490
491 /* Set the missing driver name and get the export directory */
492 *MissingDriver = LdrEntry->BaseDllName.Buffer;
493 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
494 TRUE,
495 IMAGE_DIRECTORY_ENTRY_EXPORT,
496 &ExportSize);
497 if (!ExportDirectory)
498 {
499 /* Cleanup and return */
500 DPRINT1("Invalid driver: %wZ\n", &LdrEntry->BaseDllName);
501 //MiDereferenceImports(LoadedImports);
502 if (LoadedImports) ExFreePool(LoadedImports);
503 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
504 }
505
506 /* Make sure we have an IAT */
507 if (ImportDescriptor->OriginalFirstThunk)
508 {
509 /* Get the first thunks */
510 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
511 ImportDescriptor->OriginalFirstThunk);
512 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
513 ImportDescriptor->FirstThunk);
514
515 /* Loop the IAT */
516 while (OrigThunk->u1.AddressOfData)
517 {
518 /* Snap thunk */
519 Status = MiSnapThunk(ImportBase,
520 ImageBase,
521 OrigThunk++,
522 FirstThunk++,
523 ExportDirectory,
524 ExportSize,
525 FALSE,
526 MissingApi);
527 if (!NT_SUCCESS(Status))
528 {
529 /* Cleanup and return */
530 //MiDereferenceImports(LoadedImports);
531 if (LoadedImports) ExFreePool(LoadedImports);
532 return Status;
533 }
534
535 /* Reset the buffer */
536 *MissingApi = MissingApiBuffer;
537 }
538 }
539
540 /* Go to the next import */
541 ImportDescriptor++;
542 }
543
544 /* Check if we have an import list */
545 if (LoadedImports)
546 {
547 /* Reset the count again, and loop entries*/
548 ImportCount = 0;
549 for (i = 0; i < LoadedImports->Count; i++)
550 {
551 if (LoadedImports->Entry[i])
552 {
553 /* Got an entry, OR it with 1 in case it's the single entry */
554 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
555 ImportCount++;
556 }
557 }
558
559 /* Check if we had no imports */
560 if (!ImportCount)
561 {
562 /* Free the list and set it to no imports */
563 ExFreePool(LoadedImports);
564 LoadedImports = (PVOID)-2;
565 }
566 else if (ImportCount == 1)
567 {
568 /* Just one entry, we can free the table and only use our entry */
569 ExFreePool(LoadedImports);
570 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
571 }
572 else if (ImportCount != LoadedImports->Count)
573 {
574 /* FIXME: Can this happen? */
575 DPRINT1("Unhandled scenario\n");
576 while (TRUE);
577 }
578
579 /* Return the list */
580 *LoadImports = LoadedImports;
581 }
582
583 /* Return success */
584 return STATUS_SUCCESS;
585 }
586