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)
9 /* INCLUDES ******************************************************************/
15 /* FUNCTIONS *****************************************************************/
19 MiSnapThunk(IN PVOID DllBase
,
21 IN PIMAGE_THUNK_DATA Name
,
22 IN PIMAGE_THUNK_DATA Address
,
23 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory
,
25 IN BOOLEAN SnapForwarder
,
26 OUT PCHAR
*MissingApi
)
32 PIMAGE_IMPORT_BY_NAME NameImport
;
34 ULONG Low
= 0, Mid
= 0, High
;
37 PCHAR MissingForwarder
;
38 CHAR NameBuffer
[MAXIMUM_FILENAME_LENGTH
];
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
;
48 IMAGE_THUNK_DATA ForwardThunk
;
51 /* Check if this is an ordinal */
52 IsOrdinal
= IMAGE_SNAP_BY_ORDINAL(Name
->u1
.Ordinal
);
53 if ((IsOrdinal
) && !(SnapForwarder
))
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
;
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
;
66 /* Copy the procedure name */
68 (PCHAR
)&NameImport
->Name
[0],
69 MAXIMUM_FILENAME_LENGTH
- 1);
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
);
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
])))
83 /* We have a match, get the ordinal number from here */
84 Ordinal
= OrdinalTable
[Hint
];
88 /* Do a binary search */
89 High
= ExportDirectory
->NumberOfNames
- 1;
92 /* Get new middle value */
93 Mid
= (Low
+ High
) >> 1;
96 Ret
= strcmp(NameImport
->Name
, (PCHAR
)DllBase
+ NameTable
[Mid
]);
114 /* Check if we couldn't find it */
115 if (High
< Low
) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND
;
117 /* Otherwise, this is the ordinal */
118 Ordinal
= OrdinalTable
[Mid
];
122 /* Check if the ordinal is invalid */
123 if (Ordinal
>= ExportDirectory
->NumberOfFunctions
)
126 Status
= STATUS_DRIVER_ORDINAL_NOT_FOUND
;
130 /* In case the forwarder is missing */
131 MissingForwarder
= NameBuffer
;
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
];
138 /* Assume success from now on */
139 Status
= STATUS_SUCCESS
;
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
)))
145 /* Now assume failure in case the forwarder doesn't exist */
146 Status
= STATUS_DRIVER_ENTRYPOINT_NOT_FOUND
;
148 /* Build the forwarder name */
149 DllName
.Buffer
= (PCHAR
)Address
->u1
.Function
;
150 DllName
.Length
= strchr(DllName
.Buffer
, '.') -
153 DllName
.MaximumLength
= DllName
.Length
;
156 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName
,
160 /* We failed, just return an error */
164 /* Loop the module list */
165 NextEntry
= PsLoadedModuleList
.Flink
;
166 while (NextEntry
!= &PsLoadedModuleList
)
168 /* Get the loader entry */
169 LdrEntry
= CONTAINING_RECORD(NextEntry
,
170 LDR_DATA_TABLE_ENTRY
,
173 /* Check if it matches */
174 if (RtlPrefixString((PSTRING
)&ForwarderName
,
175 (PSTRING
)&LdrEntry
->BaseDllName
,
178 /* Get the forwarder export directory */
179 ForwardExportDirectory
=
180 RtlImageDirectoryEntryToData(LdrEntry
->DllBase
,
182 IMAGE_DIRECTORY_ENTRY_EXPORT
,
184 if (!ForwardExportDirectory
) break;
186 /* Allocate a name entry */
187 ForwardLength
= strlen(DllName
.Buffer
+ DllName
.Length
) +
189 ForwardName
= ExAllocatePoolWithTag(PagedPool
,
190 sizeof(*ForwardName
) +
193 if (!ForwardName
) break;
196 RtlCopyMemory(&ForwardName
->Name
[0],
197 DllName
.Buffer
+ DllName
.Length
,
199 ForwardName
->Hint
= 0;
201 /* Set the new address */
202 *(PULONG
)&ForwardThunk
.u1
.AddressOfData
= (ULONG
)ForwardName
;
204 /* Snap the forwarder */
205 Status
= MiSnapThunk(LdrEntry
->DllBase
,
209 ForwardExportDirectory
,
214 /* Free the forwarder name and set the thunk */
215 ExFreePool(ForwardName
);
216 Address
->u1
= ForwardThunk
.u1
;
220 /* Go to the next entry */
221 NextEntry
= NextEntry
->Flink
;
225 RtlFreeUnicodeString(&ForwarderName
);
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
)
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
;
255 PIMAGE_THUNK_DATA OrigThunk
, FirstThunk
;
257 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
258 __FUNCTION__
, ImageBase
, ImageFileDirectory
);
260 /* Assume no imports */
261 *LoadImports
= (PVOID
)-2;
263 /* Get the import descriptor */
264 ImportDescriptor
= RtlImageDirectoryEntryToData(ImageBase
,
266 IMAGE_DIRECTORY_ENTRY_IMPORT
,
268 if (!ImportDescriptor
) return STATUS_SUCCESS
;
270 /* Loop all imports to count them */
271 for (CurrentImport
= ImportDescriptor
;
272 (CurrentImport
->Name
) && (CurrentImport
->OriginalFirstThunk
);
279 /* Make sure we have non-zero imports */
282 /* Calculate and allocate the list we'll need */
283 LoadedImportsSize
= ImportCount
* sizeof(PVOID
) + sizeof(SIZE_T
);
284 LoadedImports
= ExAllocatePoolWithTag(PagedPool
,
289 /* Zero it and set the count */
290 RtlZeroMemory(LoadedImports
, LoadedImportsSize
);
291 LoadedImports
->Count
= ImportCount
;
297 LoadedImports
= NULL
;
300 /* Reset the import count and loop descriptors again */
301 ImportCount
= GdiLink
= NormalLink
= 0;
302 while ((ImportDescriptor
->Name
) && (ImportDescriptor
->OriginalFirstThunk
))
305 ImportName
= (PCHAR
)((ULONG_PTR
)ImageBase
+ ImportDescriptor
->Name
);
307 /* Check if this is a GDI driver */
309 !(_strnicmp(ImportName
, "win32k", sizeof("win32k") - 1));
311 /* We can also allow dxapi */
312 NormalLink
= NormalLink
|
313 ((_strnicmp(ImportName
, "win32k", sizeof("win32k") - 1)) &&
314 (_strnicmp(ImportName
, "dxapi", sizeof("dxapi") - 1)));
316 /* Check if this is a valid GDI driver */
317 if ((GdiLink
) && (NormalLink
))
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
;
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)))
331 /* Don't reference this */
332 ReferenceNeeded
= FALSE
;
336 /* Reference these modules */
337 ReferenceNeeded
= TRUE
;
340 /* Now setup a unicode string for the import */
341 RtlInitAnsiString(&TempString
, ImportName
);
342 Status
= RtlAnsiStringToUnicodeString(&NameString
, &TempString
, TRUE
);
343 if (!NT_SUCCESS(Status
))
346 //MiDereferenceImports(LoadedImports);
347 if (LoadedImports
) ExFreePool(LoadedImports
);
351 /* We don't support name prefixes yet */
352 if (NamePrefix
) DPRINT1("Name Prefix not yet supported!\n");
354 /* Remember that we haven't loaded the import at this point */
359 /* Loop the driver list */
360 NextEntry
= PsLoadedModuleList
.Flink
;
361 while (NextEntry
!= &PsLoadedModuleList
)
363 /* Get the loader entry and compare the name */
364 LdrEntry
= CONTAINING_RECORD(NextEntry
,
365 LDR_DATA_TABLE_ENTRY
,
367 if (RtlEqualUnicodeString(&NameString
,
368 &LdrEntry
->BaseDllName
,
371 /* Get the base address */
372 ImportBase
= LdrEntry
->DllBase
;
374 /* Check if we haven't loaded yet, and we need references */
375 if (!(Loaded
) && (ReferenceNeeded
))
377 /* Make sure we're not already loading */
378 if (!(LdrEntry
->Flags
& LDRP_LOAD_IN_PROGRESS
))
380 /* Increase the load count */
381 LdrEntry
->LoadCount
++;
385 /* Done, break out */
389 /* Go to the next entry */
390 NextEntry
= NextEntry
->Flink
;
393 /* Check if we haven't loaded the import yet */
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
,
405 /* Setup the base length and copy it */
406 DllName
.Length
= ImageFileDirectory
->Length
;
407 RtlCopyMemory(DllName
.Buffer
,
408 ImageFileDirectory
->Buffer
,
409 ImageFileDirectory
->Length
);
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
;
417 Status
= LdrLoadModule(&DllName
, &DllEntry
);
418 if (NT_SUCCESS(Status
))
420 /* We can free the DLL Name */
421 ExFreePool(DllName
.Buffer
);
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;
434 /* We're out of resources */
435 Status
= STATUS_INSUFFICIENT_RESOURCES
;
438 /* Check if we're OK until now */
439 if (NT_SUCCESS(Status
))
441 /* We're now loaded */
444 /* Get the base address and other information */
445 DllBase
= DllEntry
->DllBase
;
446 ASSERT(DllBase
= DllEntry
->DllBase
);
448 /* Call the initialization routines */
450 Status
= MmCallDllInitialize(DllEntry
, &PsLoadedModuleList
);
451 if (!NT_SUCCESS(Status
))
453 /* We failed, unload the image */
454 MmUnloadSystemImage(DllEntry
);
461 /* Check if we failed by here */
462 if (!NT_SUCCESS(Status
))
464 /* Cleanup and return */
465 DPRINT1("Failed loading import\n");
466 RtlFreeUnicodeString(&NameString
);
467 //MiDereferenceImports(LoadedImports);
468 if (LoadedImports
) ExFreePool(LoadedImports
);
472 /* Loop again to make sure that everything is OK */
476 /* Check if we're support to reference this import */
477 if ((ReferenceNeeded
) && (LoadedImports
))
479 /* Make sure we're not already loading */
480 if (!(LdrEntry
->Flags
& LDRP_LOAD_IN_PROGRESS
))
483 LoadedImports
->Entry
[ImportCount
] = LdrEntry
;
488 /* Free the import name */
489 RtlFreeUnicodeString(&NameString
);
491 /* Set the missing driver name and get the export directory */
492 *MissingDriver
= LdrEntry
->BaseDllName
.Buffer
;
493 ExportDirectory
= RtlImageDirectoryEntryToData(ImportBase
,
495 IMAGE_DIRECTORY_ENTRY_EXPORT
,
497 if (!ExportDirectory
)
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
;
506 /* Make sure we have an IAT */
507 if (ImportDescriptor
->OriginalFirstThunk
)
509 /* Get the first thunks */
510 OrigThunk
= (PVOID
)((ULONG_PTR
)ImageBase
+
511 ImportDescriptor
->OriginalFirstThunk
);
512 FirstThunk
= (PVOID
)((ULONG_PTR
)ImageBase
+
513 ImportDescriptor
->FirstThunk
);
516 while (OrigThunk
->u1
.AddressOfData
)
519 Status
= MiSnapThunk(ImportBase
,
527 if (!NT_SUCCESS(Status
))
529 /* Cleanup and return */
530 //MiDereferenceImports(LoadedImports);
531 if (LoadedImports
) ExFreePool(LoadedImports
);
535 /* Reset the buffer */
536 *MissingApi
= MissingApiBuffer
;
540 /* Go to the next import */
544 /* Check if we have an import list */
547 /* Reset the count again, and loop entries*/
549 for (i
= 0; i
< LoadedImports
->Count
; i
++)
551 if (LoadedImports
->Entry
[i
])
553 /* Got an entry, OR it with 1 in case it's the single entry */
554 ImportEntry
= (PVOID
)((ULONG_PTR
)LoadedImports
->Entry
[i
] | 1);
559 /* Check if we had no imports */
562 /* Free the list and set it to no imports */
563 ExFreePool(LoadedImports
);
564 LoadedImports
= (PVOID
)-2;
566 else if (ImportCount
== 1)
568 /* Just one entry, we can free the table and only use our entry */
569 ExFreePool(LoadedImports
);
570 LoadedImports
= (PLOAD_IMPORTS
)ImportEntry
;
572 else if (ImportCount
!= LoadedImports
->Count
)
574 /* FIXME: Can this happen? */
575 DPRINT1("Unhandled scenario\n");
579 /* Return the list */
580 *LoadImports
= LoadedImports
;
584 return STATUS_SUCCESS
;