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