2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User Mode Library
4 * FILE: dll/ntdll/ldr/ldrapi.c
5 * PURPOSE: PE Loader Public APIs
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Aleksey Bragin (aleksey@reactos.org)
10 /* INCLUDES *****************************************************************/
16 /* GLOBALS *******************************************************************/
18 LONG LdrpLoaderLockAcquisitonCount
;
20 /* FUNCTIONS *****************************************************************/
27 LdrUnlockLoaderLock(IN ULONG Flags
,
28 IN ULONG Cookie OPTIONAL
)
30 NTSTATUS Status
= STATUS_SUCCESS
;
32 DPRINT("LdrUnlockLoaderLock(%x %x)\n", Flags
, Cookie
);
34 /* Check for valid flags */
37 /* Flags are invalid, check how to fail */
38 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
40 /* The caller wants us to raise status */
41 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1
);
45 /* A normal failure */
46 return STATUS_INVALID_PARAMETER_1
;
50 /* If we don't have a cookie, just return */
51 if (!Cookie
) return STATUS_SUCCESS
;
53 /* Validate the cookie */
54 if ((Cookie
& 0xF0000000) ||
55 ((Cookie
>> 16) ^ ((ULONG
)(NtCurrentTeb()->RealClientId
.UniqueThread
) & 0xFFF)))
57 DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
59 /* Invalid cookie, check how to fail */
60 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
62 /* The caller wants us to raise status */
63 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2
);
67 /* A normal failure */
68 return STATUS_INVALID_PARAMETER_2
;
72 /* Ready to release the lock */
73 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
75 /* Do a direct leave */
76 RtlLeaveCriticalSection(&LdrpLoaderLock
);
80 /* Wrap this in SEH, since we're not supposed to raise */
84 RtlLeaveCriticalSection(&LdrpLoaderLock
);
86 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
88 /* We should use the LDR Filter instead */
89 Status
= _SEH2_GetExceptionCode();
103 LdrLockLoaderLock(IN ULONG Flags
,
104 OUT PULONG Result OPTIONAL
,
105 OUT PULONG Cookie OPTIONAL
)
108 NTSTATUS Status
= STATUS_SUCCESS
;
109 BOOLEAN InInit
= LdrpInLdrInit
;
111 DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags
, Result
, Cookie
);
113 /* Zero out the outputs */
114 if (Result
) *Result
= 0;
115 if (Cookie
) *Cookie
= 0;
117 /* Validate the flags */
118 if (Flags
& ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
|
119 LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY
))
121 /* Flags are invalid, check how to fail */
122 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
124 /* The caller wants us to raise status */
125 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1
);
128 /* A normal failure */
129 return STATUS_INVALID_PARAMETER_1
;
132 /* Make sure we got a cookie */
135 /* No cookie check how to fail */
136 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
138 /* The caller wants us to raise status */
139 RtlRaiseStatus(STATUS_INVALID_PARAMETER_3
);
142 /* A normal failure */
143 return STATUS_INVALID_PARAMETER_3
;
146 /* If the flag is set, make sure we have a valid pointer to use */
147 if ((Flags
& LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY
) && !(Result
))
149 /* No pointer to return the data to */
150 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
152 /* The caller wants us to raise status */
153 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2
);
157 return STATUS_INVALID_PARAMETER_2
;
160 /* Return now if we are in the init phase */
161 if (InInit
) return STATUS_SUCCESS
;
163 /* Check what locking semantic to use */
164 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS
)
166 /* Check if we should enter or simply try */
167 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY
)
170 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock
))
173 *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED
;
179 *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED
;
185 RtlEnterCriticalSection(&LdrpLoaderLock
);
187 /* See if result was requested */
188 if (Result
) *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED
;
191 /* Increase the acquisition count */
192 OldCount
= _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount
);
194 /* Generate a cookie */
195 *Cookie
= (((ULONG
)NtCurrentTeb()->RealClientId
.UniqueThread
& 0xFFF) << 16) | OldCount
;
199 /* Wrap this in SEH, since we're not supposed to raise */
202 /* Check if we should enter or simply try */
203 if (Flags
& LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY
)
206 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock
))
209 *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED
;
210 _SEH2_YIELD(return STATUS_SUCCESS
);
215 *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED
;
221 RtlEnterCriticalSection(&LdrpLoaderLock
);
223 /* See if result was requested */
224 if (Result
) *Result
= LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED
;
227 /* Increase the acquisition count */
228 OldCount
= _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount
);
230 /* Generate a cookie */
231 *Cookie
= (((ULONG
)NtCurrentTeb()->RealClientId
.UniqueThread
& 0xFFF) << 16) | OldCount
;
233 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
235 /* We should use the LDR Filter instead */
236 Status
= _SEH2_GetExceptionCode();
250 LdrVerifyImageMatchesChecksum(IN HANDLE FileHandle
,
251 IN PLDR_CALLBACK Callback
,
252 IN PVOID CallbackContext
,
253 OUT PUSHORT ImageCharacteristics
)
255 FILE_STANDARD_INFORMATION FileStandardInfo
;
256 PIMAGE_IMPORT_DESCRIPTOR ImportData
;
257 PIMAGE_SECTION_HEADER LastSection
;
258 IO_STATUS_BLOCK IoStatusBlock
;
259 PIMAGE_NT_HEADERS NtHeader
;
260 HANDLE SectionHandle
;
262 PVOID ViewBase
= NULL
;
268 DPRINT("LdrVerifyImageMatchesChecksum() called\n");
270 /* Create the section */
271 Status
= NtCreateSection(&SectionHandle
,
278 if (!NT_SUCCESS(Status
))
280 DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status
);
284 /* Map the section */
285 Status
= NtMapViewOfSection(SectionHandle
,
295 if (!NT_SUCCESS(Status
))
297 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status
);
298 NtClose(SectionHandle
);
302 /* Get the file information */
303 Status
= NtQueryInformationFile(FileHandle
,
306 sizeof(FILE_STANDARD_INFORMATION
),
307 FileStandardInformation
);
308 if (!NT_SUCCESS(Status
))
310 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status
);
311 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
312 NtClose(SectionHandle
);
316 /* Protect with SEH */
319 /* Verify the checksum */
320 Result
= LdrVerifyMappedImageMatchesChecksum(ViewBase
,
322 FileStandardInfo
.EndOfFile
.LowPart
);
324 /* Check if a callback was supplied */
325 if (Result
&& Callback
)
327 /* Get the NT Header */
328 NtHeader
= RtlImageNtHeader(ViewBase
);
330 /* Check if caller requested this back */
331 if (ImageCharacteristics
)
333 /* Return to caller */
334 *ImageCharacteristics
= NtHeader
->FileHeader
.Characteristics
;
337 /* Get the Import Directory Data */
338 ImportData
= RtlImageDirectoryEntryToData(ViewBase
,
340 IMAGE_DIRECTORY_ENTRY_IMPORT
,
343 /* Make sure there is one */
346 /* Loop the imports */
347 while (ImportData
->Name
)
350 ImportName
= RtlImageRvaToVa(NtHeader
,
355 /* Notify the callback */
356 Callback(CallbackContext
, ImportName
);
362 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
364 /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
369 /* Unmap file and close handle */
370 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
371 NtClose(SectionHandle
);
374 return !Result
? STATUS_IMAGE_CHECKSUM_MISMATCH
: Status
;
382 LdrGetProcedureAddress_(IN PVOID BaseAddress
,
383 IN PANSI_STRING Name
,
385 OUT PVOID
*ProcedureAddress
)
387 /* Call the internal routine and tell it to execute DllInit */
388 return LdrpGetProcedureAddress(BaseAddress
, Name
, Ordinal
, ProcedureAddress
, TRUE
);
394 LdrQueryProcessModuleInformationEx(IN ULONG ProcessId
,
396 IN PRTL_PROCESS_MODULES ModuleInformation
,
398 OUT PULONG ReturnedSize OPTIONAL
)
400 PLIST_ENTRY ModuleListHead
, InitListHead
;
401 PLIST_ENTRY Entry
, InitEntry
;
402 PLDR_DATA_TABLE_ENTRY Module
, InitModule
;
403 PRTL_PROCESS_MODULE_INFORMATION ModulePtr
= NULL
;
404 NTSTATUS Status
= STATUS_SUCCESS
;
405 ULONG UsedSize
= sizeof(ULONG
);
406 ANSI_STRING AnsiString
;
409 DPRINT("LdrQueryProcessModuleInformation() called\n");
411 /* Acquire loader lock */
412 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock
);
414 /* Check if we were given enough space */
417 Status
= STATUS_INFO_LENGTH_MISMATCH
;
421 ModuleInformation
->NumberOfModules
= 0;
422 ModulePtr
= &ModuleInformation
->Modules
[0];
423 Status
= STATUS_SUCCESS
;
426 /* Traverse the list of modules */
429 ModuleListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
430 Entry
= ModuleListHead
->Flink
;
432 while (Entry
!= ModuleListHead
)
434 Module
= CONTAINING_RECORD(Entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
436 DPRINT(" Module %wZ\n", &Module
->FullDllName
);
438 /* Increase the used size */
439 UsedSize
+= sizeof(RTL_PROCESS_MODULE_INFORMATION
);
443 Status
= STATUS_INFO_LENGTH_MISMATCH
;
447 ModulePtr
->ImageBase
= Module
->DllBase
;
448 ModulePtr
->ImageSize
= Module
->SizeOfImage
;
449 ModulePtr
->Flags
= Module
->Flags
;
450 ModulePtr
->LoadCount
= Module
->LoadCount
;
451 ModulePtr
->MappedBase
= NULL
;
452 ModulePtr
->InitOrderIndex
= 0;
453 ModulePtr
->LoadOrderIndex
= ModuleInformation
->NumberOfModules
;
455 /* Now get init order index by traversing init list */
456 InitListHead
= &NtCurrentPeb()->Ldr
->InInitializationOrderModuleList
;
457 InitEntry
= InitListHead
->Flink
;
459 while (InitEntry
!= InitListHead
)
461 InitModule
= CONTAINING_RECORD(InitEntry
, LDR_DATA_TABLE_ENTRY
, InInitializationOrderModuleList
);
463 /* Increase the index */
464 ModulePtr
->InitOrderIndex
++;
466 /* Quit the loop if our module is found */
467 if (InitModule
== Module
) break;
469 /* Advance to the next entry */
470 InitEntry
= InitEntry
->Flink
;
473 /* Prepare ANSI string with the module's name */
474 AnsiString
.Length
= 0;
475 AnsiString
.MaximumLength
= sizeof(ModulePtr
->FullPathName
);
476 AnsiString
.Buffer
= ModulePtr
->FullPathName
;
477 RtlUnicodeStringToAnsiString(&AnsiString
,
478 &Module
->FullDllName
,
481 /* Calculate OffsetToFileName field */
482 p
= strrchr(ModulePtr
->FullPathName
, '\\');
484 ModulePtr
->OffsetToFileName
= p
- ModulePtr
->FullPathName
+ 1;
486 ModulePtr
->OffsetToFileName
= 0;
488 /* Advance to the next module in the output list */
491 /* Increase number of modules */
492 if (ModuleInformation
)
493 ModuleInformation
->NumberOfModules
++;
496 /* Go to the next entry in the modules list */
497 Entry
= Entry
->Flink
;
500 /* Set returned size if it was provided */
502 *ReturnedSize
= UsedSize
;
504 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
506 /* Ignoring the exception */
509 /* Release the lock */
510 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock
);
512 DPRINT("LdrQueryProcessModuleInformation() done\n");
522 LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation
,
524 OUT PULONG ReturnedSize OPTIONAL
)
526 /* Call Ex version of the API */
527 return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation
, Size
, ReturnedSize
);
532 LdrEnumerateLoadedModules(BOOLEAN ReservedFlag
, PLDR_ENUM_CALLBACK EnumProc
, PVOID Context
)
534 PLIST_ENTRY ListHead
, ListEntry
;
535 PLDR_DATA_TABLE_ENTRY LdrEntry
;
538 BOOLEAN Stop
= FALSE
;
540 /* Check parameters */
541 if (ReservedFlag
|| !EnumProc
) return STATUS_INVALID_PARAMETER
;
543 /* Acquire the loader lock */
544 Status
= LdrLockLoaderLock(0, NULL
, &Cookie
);
545 if (!NT_SUCCESS(Status
)) return Status
;
547 /* Loop all the modules and call enum proc */
548 ListHead
= &NtCurrentPeb()->Ldr
->InLoadOrderModuleList
;
549 ListEntry
= ListHead
->Flink
;
550 while (ListHead
!= ListEntry
)
553 LdrEntry
= CONTAINING_RECORD(ListEntry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
555 /* Call the enumeration proc inside SEH */
558 EnumProc(LdrEntry
, Context
, &Stop
);
560 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
562 /* Ignoring the exception */
565 /* Break if we were asked to stop enumeration */
568 /* Release loader lock */
569 Status
= LdrUnlockLoaderLock(0, Cookie
);
571 /* Reset any successful status to STATUS_SUCCESS, but leave
572 failure to the caller */
573 if (NT_SUCCESS(Status
))
574 Status
= STATUS_SUCCESS
;
576 /* Return any possible failure status */
580 /* Advance to the next module */
581 ListEntry
= ListEntry
->Flink
;
584 /* Release loader lock, it must succeed this time */
585 Status
= LdrUnlockLoaderLock(0, Cookie
);
586 ASSERT(NT_SUCCESS(Status
));
589 return STATUS_SUCCESS
;