1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User-Mode DLL
4 * FILE: lib/ntdll/rtl/libsup.c
5 * PURPOSE: RTL Support Routines
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Gunnar Dalsnes
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13 #include <apisets.h>
14 #include <compat_undoc.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 SIZE_T RtlpAllocDeallocQueryBufferSize = PAGE_SIZE;
20 PTEB LdrpTopLevelDllBeingLoadedTeb = NULL;
21 PVOID MmHighestUserAddress = (PVOID)MI_HIGHEST_USER_ADDRESS;
22
23 /* FUNCTIONS ***************************************************************/
24
25 BOOLEAN
26 NTAPI
RtlpCheckForActiveDebugger(VOID)27 RtlpCheckForActiveDebugger(VOID)
28 {
29 /* Return the flag in the PEB */
30 return NtCurrentPeb()->BeingDebugged;
31 }
32
33 BOOLEAN
34 NTAPI
RtlpSetInDbgPrint(VOID)35 RtlpSetInDbgPrint(VOID)
36 {
37 /* Check if it's already set and return TRUE if so */
38 if (NtCurrentTeb()->InDbgPrint) return TRUE;
39
40 /* Set it and return */
41 NtCurrentTeb()->InDbgPrint = TRUE;
42 return FALSE;
43 }
44
45 VOID
46 NTAPI
RtlpClearInDbgPrint(VOID)47 RtlpClearInDbgPrint(VOID)
48 {
49 /* Clear the flag */
50 NtCurrentTeb()->InDbgPrint = FALSE;
51 }
52
53 KPROCESSOR_MODE
54 NTAPI
RtlpGetMode(VOID)55 RtlpGetMode(VOID)
56 {
57 return UserMode;
58 }
59
60 /*
61 * @implemented
62 */
63 PPEB
64 NTAPI
RtlGetCurrentPeb(VOID)65 RtlGetCurrentPeb(VOID)
66 {
67 return NtCurrentPeb();
68 }
69
70 /*
71 * @implemented
72 */
73 VOID NTAPI
RtlAcquirePebLock(VOID)74 RtlAcquirePebLock(VOID)
75 {
76 PPEB Peb = NtCurrentPeb ();
77 RtlEnterCriticalSection(Peb->FastPebLock);
78 }
79
80 /*
81 * @implemented
82 */
83 VOID NTAPI
RtlReleasePebLock(VOID)84 RtlReleasePebLock(VOID)
85 {
86 PPEB Peb = NtCurrentPeb ();
87 RtlLeaveCriticalSection(Peb->FastPebLock);
88 }
89
90 /*
91 * @implemented
92 */
93 ULONG
94 NTAPI
RtlGetNtGlobalFlags(VOID)95 RtlGetNtGlobalFlags(VOID)
96 {
97 PPEB pPeb = NtCurrentPeb();
98 return pPeb->NtGlobalFlag;
99 }
100
101 NTSTATUS
102 NTAPI
RtlDeleteHeapLock(IN OUT PHEAP_LOCK Lock)103 RtlDeleteHeapLock(IN OUT PHEAP_LOCK Lock)
104 {
105 return RtlDeleteCriticalSection(&Lock->CriticalSection);
106 }
107
108 NTSTATUS
109 NTAPI
RtlEnterHeapLock(IN OUT PHEAP_LOCK Lock,IN BOOLEAN Exclusive)110 RtlEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
111 {
112 UNREFERENCED_PARAMETER(Exclusive);
113
114 return RtlEnterCriticalSection(&Lock->CriticalSection);
115 }
116
117 BOOLEAN
118 NTAPI
RtlTryEnterHeapLock(IN OUT PHEAP_LOCK Lock,IN BOOLEAN Exclusive)119 RtlTryEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
120 {
121 UNREFERENCED_PARAMETER(Exclusive);
122
123 return RtlTryEnterCriticalSection(&Lock->CriticalSection);
124 }
125
126 NTSTATUS
127 NTAPI
RtlInitializeHeapLock(IN OUT PHEAP_LOCK * Lock)128 RtlInitializeHeapLock(IN OUT PHEAP_LOCK *Lock)
129 {
130 return RtlInitializeCriticalSection(&(*Lock)->CriticalSection);
131 }
132
133 NTSTATUS
134 NTAPI
RtlLeaveHeapLock(IN OUT PHEAP_LOCK Lock)135 RtlLeaveHeapLock(IN OUT PHEAP_LOCK Lock)
136 {
137 return RtlLeaveCriticalSection(&Lock->CriticalSection);
138 }
139
140 PVOID
141 NTAPI
RtlpAllocateMemory(UINT Bytes,ULONG Tag)142 RtlpAllocateMemory(UINT Bytes,
143 ULONG Tag)
144 {
145 UNREFERENCED_PARAMETER(Tag);
146
147 return RtlAllocateHeap(RtlGetProcessHeap(),
148 0,
149 Bytes);
150 }
151
152
153 VOID
154 NTAPI
RtlpFreeMemory(PVOID Mem,ULONG Tag)155 RtlpFreeMemory(PVOID Mem,
156 ULONG Tag)
157 {
158 UNREFERENCED_PARAMETER(Tag);
159
160 RtlFreeHeap(RtlGetProcessHeap(),
161 0,
162 Mem);
163 }
164
165
166 #if DBG
167 VOID FASTCALL
CHECK_PAGED_CODE_RTL(char * file,int line)168 CHECK_PAGED_CODE_RTL(char *file, int line)
169 {
170 /* meaningless in user mode */
171 }
172 #endif
173
174 VOID
175 NTAPI
RtlpSetHeapParameters(IN PRTL_HEAP_PARAMETERS Parameters)176 RtlpSetHeapParameters(IN PRTL_HEAP_PARAMETERS Parameters)
177 {
178 PPEB Peb;
179
180 /* Get PEB */
181 Peb = RtlGetCurrentPeb();
182
183 /* Apply defaults for non-set parameters */
184 if (!Parameters->SegmentCommit) Parameters->SegmentCommit = Peb->HeapSegmentCommit;
185 if (!Parameters->SegmentReserve) Parameters->SegmentReserve = Peb->HeapSegmentReserve;
186 if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
187 if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
188 }
189
190 BOOLEAN
191 NTAPI
RtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame,IN ULONG_PTR RegistrationFrameEnd,IN OUT PULONG_PTR StackLow,IN OUT PULONG_PTR StackHigh)192 RtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame,
193 IN ULONG_PTR RegistrationFrameEnd,
194 IN OUT PULONG_PTR StackLow,
195 IN OUT PULONG_PTR StackHigh)
196 {
197 /* There's no such thing as a DPC stack in user-mode */
198 return FALSE;
199 }
200
201 VOID
202 NTAPI
RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT ContextRecord,IN PVOID ContextData,IN ULONG Size)203 RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord,
204 IN PCONTEXT ContextRecord,
205 IN PVOID ContextData,
206 IN ULONG Size)
207 {
208 /* Exception logging is not done in user-mode */
209 }
210
211 BOOLEAN
212 NTAPI
RtlpCaptureStackLimits(IN ULONG_PTR Ebp,IN ULONG_PTR * StackBegin,IN ULONG_PTR * StackEnd)213 RtlpCaptureStackLimits(IN ULONG_PTR Ebp,
214 IN ULONG_PTR *StackBegin,
215 IN ULONG_PTR *StackEnd)
216 {
217 /* FIXME: Verify */
218 *StackBegin = (ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit;
219 *StackEnd = (ULONG_PTR)NtCurrentTeb()->NtTib.StackBase;
220 return TRUE;
221 }
222
223 #ifndef _M_AMD64
224 /*
225 * @implemented
226 */
227 ULONG
228 NTAPI
RtlWalkFrameChain(OUT PVOID * Callers,IN ULONG Count,IN ULONG Flags)229 RtlWalkFrameChain(OUT PVOID *Callers,
230 IN ULONG Count,
231 IN ULONG Flags)
232 {
233 ULONG_PTR Stack, NewStack, StackBegin, StackEnd = 0;
234 ULONG Eip;
235 BOOLEAN Result, StopSearch = FALSE;
236 ULONG i = 0;
237
238 /* Get current EBP */
239 #if defined(_M_IX86)
240 #if defined __GNUC__
241 __asm__("mov %%ebp, %0" : "=r" (Stack) : );
242 #elif defined(_MSC_VER)
243 __asm mov Stack, ebp
244 #endif
245 #elif defined(_M_MIPS)
246 __asm__("move $sp, %0" : "=r" (Stack) : );
247 #elif defined(_M_PPC)
248 __asm__("mr %0,1" : "=r" (Stack) : );
249 #elif defined(_M_ARM)
250 #if defined __GNUC__
251 __asm__("mov sp, %0" : "=r"(Stack) : );
252 #elif defined(_MSC_VER)
253 // FIXME: Hack. Probably won't work if this ever actually manages to run someday.
254 Stack = (ULONG_PTR)&Stack;
255 #endif
256 #else
257 #error Unknown architecture
258 #endif
259
260 /* Set it as the stack begin limit as well */
261 StackBegin = (ULONG_PTR)Stack;
262
263 /* Check if we're called for non-logging mode */
264 if (!Flags)
265 {
266 /* Get the actual safe limits */
267 Result = RtlpCaptureStackLimits((ULONG_PTR)Stack,
268 &StackBegin,
269 &StackEnd);
270 if (!Result) return 0;
271 }
272
273 /* Use a SEH block for maximum protection */
274 _SEH2_TRY
275 {
276 /* Loop the frames */
277 for (i = 0; i < Count; i++)
278 {
279 /*
280 * Leave if we're past the stack,
281 * if we're before the stack,
282 * or if we've reached ourselves.
283 */
284 if ((Stack >= StackEnd) ||
285 (!i ? (Stack < StackBegin) : (Stack <= StackBegin)) ||
286 ((StackEnd - Stack) < (2 * sizeof(ULONG_PTR))))
287 {
288 /* We're done or hit a bad address */
289 break;
290 }
291
292 /* Get new stack and EIP */
293 NewStack = *(PULONG_PTR)Stack;
294 Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
295
296 /* Check if the new pointer is above the oldone and past the end */
297 if (!((Stack < NewStack) && (NewStack < StackEnd)))
298 {
299 /* Stop searching after this entry */
300 StopSearch = TRUE;
301 }
302
303 /* Also make sure that the EIP isn't a stack address */
304 if ((StackBegin < Eip) && (Eip < StackEnd)) break;
305
306 /* FIXME: Check that EIP is inside a loaded module */
307
308 /* Save this frame */
309 Callers[i] = (PVOID)Eip;
310
311 /* Check if we should continue */
312 if (StopSearch)
313 {
314 /* Return the next index */
315 i++;
316 break;
317 }
318
319 /* Move to the next stack */
320 Stack = NewStack;
321 }
322 }
323 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
324 {
325 /* No index */
326 i = 0;
327 }
328 _SEH2_END;
329
330 /* Return frames parsed */
331 return i;
332 }
333 #endif
334
335 VOID
336 NTAPI
RtlpGetStackLimits(OUT PULONG_PTR LowLimit,OUT PULONG_PTR HighLimit)337 RtlpGetStackLimits(
338 OUT PULONG_PTR LowLimit,
339 OUT PULONG_PTR HighLimit)
340 {
341 *LowLimit = (ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit;
342 *HighLimit = (ULONG_PTR)NtCurrentTeb()->NtTib.StackBase;
343 }
344
345 BOOLEAN
346 NTAPI
RtlIsThreadWithinLoaderCallout(VOID)347 RtlIsThreadWithinLoaderCallout(VOID)
348 {
349 return LdrpTopLevelDllBeingLoadedTeb == NtCurrentTeb();
350 }
351
352 /* RTL Atom Tables ************************************************************/
353
354 typedef struct _RTL_ATOM_HANDLE
355 {
356 RTL_HANDLE_TABLE_ENTRY Handle;
357 PRTL_ATOM_TABLE_ENTRY AtomEntry;
358 } RTL_ATOM_HANDLE, *PRTL_ATOM_HANDLE;
359
360 NTSTATUS
RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)361 RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
362 {
363 RtlInitializeCriticalSection(&AtomTable->CriticalSection);
364 return STATUS_SUCCESS;
365 }
366
367
368 VOID
RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)369 RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
370 {
371 RtlDeleteCriticalSection(&AtomTable->CriticalSection);
372 }
373
374
375 BOOLEAN
RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)376 RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
377 {
378 RtlEnterCriticalSection(&AtomTable->CriticalSection);
379 return TRUE;
380 }
381
382
383 VOID
RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)384 RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
385 {
386 RtlLeaveCriticalSection(&AtomTable->CriticalSection);
387 }
388
389
390 /* handle functions */
391
392 BOOLEAN
RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)393 RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
394 {
395 RtlInitializeHandleTable(0xCFFF,
396 sizeof(RTL_ATOM_HANDLE),
397 &AtomTable->RtlHandleTable);
398
399 return TRUE;
400 }
401
402 VOID
RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)403 RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
404 {
405 RtlDestroyHandleTable(&AtomTable->RtlHandleTable);
406 }
407
408 PRTL_ATOM_TABLE
RtlpAllocAtomTable(ULONG Size)409 RtlpAllocAtomTable(ULONG Size)
410 {
411 return (PRTL_ATOM_TABLE)RtlAllocateHeap(RtlGetProcessHeap(),
412 HEAP_ZERO_MEMORY,
413 Size);
414 }
415
416 VOID
RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)417 RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)
418 {
419 RtlFreeHeap(RtlGetProcessHeap(),
420 0,
421 AtomTable);
422 }
423
424 PRTL_ATOM_TABLE_ENTRY
RtlpAllocAtomTableEntry(ULONG Size)425 RtlpAllocAtomTableEntry(ULONG Size)
426 {
427 return (PRTL_ATOM_TABLE_ENTRY)RtlAllocateHeap(RtlGetProcessHeap(),
428 HEAP_ZERO_MEMORY,
429 Size);
430 }
431
432 VOID
RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)433 RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)
434 {
435 RtlFreeHeap(RtlGetProcessHeap(),
436 0,
437 Entry);
438 }
439
440 VOID
RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable,PRTL_ATOM_TABLE_ENTRY Entry)441 RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
442 {
443 PRTL_HANDLE_TABLE_ENTRY RtlHandleEntry;
444
445 if (RtlIsValidIndexHandle(&AtomTable->RtlHandleTable,
446 (ULONG)Entry->HandleIndex,
447 &RtlHandleEntry))
448 {
449 RtlFreeHandle(&AtomTable->RtlHandleTable,
450 RtlHandleEntry);
451 }
452 }
453
454 BOOLEAN
RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable,PRTL_ATOM_TABLE_ENTRY Entry)455 RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
456 {
457 ULONG HandleIndex;
458 PRTL_HANDLE_TABLE_ENTRY RtlHandle;
459
460 RtlHandle = RtlAllocateHandle(&AtomTable->RtlHandleTable,
461 &HandleIndex);
462 if (RtlHandle != NULL)
463 {
464 PRTL_ATOM_HANDLE AtomHandle = (PRTL_ATOM_HANDLE)RtlHandle;
465
466 /* FIXME - Handle Indexes >= 0xC000 ?! */
467 if (HandleIndex < 0xC000)
468 {
469 Entry->HandleIndex = (USHORT)HandleIndex;
470 Entry->Atom = 0xC000 + (USHORT)HandleIndex;
471
472 AtomHandle->AtomEntry = Entry;
473 AtomHandle->Handle.Flags = RTL_HANDLE_VALID;
474
475 return TRUE;
476 }
477 else
478 {
479 /* set the valid flag, otherwise RtlFreeHandle will fail! */
480 AtomHandle->Handle.Flags = RTL_HANDLE_VALID;
481
482 RtlFreeHandle(&AtomTable->RtlHandleTable,
483 RtlHandle);
484 }
485 }
486
487 return FALSE;
488 }
489
490 PRTL_ATOM_TABLE_ENTRY
RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable,ULONG Index)491 RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index)
492 {
493 PRTL_HANDLE_TABLE_ENTRY RtlHandle;
494
495 if (RtlIsValidIndexHandle(&AtomTable->RtlHandleTable,
496 Index,
497 &RtlHandle))
498 {
499 PRTL_ATOM_HANDLE AtomHandle = (PRTL_ATOM_HANDLE)RtlHandle;
500
501 return AtomHandle->AtomEntry;
502 }
503
504 return NULL;
505 }
506
507 /* Ldr SEH-Protected access to IMAGE_NT_HEADERS */
508
509 /* Rtl SEH-Free version of this */
510 NTSTATUS
511 NTAPI
512 RtlpImageNtHeaderEx(
513 _In_ ULONG Flags,
514 _In_ PVOID Base,
515 _In_ ULONG64 Size,
516 _Out_ PIMAGE_NT_HEADERS *OutHeaders);
517
518
519 /*
520 * @implemented
521 * @note: This is here, so that we do not drag SEH into rosload, freeldr and bootmgfw
522 */
523 NTSTATUS
524 NTAPI
RtlImageNtHeaderEx(_In_ ULONG Flags,_In_ PVOID Base,_In_ ULONG64 Size,_Out_ PIMAGE_NT_HEADERS * OutHeaders)525 RtlImageNtHeaderEx(
526 _In_ ULONG Flags,
527 _In_ PVOID Base,
528 _In_ ULONG64 Size,
529 _Out_ PIMAGE_NT_HEADERS *OutHeaders)
530 {
531 NTSTATUS Status;
532
533 /* Assume failure. This is also done in RtlpImageNtHeaderEx, but this is guarded by SEH. */
534 if (OutHeaders != NULL)
535 *OutHeaders = NULL;
536
537 _SEH2_TRY
538 {
539 Status = RtlpImageNtHeaderEx(Flags, Base, Size, OutHeaders);
540 }
541 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
542 {
543 /* Fail with the SEH error */
544 Status = _SEH2_GetExceptionCode();
545 }
546 _SEH2_END;
547
548 return Status;
549 }
550
551 /*
552 * Ldr Resource support code
553 */
554
555 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
556 LPCWSTR name, void *root,
557 int want_dir );
558 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
559 WORD id, void *root, int want_dir );
560 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
561 void *root, int want_dir );
562 int push_language( USHORT *list, ULONG pos, WORD lang );
563
564 /**********************************************************************
565 * find_entry
566 *
567 * Find a resource entry
568 */
find_entry(PVOID BaseAddress,LDR_RESOURCE_INFO * info,ULONG level,void ** ret,int want_dir)569 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
570 ULONG level, void **ret, int want_dir )
571 {
572 ULONG size;
573 void *root;
574 IMAGE_RESOURCE_DIRECTORY *resdirptr;
575 USHORT list[9]; /* list of languages to try */
576 int i, pos = 0;
577 LCID user_lcid, system_lcid;
578
579 root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
580 if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
581 if (size < sizeof(*resdirptr)) return STATUS_RESOURCE_DATA_NOT_FOUND;
582 resdirptr = root;
583
584 if (!level--) goto done;
585 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
586 return STATUS_RESOURCE_TYPE_NOT_FOUND;
587 if (!level--) return STATUS_SUCCESS;
588
589 resdirptr = *ret;
590 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
591 return STATUS_RESOURCE_NAME_NOT_FOUND;
592 if (!level--) return STATUS_SUCCESS;
593 if (level) return STATUS_INVALID_PARAMETER; /* level > 3 */
594
595 /* 1. specified language */
596 pos = push_language( list, pos, info->Language );
597
598 /* 2. specified language with neutral sublanguage */
599 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(info->Language), SUBLANG_NEUTRAL ) );
600
601 /* 3. neutral language with neutral sublanguage */
602 pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
603
604 /* if no explicitly specified language, try some defaults */
605 if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
606 {
607 /* user defaults, unless SYS_DEFAULT sublanguage specified */
608 if (SUBLANGID(info->Language) != SUBLANG_SYS_DEFAULT)
609 {
610 /* 4. current thread locale language */
611 pos = push_language( list, pos, LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) );
612
613 if (NT_SUCCESS(NtQueryDefaultLocale(TRUE, &user_lcid)))
614 {
615 /* 5. user locale language */
616 pos = push_language( list, pos, LANGIDFROMLCID(user_lcid) );
617
618 /* 6. user locale language with neutral sublanguage */
619 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(user_lcid), SUBLANG_NEUTRAL ) );
620 }
621 }
622
623 /* now system defaults */
624
625 if (NT_SUCCESS(NtQueryDefaultLocale(FALSE, &system_lcid)))
626 {
627 /* 7. system locale language */
628 pos = push_language( list, pos, LANGIDFROMLCID( system_lcid ) );
629
630 /* 8. system locale language with neutral sublanguage */
631 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(system_lcid), SUBLANG_NEUTRAL ) );
632 }
633
634 /* 9. English */
635 pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
636 }
637
638 resdirptr = *ret;
639 for (i = 0; i < pos; i++)
640 if ((*ret = find_entry_by_id( resdirptr, list[i], root, want_dir ))) return STATUS_SUCCESS;
641
642 /* if no explicitly specified language, return the first entry */
643 if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
644 {
645 if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
646 }
647 return STATUS_RESOURCE_LANG_NOT_FOUND;
648
649 done:
650 *ret = resdirptr;
651 return STATUS_SUCCESS;
652 }
653
654 /*
655 * @implemented
656 */
657 PVOID NTAPI
RtlPcToFileHeader(IN PVOID PcValue,PVOID * BaseOfImage)658 RtlPcToFileHeader(IN PVOID PcValue,
659 PVOID* BaseOfImage)
660 {
661 PLIST_ENTRY ModuleListHead;
662 PLIST_ENTRY Entry;
663 PLDR_DATA_TABLE_ENTRY Module;
664 PVOID ImageBase = NULL;
665
666 RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
667 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
668 Entry = ModuleListHead->Flink;
669 while (Entry != ModuleListHead)
670 {
671 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
672
673 if ((ULONG_PTR)PcValue >= (ULONG_PTR)Module->DllBase &&
674 (ULONG_PTR)PcValue < (ULONG_PTR)Module->DllBase + Module->SizeOfImage)
675 {
676 ImageBase = Module->DllBase;
677 break;
678 }
679 Entry = Entry->Flink;
680 }
681 RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
682
683 *BaseOfImage = ImageBase;
684 return ImageBase;
685 }
686
get_buffer(LPWSTR * buffer,SIZE_T needed,PUNICODE_STRING CallerBuffer,BOOLEAN bAllocateBuffer)687 NTSTATUS get_buffer(LPWSTR *buffer, SIZE_T needed, PUNICODE_STRING CallerBuffer, BOOLEAN bAllocateBuffer)
688 {
689 WCHAR *p;
690
691 if (CallerBuffer && CallerBuffer->MaximumLength > needed)
692 {
693 p = CallerBuffer->Buffer;
694 CallerBuffer->Length = needed - sizeof(WCHAR);
695 }
696 else
697 {
698 if (!bAllocateBuffer)
699 return STATUS_BUFFER_TOO_SMALL;
700
701 if (CallerBuffer)
702 CallerBuffer->Buffer[0] = 0;
703
704 p = RtlAllocateHeap(RtlGetProcessHeap(), 0, needed );
705 if (!p)
706 return STATUS_NO_MEMORY;
707 }
708 *buffer = p;
709
710 return STATUS_SUCCESS;
711 }
712
713 /* NOTE: Remove this one once our actctx support becomes better */
find_actctx_dll(PUNICODE_STRING pnameW,LPWSTR * fullname,PUNICODE_STRING CallerBuffer,BOOLEAN bAllocateBuffer)714 NTSTATUS find_actctx_dll( PUNICODE_STRING pnameW, LPWSTR *fullname, PUNICODE_STRING CallerBuffer, BOOLEAN bAllocateBuffer)
715 {
716 static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\'};
717 static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0};
718
719 ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *info;
720 ACTCTX_SECTION_KEYED_DATA data;
721 NTSTATUS status;
722 SIZE_T needed, size = 1024;
723 WCHAR *p;
724
725 data.cbSize = sizeof(data);
726 status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
727 ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
728 pnameW, &data );
729 if (status != STATUS_SUCCESS)
730 {
731 //DPRINT1("RtlFindActivationContextSectionString returned 0x%x for %wZ\n", status, pnameW);
732 return status;
733 }
734
735 for (;;)
736 {
737 if (!(info = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
738 {
739 status = STATUS_NO_MEMORY;
740 goto done;
741 }
742 status = RtlQueryInformationActivationContext( 0, data.hActCtx, &data.ulAssemblyRosterIndex,
743 AssemblyDetailedInformationInActivationContext,
744 info, size, &needed );
745 if (status == STATUS_SUCCESS) break;
746 if (status != STATUS_BUFFER_TOO_SMALL) goto done;
747 RtlFreeHeap( RtlGetProcessHeap(), 0, info );
748 size = needed;
749 }
750
751 DPRINT("manifestpath === %S\n", info->lpAssemblyManifestPath);
752 DPRINT("DirectoryName === %S\n", info->lpAssemblyDirectoryName);
753 if (!info->lpAssemblyManifestPath /*|| !info->lpAssemblyDirectoryName*/)
754 {
755 status = STATUS_SXS_KEY_NOT_FOUND;
756 goto done;
757 }
758
759 if ((p = wcsrchr( info->lpAssemblyManifestPath, '\\' )))
760 {
761 DWORD dirlen = info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
762
763 p++;
764 if (!info->lpAssemblyDirectoryName || _wcsnicmp( p, info->lpAssemblyDirectoryName, dirlen ) || _wcsicmp( p + dirlen, dotManifestW ))
765 {
766 /* manifest name does not match directory name, so it's not a global
767 * windows/winsxs manifest; use the manifest directory name instead */
768 dirlen = p - info->lpAssemblyManifestPath;
769 needed = (dirlen + 1) * sizeof(WCHAR) + pnameW->Length;
770
771 status = get_buffer(fullname, needed, CallerBuffer, bAllocateBuffer);
772 if (!NT_SUCCESS(status))
773 goto done;
774
775 p = *fullname;
776
777 memcpy( p, info->lpAssemblyManifestPath, dirlen * sizeof(WCHAR) );
778 p += dirlen;
779 memcpy( p, pnameW->Buffer, pnameW->Length);
780 p += (pnameW->Length / sizeof(WCHAR));
781 *p = L'\0';
782
783 goto done;
784 }
785 }
786
787 needed = (wcslen(SharedUserData->NtSystemRoot) * sizeof(WCHAR) +
788 sizeof(winsxsW) + info->ulAssemblyDirectoryNameLength + pnameW->Length + 2*sizeof(WCHAR));
789
790 status = get_buffer(fullname, needed, CallerBuffer, bAllocateBuffer);
791 if (!NT_SUCCESS(status))
792 goto done;
793
794 p = *fullname;
795
796 wcscpy( p, SharedUserData->NtSystemRoot );
797 p += wcslen(p);
798 memcpy( p, winsxsW, sizeof(winsxsW) );
799 p += sizeof(winsxsW) / sizeof(WCHAR);
800 memcpy( p, info->lpAssemblyDirectoryName, info->ulAssemblyDirectoryNameLength );
801 p += info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
802 *p++ = L'\\';
803 memcpy( p, pnameW->Buffer, pnameW->Length);
804 p += (pnameW->Length / sizeof(WCHAR));
805 *p = L'\0';
806
807 done:
808 RtlFreeHeap( RtlGetProcessHeap(), 0, info );
809 RtlReleaseActivationContext( data.hActCtx );
810 DPRINT("%S\n", fullname);
811 return status;
812 }
813
814 /*
815 * @unimplemented
816 */
817 NTSYSAPI
818 NTSTATUS
819 NTAPI
RtlDosApplyFileIsolationRedirection_Ustr(IN ULONG Flags,IN PUNICODE_STRING OriginalName,IN PUNICODE_STRING Extension,IN OUT PUNICODE_STRING StaticString,IN OUT PUNICODE_STRING DynamicString,IN OUT PUNICODE_STRING * NewName,IN PULONG NewFlags,IN PSIZE_T FileNameSize,IN PSIZE_T RequiredLength)820 RtlDosApplyFileIsolationRedirection_Ustr(IN ULONG Flags,
821 IN PUNICODE_STRING OriginalName,
822 IN PUNICODE_STRING Extension,
823 IN OUT PUNICODE_STRING StaticString,
824 IN OUT PUNICODE_STRING DynamicString,
825 IN OUT PUNICODE_STRING *NewName,
826 IN PULONG NewFlags,
827 IN PSIZE_T FileNameSize,
828 IN PSIZE_T RequiredLength)
829 {
830 NTSTATUS Status;
831 LPWSTR fullname;
832 WCHAR buffer [MAX_PATH];
833 UNICODE_STRING localStr, localStr2, *pstrParam;
834 WCHAR *p;
835 BOOLEAN GotExtension;
836 WCHAR c;
837 C_ASSERT(sizeof(UNICODE_NULL) == sizeof(WCHAR));
838
839
840 /* Check for invalid parameters */
841 if (!OriginalName)
842 {
843 return STATUS_INVALID_PARAMETER;
844 }
845
846 if (!DynamicString && !StaticString)
847 {
848 return STATUS_INVALID_PARAMETER;
849 }
850
851 if ((DynamicString) && (StaticString) && !(NewName))
852 {
853 return STATUS_INVALID_PARAMETER;
854 }
855
856 if (!OriginalName->Buffer || OriginalName->Length == 0)
857 {
858 return STATUS_SXS_KEY_NOT_FOUND;
859 }
860
861 if (StaticString && (OriginalName == StaticString || OriginalName->Buffer == StaticString->Buffer))
862 {
863 return STATUS_SXS_KEY_NOT_FOUND;
864 }
865
866 if ((Flags & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL) &&
867 NtCurrentPeb()->ProcessParameters &&
868 (NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_PRIVATE_DLL_PATH))
869 {
870 UNICODE_STRING RealName, LocalName;
871 WCHAR RealNameBuf[MAX_PATH], LocalNameBuf[MAX_PATH];
872
873 RtlInitEmptyUnicodeString(&RealName, RealNameBuf, sizeof(RealNameBuf));
874 RtlInitEmptyUnicodeString(&LocalName, LocalNameBuf, sizeof(LocalNameBuf));
875
876 Status = RtlComputePrivatizedDllName_U(OriginalName, &RealName, &LocalName);
877 if (!NT_SUCCESS(Status))
878 {
879 DPRINT1("RtlComputePrivatizedDllName_U failed for %wZ: 0x%lx\n", OriginalName, Status);
880 return Status;
881 }
882
883 if (RtlDoesFileExists_UStr(&LocalName))
884 {
885 Status = get_buffer(&fullname, LocalName.Length + sizeof(UNICODE_NULL), StaticString, DynamicString != NULL);
886 if (NT_SUCCESS(Status))
887 {
888 RtlCopyMemory(fullname, LocalName.Buffer, LocalName.Length + sizeof(UNICODE_NULL));
889 }
890 else
891 {
892 DPRINT1("Error while retrieving buffer for %wZ: 0x%lx\n", OriginalName, Status);
893 }
894 }
895 else if (RtlDoesFileExists_UStr(&RealName))
896 {
897 Status = get_buffer(&fullname, RealName.Length + sizeof(UNICODE_NULL), StaticString, DynamicString != NULL);
898 if (NT_SUCCESS(Status))
899 {
900 RtlCopyMemory(fullname, RealName.Buffer, RealName.Length + sizeof(UNICODE_NULL));
901 }
902 else
903 {
904 DPRINT1("Error while retrieving buffer for %wZ: 0x%lx\n", OriginalName, Status);
905 }
906 }
907 else
908 {
909 Status = STATUS_NOT_FOUND;
910 }
911
912 if (RealName.Buffer != RealNameBuf)
913 RtlFreeUnicodeString(&RealName);
914 if (LocalName.Buffer != LocalNameBuf)
915 RtlFreeUnicodeString(&LocalName);
916
917 if (NT_SUCCESS(Status))
918 {
919 DPRINT("Redirecting %wZ to %S\n", OriginalName, fullname);
920 if (!StaticString || StaticString->Buffer != fullname)
921 {
922 RtlInitUnicodeString(DynamicString, fullname);
923 *NewName = DynamicString;
924 }
925 else
926 {
927 *NewName = StaticString;
928 }
929 return Status;
930 }
931 }
932
933 pstrParam = OriginalName;
934
935 /* Get the file name with an extension */
936 p = OriginalName->Buffer + OriginalName->Length / sizeof(WCHAR) - 1;
937 GotExtension = FALSE;
938 while (p >= OriginalName->Buffer)
939 {
940 c = *p--;
941 if (c == L'.')
942 {
943 GotExtension = TRUE;
944 }
945 else if (c == L'\\')
946 {
947 localStr.Buffer = p + 2;
948 localStr.Length = OriginalName->Length - ((ULONG_PTR)localStr.Buffer - (ULONG_PTR)OriginalName->Buffer);
949 localStr.MaximumLength = OriginalName->MaximumLength - ((ULONG_PTR)localStr.Buffer - (ULONG_PTR)OriginalName->Buffer);
950 pstrParam = &localStr;
951 break;
952 }
953 }
954
955 if (!GotExtension)
956 {
957 if (!Extension)
958 {
959 return STATUS_SXS_KEY_NOT_FOUND;
960 }
961
962 if (pstrParam->Length + Extension->Length > sizeof(buffer))
963 {
964 //FIXME!
965 return STATUS_NO_MEMORY;
966 }
967
968 RtlInitEmptyUnicodeString(&localStr2, buffer, sizeof(buffer));
969 RtlAppendUnicodeStringToString(&localStr2, pstrParam);
970 RtlAppendUnicodeStringToString(&localStr2, Extension);
971 pstrParam = &localStr2;
972 }
973
974 /* Use wine's function as long as we use wine's sxs implementation in ntdll */
975 Status = find_actctx_dll(pstrParam, &fullname, StaticString, DynamicString != NULL);
976 if (!NT_SUCCESS(Status))
977 {
978 return Status;
979 }
980
981 DPRINT("Redirecting %wZ to %S\n", OriginalName, fullname);
982
983 if (!StaticString || StaticString->Buffer != fullname)
984 {
985 RtlInitUnicodeString(DynamicString, fullname);
986 *NewName = DynamicString;
987 }
988 else
989 {
990 *NewName = StaticString;
991 }
992
993 return Status;
994 }
995
996 #ifndef TAG_USTR
997 #define TAG_USTR 'RTSU'
998 #endif
999 #ifndef RtlpAllocateStringMemory
1000 #define RtlpAllocateStringMemory(Bytes, Tag) RtlpAllocateMemory(Bytes, Tag)
1001 #endif
1002
1003 static DWORD
LdrpApisetVersion(VOID)1004 LdrpApisetVersion(VOID)
1005 {
1006 static DWORD CachedApisetVersion = ~0u;
1007
1008 if (CachedApisetVersion == ~0u)
1009 {
1010 DWORD CompatVersion = RosGetProcessCompatVersion();
1011
1012 switch (CompatVersion)
1013 {
1014 case 0:
1015 break;
1016 case _WIN32_WINNT_VISTA:
1017 /* No apisets in vista yet*/
1018 CachedApisetVersion = 0;
1019 break;
1020 case _WIN32_WINNT_WIN7:
1021 CachedApisetVersion = APISET_WIN7;
1022 DPRINT1("Activating apisets for Win7\n");
1023 break;
1024 case _WIN32_WINNT_WIN8:
1025 CachedApisetVersion = APISET_WIN8;
1026 DPRINT1("Activating apisets for Win8\n");
1027 break;
1028 case _WIN32_WINNT_WINBLUE:
1029 CachedApisetVersion = APISET_WIN81;
1030 DPRINT1("Activating apisets for Win8.1\n");
1031 break;
1032 case _WIN32_WINNT_WIN10:
1033 CachedApisetVersion = APISET_WIN10;
1034 DPRINT1("Activating apisets for Win10\n");
1035 break;
1036 default:
1037 DPRINT1("Unknown version 0x%x\n", CompatVersion);
1038 CachedApisetVersion = 0;
1039 break;
1040 }
1041 }
1042
1043 return CachedApisetVersion;
1044 }
1045
1046 NTSYSAPI
1047 NTSTATUS
1048 NTAPI
LdrpApplyFileNameRedirection(_In_ PUNICODE_STRING OriginalName,_In_ PUNICODE_STRING Extension,_Inout_opt_ PUNICODE_STRING StaticString,_Inout_opt_ PUNICODE_STRING DynamicString,_Inout_ PUNICODE_STRING * NewName,_Out_ PBOOLEAN RedirectedDll)1049 LdrpApplyFileNameRedirection(
1050 _In_ PUNICODE_STRING OriginalName,
1051 _In_ PUNICODE_STRING Extension,
1052 _Inout_opt_ PUNICODE_STRING StaticString,
1053 _Inout_opt_ PUNICODE_STRING DynamicString,
1054 _Inout_ PUNICODE_STRING *NewName,
1055 _Out_ PBOOLEAN RedirectedDll)
1056 {
1057
1058 /* Check for invalid parameters */
1059 if (!OriginalName)
1060 {
1061 return STATUS_INVALID_PARAMETER;
1062 }
1063
1064 if (!DynamicString && !StaticString)
1065 {
1066 return STATUS_INVALID_PARAMETER;
1067 }
1068
1069 if (!NewName)
1070 {
1071 return STATUS_INVALID_PARAMETER;
1072 }
1073
1074 *RedirectedDll = FALSE;
1075
1076 PCUNICODE_STRING PrevNewName = *NewName;
1077 UNICODE_STRING ApisetName = {0};
1078 NTSTATUS Status = STATUS_SUCCESS;
1079
1080 DWORD ApisetVersion = LdrpApisetVersion();
1081 if (ApisetVersion)
1082 {
1083 Status = ApiSetResolveToHost(ApisetVersion, OriginalName, RedirectedDll, &ApisetName);
1084 if (!NT_SUCCESS(Status))
1085 {
1086 DPRINT1("ApiSetResolveToHost FAILED: (Status 0x%x)\n", Status);
1087 return Status;
1088 }
1089 }
1090
1091 if (*RedirectedDll)
1092 {
1093 UNICODE_STRING NtSystemRoot;
1094 static const UNICODE_STRING System32 = RTL_CONSTANT_STRING(L"\\System32\\");
1095 PUNICODE_STRING ResultPath = NULL;
1096
1097 /* This is an apiset we can use */
1098 RtlInitUnicodeString(&NtSystemRoot, SharedUserData->NtSystemRoot);
1099
1100 SIZE_T Needed = System32.Length + ApisetName.Length + NtSystemRoot.Length + sizeof(UNICODE_NULL);
1101
1102 if (StaticString && StaticString->MaximumLength >= (USHORT)Needed)
1103 {
1104 StaticString->Length = 0;
1105 ResultPath = StaticString;
1106 }
1107 else if (DynamicString)
1108 {
1109 DynamicString->Buffer = RtlpAllocateStringMemory(Needed, TAG_USTR);
1110 if (DynamicString->Buffer == NULL)
1111 {
1112 DPRINT1("LdrpApplyFileNameRedirection out of memory\n");
1113 return STATUS_NO_MEMORY;
1114 }
1115 DynamicString->MaximumLength = (USHORT)Needed;
1116 DynamicString->Length = 0;
1117
1118 ResultPath = DynamicString;
1119 }
1120 else
1121 {
1122 DPRINT1("ERROR: LdrpApplyFileNameRedirection no inputbuffer valid\n");
1123 return STATUS_INVALID_PARAMETER;
1124 }
1125
1126 RtlAppendUnicodeStringToString(ResultPath, &NtSystemRoot);
1127 RtlAppendUnicodeStringToString(ResultPath, &System32);
1128 RtlAppendUnicodeStringToString(ResultPath, &ApisetName);
1129 DPRINT1("ApiSetResolveToHost redirected %wZ to %wZ\n", OriginalName, ResultPath);
1130 *NewName = ResultPath;
1131 }
1132 else
1133 {
1134 /* Check if the SxS Assemblies specify another file */
1135 Status = RtlDosApplyFileIsolationRedirection_Ustr(RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, OriginalName, Extension, StaticString, DynamicString, NewName, NULL, NULL, NULL);
1136
1137 /* Check success */
1138 if (NT_SUCCESS(Status))
1139 {
1140 /* Let Ldrp know */
1141 *RedirectedDll = TRUE;
1142 }
1143 else if (Status == STATUS_SXS_KEY_NOT_FOUND)
1144 {
1145 ASSERT(*NewName == PrevNewName);
1146 Status = STATUS_SUCCESS;
1147 }
1148 else
1149 {
1150 /* Unrecoverable SxS failure; did we get a string? */
1151 if (DynamicString && DynamicString->Buffer)
1152 RtlFreeUnicodeString(DynamicString);
1153 return Status;
1154 }
1155 }
1156
1157 return Status;
1158 }
1159
1160 /*
1161 * @implemented
1162 */
1163 NTSYSAPI
1164 NTSTATUS
1165 NTAPI
RtlWow64EnableFsRedirection(IN BOOLEAN Wow64FsEnableRedirection)1166 RtlWow64EnableFsRedirection(IN BOOLEAN Wow64FsEnableRedirection)
1167 {
1168 /* This is what Windows returns on x86 */
1169 return STATUS_NOT_IMPLEMENTED;
1170 }
1171
1172 /*
1173 * @implemented
1174 */
1175 NTSYSAPI
1176 NTSTATUS
1177 NTAPI
RtlWow64EnableFsRedirectionEx(IN PVOID Wow64FsEnableRedirection,OUT PVOID * OldFsRedirectionLevel)1178 RtlWow64EnableFsRedirectionEx(IN PVOID Wow64FsEnableRedirection,
1179 OUT PVOID *OldFsRedirectionLevel)
1180 {
1181 /* This is what Windows returns on x86 */
1182 return STATUS_NOT_IMPLEMENTED;
1183 }
1184
1185 /*
1186 * @unimplemented
1187 */
1188 NTSYSAPI
1189 NTSTATUS
1190 NTAPI
RtlComputeImportTableHash(IN HANDLE FileHandle,OUT PCHAR Hash,IN ULONG ImportTableHashSize)1191 RtlComputeImportTableHash(IN HANDLE FileHandle,
1192 OUT PCHAR Hash,
1193 IN ULONG ImportTableHashSize)
1194 {
1195 UNIMPLEMENTED;
1196 return STATUS_NOT_IMPLEMENTED;
1197 }
1198
1199 NTSTATUS
1200 NTAPI
RtlpSafeCopyMemory(_Out_writes_bytes_all_ (Length)VOID UNALIGNED * Destination,_In_reads_bytes_ (Length)CONST VOID UNALIGNED * Source,_In_ SIZE_T Length)1201 RtlpSafeCopyMemory(
1202 _Out_writes_bytes_all_(Length) VOID UNALIGNED *Destination,
1203 _In_reads_bytes_(Length) CONST VOID UNALIGNED *Source,
1204 _In_ SIZE_T Length)
1205 {
1206 _SEH2_TRY
1207 {
1208 RtlCopyMemory(Destination, Source, Length);
1209 }
1210 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1211 {
1212 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1213 }
1214 _SEH2_END;
1215
1216 return STATUS_SUCCESS;
1217 }
1218
1219 /* FIXME: code duplication with kernel32/client/time.c */
1220 ULONG
1221 NTAPI
RtlGetTickCount(VOID)1222 RtlGetTickCount(VOID)
1223 {
1224 ULARGE_INTEGER TickCount;
1225
1226 #ifdef _WIN64
1227 TickCount.QuadPart = *((volatile ULONG64*)&SharedUserData->TickCount);
1228 #else
1229 while (TRUE)
1230 {
1231 TickCount.HighPart = (ULONG)SharedUserData->TickCount.High1Time;
1232 TickCount.LowPart = SharedUserData->TickCount.LowPart;
1233
1234 if (TickCount.HighPart == (ULONG)SharedUserData->TickCount.High2Time)
1235 break;
1236
1237 YieldProcessor();
1238 }
1239 #endif
1240
1241 return (ULONG)((UInt32x32To64(TickCount.LowPart,
1242 SharedUserData->TickCountMultiplier) >> 24) +
1243 UInt32x32To64((TickCount.HighPart << 8) & 0xFFFFFFFF,
1244 SharedUserData->TickCountMultiplier));
1245 }
1246
1247 /* EOF */
1248