[CMAKE]
[reactos.git] / dll / ntdll / ldr / ldrapi.c
1 /*
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)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 LONG LdrpLoaderLockAcquisitonCount;
19
20 /* FUNCTIONS *****************************************************************/
21
22 /*
23 * @implemented
24 */
25 NTSTATUS
26 NTAPI
27 LdrUnlockLoaderLock(IN ULONG Flags,
28 IN ULONG Cookie OPTIONAL)
29 {
30 NTSTATUS Status = STATUS_SUCCESS;
31
32 DPRINT("LdrUnlockLoaderLock(%x %x)\n", Flags, Cookie);
33
34 /* Check for valid flags */
35 if (Flags & ~1)
36 {
37 /* Flags are invalid, check how to fail */
38 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
39 {
40 /* The caller wants us to raise status */
41 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
42 }
43 else
44 {
45 /* A normal failure */
46 return STATUS_INVALID_PARAMETER_1;
47 }
48 }
49
50 /* If we don't have a cookie, just return */
51 if (!Cookie) return STATUS_SUCCESS;
52
53 /* Validate the cookie */
54 if ((Cookie & 0xF0000000) ||
55 ((Cookie >> 16) ^ ((ULONG)(NtCurrentTeb()->RealClientId.UniqueThread) & 0xFFF)))
56 {
57 DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
58
59 /* Invalid cookie, check how to fail */
60 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
61 {
62 /* The caller wants us to raise status */
63 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
64 }
65 else
66 {
67 /* A normal failure */
68 return STATUS_INVALID_PARAMETER_2;
69 }
70 }
71
72 /* Ready to release the lock */
73 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
74 {
75 /* Do a direct leave */
76 RtlLeaveCriticalSection(&LdrpLoaderLock);
77 }
78 else
79 {
80 /* Wrap this in SEH, since we're not supposed to raise */
81 _SEH2_TRY
82 {
83 /* Leave the lock */
84 RtlLeaveCriticalSection(&LdrpLoaderLock);
85 }
86 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
87 {
88 /* We should use the LDR Filter instead */
89 Status = _SEH2_GetExceptionCode();
90 }
91 _SEH2_END;
92 }
93
94 /* All done */
95 return Status;
96 }
97
98 /*
99 * @implemented
100 */
101 NTSTATUS
102 NTAPI
103 LdrLockLoaderLock(IN ULONG Flags,
104 OUT PULONG Result OPTIONAL,
105 OUT PULONG Cookie OPTIONAL)
106 {
107 LONG OldCount;
108 NTSTATUS Status = STATUS_SUCCESS;
109 BOOLEAN InInit = LdrpInLdrInit;
110
111 DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags, Result, Cookie);
112
113 /* Zero out the outputs */
114 if (Result) *Result = 0;
115 if (Cookie) *Cookie = 0;
116
117 /* Validate the flags */
118 if (Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS |
119 LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY))
120 {
121 /* Flags are invalid, check how to fail */
122 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
123 {
124 /* The caller wants us to raise status */
125 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
126 }
127
128 /* A normal failure */
129 return STATUS_INVALID_PARAMETER_1;
130 }
131
132 /* Make sure we got a cookie */
133 if (!Cookie)
134 {
135 /* No cookie check how to fail */
136 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
137 {
138 /* The caller wants us to raise status */
139 RtlRaiseStatus(STATUS_INVALID_PARAMETER_3);
140 }
141
142 /* A normal failure */
143 return STATUS_INVALID_PARAMETER_3;
144 }
145
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))
148 {
149 /* No pointer to return the data to */
150 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
151 {
152 /* The caller wants us to raise status */
153 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
154 }
155
156 /* Fail */
157 return STATUS_INVALID_PARAMETER_2;
158 }
159
160 /* Return now if we are in the init phase */
161 if (InInit) return STATUS_SUCCESS;
162
163 /* Check what locking semantic to use */
164 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
165 {
166 /* Check if we should enter or simply try */
167 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
168 {
169 /* Do a try */
170 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
171 {
172 /* It's locked */
173 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
174 goto Quickie;
175 }
176 else
177 {
178 /* It worked */
179 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
180 }
181 }
182 else
183 {
184 /* Do a enter */
185 RtlEnterCriticalSection(&LdrpLoaderLock);
186
187 /* See if result was requested */
188 if (Result) *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
189 }
190
191 /* Increase the acquisition count */
192 OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
193
194 /* Generate a cookie */
195 *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
196 }
197 else
198 {
199 /* Wrap this in SEH, since we're not supposed to raise */
200 _SEH2_TRY
201 {
202 /* Check if we should enter or simply try */
203 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
204 {
205 /* Do a try */
206 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
207 {
208 /* It's locked */
209 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
210 _SEH2_YIELD(return STATUS_SUCCESS);
211 }
212 else
213 {
214 /* It worked */
215 *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
216 }
217 }
218 else
219 {
220 /* Do an enter */
221 RtlEnterCriticalSection(&LdrpLoaderLock);
222
223 /* See if result was requested */
224 if (Result) *Result = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
225 }
226
227 /* Increase the acquisition count */
228 OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
229
230 /* Generate a cookie */
231 *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
232 }
233 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
234 {
235 /* We should use the LDR Filter instead */
236 Status = _SEH2_GetExceptionCode();
237 }
238 _SEH2_END;
239 }
240
241 Quickie:
242 return Status;
243 }
244
245 /*
246 * @implemented
247 */
248 NTSTATUS
249 NTAPI
250 LdrVerifyImageMatchesChecksum(IN HANDLE FileHandle,
251 IN PLDR_CALLBACK Callback,
252 IN PVOID CallbackContext,
253 OUT PUSHORT ImageCharacteristics)
254 {
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;
261 SIZE_T ViewSize = 0;
262 PVOID ViewBase = NULL;
263 BOOLEAN Result;
264 NTSTATUS Status;
265 PVOID ImportName;
266 ULONG Size;
267
268 DPRINT("LdrVerifyImageMatchesChecksum() called\n");
269
270 /* Create the section */
271 Status = NtCreateSection(&SectionHandle,
272 SECTION_MAP_EXECUTE,
273 NULL,
274 NULL,
275 PAGE_EXECUTE,
276 SEC_COMMIT,
277 FileHandle);
278 if (!NT_SUCCESS(Status))
279 {
280 DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status);
281 return Status;
282 }
283
284 /* Map the section */
285 Status = NtMapViewOfSection(SectionHandle,
286 NtCurrentProcess(),
287 &ViewBase,
288 0,
289 0,
290 NULL,
291 &ViewSize,
292 ViewShare,
293 0,
294 PAGE_EXECUTE);
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
298 NtClose(SectionHandle);
299 return Status;
300 }
301
302 /* Get the file information */
303 Status = NtQueryInformationFile(FileHandle,
304 &IoStatusBlock,
305 &FileStandardInfo,
306 sizeof(FILE_STANDARD_INFORMATION),
307 FileStandardInformation);
308 if (!NT_SUCCESS(Status))
309 {
310 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
311 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
312 NtClose(SectionHandle);
313 return Status;
314 }
315
316 /* Protect with SEH */
317 _SEH2_TRY
318 {
319 /* Verify the checksum */
320 Result = LdrVerifyMappedImageMatchesChecksum(ViewBase,
321 ViewSize,
322 FileStandardInfo.EndOfFile.LowPart);
323
324 /* Check if a callback was supplied */
325 if (Result && Callback)
326 {
327 /* Get the NT Header */
328 NtHeader = RtlImageNtHeader(ViewBase);
329
330 /* Check if caller requested this back */
331 if (ImageCharacteristics)
332 {
333 /* Return to caller */
334 *ImageCharacteristics = NtHeader->FileHeader.Characteristics;
335 }
336
337 /* Get the Import Directory Data */
338 ImportData = RtlImageDirectoryEntryToData(ViewBase,
339 FALSE,
340 IMAGE_DIRECTORY_ENTRY_IMPORT,
341 &Size);
342
343 /* Make sure there is one */
344 if (ImportData)
345 {
346 /* Loop the imports */
347 while (ImportData->Name)
348 {
349 /* Get the name */
350 ImportName = RtlImageRvaToVa(NtHeader,
351 ViewBase,
352 ImportData->Name,
353 &LastSection);
354
355 /* Notify the callback */
356 Callback(CallbackContext, ImportName);
357 ImportData++;
358 }
359 }
360 }
361 }
362 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
363 {
364 /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
365 Result = FALSE;
366 }
367 _SEH2_END;
368
369 /* Unmap file and close handle */
370 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
371 NtClose(SectionHandle);
372
373 /* Return status */
374 return !Result ? STATUS_IMAGE_CHECKSUM_MISMATCH : Status;
375 }
376
377 /*
378 * @implemented
379 */
380 NTSTATUS
381 NTAPI
382 LdrGetProcedureAddress_(IN PVOID BaseAddress,
383 IN PANSI_STRING Name,
384 IN ULONG Ordinal,
385 OUT PVOID *ProcedureAddress)
386 {
387 /* Call the internal routine and tell it to execute DllInit */
388 return LdrpGetProcedureAddress(BaseAddress, Name, Ordinal, ProcedureAddress, TRUE);
389 }
390
391
392 NTSTATUS
393 NTAPI
394 LdrQueryProcessModuleInformationEx(IN ULONG ProcessId,
395 IN ULONG Reserved,
396 IN PRTL_PROCESS_MODULES ModuleInformation,
397 IN ULONG Size,
398 OUT PULONG ReturnedSize OPTIONAL)
399 {
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;
407 PCHAR p;
408
409 DPRINT("LdrQueryProcessModuleInformation() called\n");
410
411 /* Acquire loader lock */
412 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
413
414 /* Check if we were given enough space */
415 if (Size < UsedSize)
416 {
417 Status = STATUS_INFO_LENGTH_MISMATCH;
418 }
419 else
420 {
421 ModuleInformation->NumberOfModules = 0;
422 ModulePtr = &ModuleInformation->Modules[0];
423 Status = STATUS_SUCCESS;
424 }
425
426 /* Traverse the list of modules */
427 _SEH2_TRY
428 {
429 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
430 Entry = ModuleListHead->Flink;
431
432 while (Entry != ModuleListHead)
433 {
434 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
435
436 DPRINT(" Module %wZ\n", &Module->FullDllName);
437
438 /* Increase the used size */
439 UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION);
440
441 if (UsedSize > Size)
442 {
443 Status = STATUS_INFO_LENGTH_MISMATCH;
444 }
445 else
446 {
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;
454
455 /* Now get init order index by traversing init list */
456 InitListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
457 InitEntry = InitListHead->Flink;
458
459 while (InitEntry != InitListHead)
460 {
461 InitModule = CONTAINING_RECORD(InitEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
462
463 /* Increase the index */
464 ModulePtr->InitOrderIndex++;
465
466 /* Quit the loop if our module is found */
467 if (InitModule == Module) break;
468
469 /* Advance to the next entry */
470 InitEntry = InitEntry->Flink;
471 }
472
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,
479 FALSE);
480
481 /* Calculate OffsetToFileName field */
482 p = strrchr(ModulePtr->FullPathName, '\\');
483 if (p != NULL)
484 ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1;
485 else
486 ModulePtr->OffsetToFileName = 0;
487
488 /* Advance to the next module in the output list */
489 ModulePtr++;
490
491 /* Increase number of modules */
492 if (ModuleInformation)
493 ModuleInformation->NumberOfModules++;
494 }
495
496 /* Go to the next entry in the modules list */
497 Entry = Entry->Flink;
498 }
499
500 /* Set returned size if it was provided */
501 if (ReturnedSize)
502 *ReturnedSize = UsedSize;
503 }
504 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
505 {
506 /* Ignoring the exception */
507 } _SEH2_END;
508
509 /* Release the lock */
510 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
511
512 DPRINT("LdrQueryProcessModuleInformation() done\n");
513
514 return Status;
515 }
516
517 /*
518 * @implemented
519 */
520 NTSTATUS
521 NTAPI
522 LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation,
523 IN ULONG Size,
524 OUT PULONG ReturnedSize OPTIONAL)
525 {
526 /* Call Ex version of the API */
527 return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation, Size, ReturnedSize);
528 }
529
530 NTSTATUS
531 NTAPI
532 LdrEnumerateLoadedModules(BOOLEAN ReservedFlag, PLDR_ENUM_CALLBACK EnumProc, PVOID Context)
533 {
534 PLIST_ENTRY ListHead, ListEntry;
535 PLDR_DATA_TABLE_ENTRY LdrEntry;
536 NTSTATUS Status;
537 ULONG Cookie;
538 BOOLEAN Stop = FALSE;
539
540 /* Check parameters */
541 if (ReservedFlag || !EnumProc) return STATUS_INVALID_PARAMETER;
542
543 /* Acquire the loader lock */
544 Status = LdrLockLoaderLock(0, NULL, &Cookie);
545 if (!NT_SUCCESS(Status)) return Status;
546
547 /* Loop all the modules and call enum proc */
548 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
549 ListEntry = ListHead->Flink;
550 while (ListHead != ListEntry)
551 {
552 /* Get the entry */
553 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
554
555 /* Call the enumeration proc inside SEH */
556 _SEH2_TRY
557 {
558 EnumProc(LdrEntry, Context, &Stop);
559 }
560 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
561 {
562 /* Ignoring the exception */
563 } _SEH2_END;
564
565 /* Break if we were asked to stop enumeration */
566 if (Stop)
567 {
568 /* Release loader lock */
569 Status = LdrUnlockLoaderLock(0, Cookie);
570
571 /* Reset any successful status to STATUS_SUCCESS, but leave
572 failure to the caller */
573 if (NT_SUCCESS(Status))
574 Status = STATUS_SUCCESS;
575
576 /* Return any possible failure status */
577 return Status;
578 }
579
580 /* Advance to the next module */
581 ListEntry = ListEntry->Flink;
582 }
583
584 /* Release loader lock, it must succeed this time */
585 Status = LdrUnlockLoaderLock(0, Cookie);
586 ASSERT(NT_SUCCESS(Status));
587
588 /* Return success */
589 return STATUS_SUCCESS;
590 }
591
592 /* EOF */