cbff1792b2cdf6f7137689d2100e1a018d5cacdd
[reactos.git] / reactos / ntoskrnl / rtl / libsupp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/rtl/libsupp.c
5 * PURPOSE: RTL Support Routines
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Gunnar Dalsnes
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define TAG_ATMT 'TotA' /* Atom table */
17
18 extern ULONG NtGlobalFlag;
19
20 typedef struct _RTL_RANGE_ENTRY
21 {
22 LIST_ENTRY Entry;
23 RTL_RANGE Range;
24 } RTL_RANGE_ENTRY, *PRTL_RANGE_ENTRY;
25
26 PAGED_LOOKASIDE_LIST RtlpRangeListEntryLookasideList;
27 SIZE_T RtlpAllocDeallocQueryBufferSize = 128;
28
29 /* FUNCTIONS *****************************************************************/
30
31 VOID
32 NTAPI
33 RtlInitializeRangeListPackage(VOID)
34 {
35 /* Setup the lookaside list for allocations (not used yet) */
36 ExInitializePagedLookasideList(&RtlpRangeListEntryLookasideList,
37 NULL,
38 NULL,
39 POOL_COLD_ALLOCATION,
40 sizeof(RTL_RANGE_ENTRY),
41 'elRR',
42 16);
43 }
44
45 BOOLEAN
46 NTAPI
47 RtlpCheckForActiveDebugger(VOID)
48 {
49 /* This check is meaningless in kernel-mode */
50 return FALSE;
51 }
52
53 BOOLEAN
54 NTAPI
55 RtlpSetInDbgPrint(VOID)
56 {
57 /* Nothing to set in kernel mode */
58 return FALSE;
59 }
60
61 VOID
62 NTAPI
63 RtlpClearInDbgPrint(VOID)
64 {
65 /* Nothing to clear in kernel mode */
66 }
67
68 KPROCESSOR_MODE
69 NTAPI
70 RtlpGetMode()
71 {
72 return KernelMode;
73 }
74
75 PVOID
76 NTAPI
77 RtlpAllocateMemory(ULONG Bytes,
78 ULONG Tag)
79 {
80 return ExAllocatePoolWithTag(PagedPool,
81 (SIZE_T)Bytes,
82 Tag);
83 }
84
85
86 #define TAG_USTR 'RTSU'
87 #define TAG_ASTR 'RTSA'
88 #define TAG_OSTR 'RTSO'
89 VOID
90 NTAPI
91 RtlpFreeMemory(PVOID Mem,
92 ULONG Tag)
93 {
94 if (Tag == TAG_ASTR || Tag == TAG_OSTR || Tag == TAG_USTR)
95 ExFreePool(Mem);
96 else
97 ExFreePoolWithTag(Mem, Tag);
98 }
99
100 /*
101 * @implemented
102 */
103 VOID NTAPI
104 RtlAcquirePebLock(VOID)
105 {
106
107 }
108
109 /*
110 * @implemented
111 */
112 VOID NTAPI
113 RtlReleasePebLock(VOID)
114 {
115
116 }
117
118 NTSTATUS
119 NTAPI
120 LdrShutdownThread(VOID)
121 {
122 return STATUS_SUCCESS;
123 }
124
125
126 PPEB
127 NTAPI
128 RtlGetCurrentPeb(VOID)
129 {
130 return ((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->Peb;
131 }
132
133 NTSTATUS
134 NTAPI
135 RtlDeleteHeapLock(
136 PRTL_CRITICAL_SECTION CriticalSection)
137 {
138 ASSERT(FALSE);
139 return STATUS_SUCCESS;
140 }
141
142 NTSTATUS
143 NTAPI
144 RtlEnterHeapLock(
145 PRTL_CRITICAL_SECTION CriticalSection)
146 {
147 ASSERT(FALSE);
148 return STATUS_SUCCESS;
149 }
150
151 NTSTATUS
152 NTAPI
153 RtlInitializeHeapLock(
154 PRTL_CRITICAL_SECTION CriticalSection)
155 {
156 ASSERT(FALSE);
157 return STATUS_SUCCESS;
158 }
159
160 NTSTATUS
161 NTAPI
162 RtlLeaveHeapLock(
163 PRTL_CRITICAL_SECTION CriticalSection)
164 {
165 ASSERT(FALSE);
166 return STATUS_SUCCESS;
167 }
168
169 #if DBG
170 VOID FASTCALL
171 CHECK_PAGED_CODE_RTL(char *file, int line)
172 {
173 if(KeGetCurrentIrql() > APC_LEVEL)
174 {
175 DbgPrint("%s:%i: Pagable code called at IRQL > APC_LEVEL (%d)\n", file, line, KeGetCurrentIrql());
176 ASSERT(FALSE);
177 }
178 }
179 #endif
180
181 VOID
182 NTAPI
183 RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord,
184 IN PCONTEXT ContextRecord,
185 IN PVOID ContextData,
186 IN ULONG Size)
187 {
188 /* Check the global flag */
189 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING)
190 {
191 /* FIXME: Log this exception */
192 }
193 }
194
195 BOOLEAN
196 NTAPI
197 RtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame,
198 IN ULONG_PTR RegistrationFrameEnd,
199 IN OUT PULONG_PTR StackLow,
200 IN OUT PULONG_PTR StackHigh)
201 {
202 PKPRCB Prcb;
203 ULONG_PTR DpcStack;
204
205 /* Check if we are at DISPATCH or higher */
206 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
207 {
208 /* Get the PRCB and DPC Stack */
209 Prcb = KeGetCurrentPrcb();
210 DpcStack = (ULONG_PTR)Prcb->DpcStack;
211
212 /* Check if we are in a DPC and the stack matches */
213 if ((Prcb->DpcRoutineActive) &&
214 (RegistrationFrameEnd <= DpcStack) &&
215 ((ULONG_PTR)RegistrationFrame >= DpcStack - KERNEL_STACK_SIZE))
216 {
217 /* Update the limits to the DPC Stack's */
218 *StackHigh = DpcStack;
219 *StackLow = DpcStack - KERNEL_STACK_SIZE;
220 return TRUE;
221 }
222 }
223
224 /* Not in DPC stack */
225 return FALSE;
226 }
227
228 #if !defined(_ARM_) && !defined(_AMD64_)
229
230 BOOLEAN
231 NTAPI
232 RtlpCaptureStackLimits(IN ULONG_PTR Ebp,
233 IN ULONG_PTR *StackBegin,
234 IN ULONG_PTR *StackEnd)
235 {
236 PKTHREAD Thread = KeGetCurrentThread();
237
238 /* Don't even try at ISR level or later */
239 if (KeGetCurrentIrql() > DISPATCH_LEVEL) return FALSE;
240
241 /* Start with defaults */
242 *StackBegin = Thread->StackLimit;
243 *StackEnd = (ULONG_PTR)Thread->StackBase;
244
245 /* Check if EBP is inside the stack */
246 if ((*StackBegin <= Ebp) && (Ebp <= *StackEnd))
247 {
248 /* Then make the stack start at EBP */
249 *StackBegin = Ebp;
250 }
251 else
252 {
253 /* Now we're going to assume we're on the DPC stack */
254 *StackEnd = (ULONG_PTR)(KeGetPcr()->Prcb->DpcStack);
255 *StackBegin = *StackEnd - KERNEL_STACK_SIZE;
256
257 /* Check if we seem to be on the DPC stack */
258 if ((*StackEnd) && (*StackBegin < Ebp) && (Ebp <= *StackEnd))
259 {
260 /* We're on the DPC stack */
261 *StackBegin = Ebp;
262 }
263 else
264 {
265 /* We're somewhere else entirely... use EBP for safety */
266 *StackBegin = Ebp;
267 *StackEnd = (ULONG_PTR)PAGE_ALIGN(*StackBegin);
268 }
269 }
270
271 /* Return success */
272 return TRUE;
273 }
274
275 /*
276 * @implemented
277 */
278 ULONG
279 NTAPI
280 RtlWalkFrameChain(OUT PVOID *Callers,
281 IN ULONG Count,
282 IN ULONG Flags)
283 {
284 ULONG_PTR Stack, NewStack, StackBegin, StackEnd = 0;
285 ULONG Eip;
286 BOOLEAN Result, StopSearch = FALSE;
287 ULONG i = 0;
288 PETHREAD Thread = PsGetCurrentThread();
289 PTEB Teb;
290 PKTRAP_FRAME TrapFrame;
291
292 /* Get current EBP */
293 #if defined(_M_IX86)
294 #if defined __GNUC__
295 __asm__("mov %%ebp, %0" : "=r" (Stack) : );
296 #elif defined(_MSC_VER)
297 __asm mov Stack, ebp
298 #endif
299 #elif defined(_M_MIPS)
300 __asm__("move $sp, %0" : "=r" (Stack) : );
301 #elif defined(_M_PPC)
302 __asm__("mr %0,1" : "=r" (Stack) : );
303 #elif defined(_M_ARM)
304 __asm__("mov sp, %0" : "=r"(Stack) : );
305 #else
306 #error Unknown architecture
307 #endif
308
309 /* Set it as the stack begin limit as well */
310 StackBegin = (ULONG_PTR)Stack;
311
312 /* Check if we're called for non-logging mode */
313 if (!Flags)
314 {
315 /* Get the actual safe limits */
316 Result = RtlpCaptureStackLimits((ULONG_PTR)Stack,
317 &StackBegin,
318 &StackEnd);
319 if (!Result) return 0;
320 }
321
322 /* Use a SEH block for maximum protection */
323 _SEH2_TRY
324 {
325 /* Check if we want the user-mode stack frame */
326 if (Flags == 1)
327 {
328 /* Get the trap frame and TEB */
329 TrapFrame = KeGetTrapFrame(&Thread->Tcb);
330 Teb = Thread->Tcb.Teb;
331
332 /* Make sure we can trust the TEB and trap frame */
333 if (!(Teb) ||
334 (KeIsAttachedProcess()) ||
335 (KeGetCurrentIrql() >= DISPATCH_LEVEL))
336 {
337 /* Invalid or unsafe attempt to get the stack */
338 _SEH2_YIELD(return 0;)
339 }
340
341 /* Get the stack limits */
342 StackBegin = (ULONG_PTR)Teb->NtTib.StackLimit;
343 StackEnd = (ULONG_PTR)Teb->NtTib.StackBase;
344 #ifdef _M_IX86
345 Stack = TrapFrame->Ebp;
346 #elif defined(_M_PPC)
347 Stack = TrapFrame->Gpr1;
348 #else
349 #error Unknown architecture
350 #endif
351
352 /* Validate them */
353 if (StackEnd <= StackBegin) return 0;
354 ProbeForRead((PVOID)StackBegin,
355 StackEnd - StackBegin,
356 sizeof(CHAR));
357 }
358
359 /* Loop the frames */
360 for (i = 0; i < Count; i++)
361 {
362 /*
363 * Leave if we're past the stack,
364 * if we're before the stack,
365 * or if we've reached ourselves.
366 */
367 if ((Stack >= StackEnd) ||
368 (!i ? (Stack < StackBegin) : (Stack <= StackBegin)) ||
369 ((StackEnd - Stack) < (2 * sizeof(ULONG_PTR))))
370 {
371 /* We're done or hit a bad address */
372 break;
373 }
374
375 /* Get new stack and EIP */
376 NewStack = *(PULONG_PTR)Stack;
377 Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
378
379 /* Check if the new pointer is above the oldone and past the end */
380 if (!((Stack < NewStack) && (NewStack < StackEnd)))
381 {
382 /* Stop searching after this entry */
383 StopSearch = TRUE;
384 }
385
386 /* Also make sure that the EIP isn't a stack address */
387 if ((StackBegin < Eip) && (Eip < StackEnd)) break;
388
389 /* Check if we reached a user-mode address */
390 if (!(Flags) && !(Eip & 0x80000000)) break; // FIXME: 3GB breakage
391
392 /* Save this frame */
393 Callers[i] = (PVOID)Eip;
394
395 /* Check if we should continue */
396 if (StopSearch)
397 {
398 /* Return the next index */
399 i++;
400 break;
401 }
402
403 /* Move to the next stack */
404 Stack = NewStack;
405 }
406 }
407 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
408 {
409 /* No index */
410 i = 0;
411 }
412 _SEH2_END;
413
414 /* Return frames parsed */
415 return i;
416 }
417
418 #endif
419
420 /* RTL Atom Tables ************************************************************/
421
422 NTSTATUS
423 RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
424 {
425 ExInitializeFastMutex(&AtomTable->FastMutex);
426
427 return STATUS_SUCCESS;
428 }
429
430
431 VOID
432 RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
433 {
434 }
435
436
437 BOOLEAN
438 RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
439 {
440 ExAcquireFastMutex(&AtomTable->FastMutex);
441 return TRUE;
442 }
443
444 VOID
445 RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
446 {
447 ExReleaseFastMutex(&AtomTable->FastMutex);
448 }
449
450 BOOLEAN
451 RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
452 {
453 AtomTable->ExHandleTable = ExCreateHandleTable(NULL);
454 return (AtomTable->ExHandleTable != NULL);
455 }
456
457 VOID
458 RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
459 {
460 if (AtomTable->ExHandleTable)
461 {
462 ExSweepHandleTable(AtomTable->ExHandleTable,
463 NULL,
464 NULL);
465 ExDestroyHandleTable(AtomTable->ExHandleTable, NULL);
466 AtomTable->ExHandleTable = NULL;
467 }
468 }
469
470 PRTL_ATOM_TABLE
471 RtlpAllocAtomTable(ULONG Size)
472 {
473 PRTL_ATOM_TABLE Table = ExAllocatePool(NonPagedPool,
474 Size);
475 if (Table != NULL)
476 {
477 RtlZeroMemory(Table,
478 Size);
479 }
480
481 return Table;
482 }
483
484 VOID
485 RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)
486 {
487 ExFreePool(AtomTable);
488 }
489
490 PRTL_ATOM_TABLE_ENTRY
491 RtlpAllocAtomTableEntry(ULONG Size)
492 {
493 PRTL_ATOM_TABLE_ENTRY Entry;
494
495 Entry = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_ATMT);
496 if (Entry != NULL)
497 {
498 RtlZeroMemory(Entry, Size);
499 }
500
501 return Entry;
502 }
503
504 VOID
505 RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)
506 {
507 ExFreePoolWithTag(Entry, TAG_ATMT);
508 }
509
510 VOID
511 RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
512 {
513 ExDestroyHandle(AtomTable->ExHandleTable,
514 (HANDLE)((ULONG_PTR)Entry->HandleIndex << 2),
515 NULL);
516 }
517
518 BOOLEAN
519 RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
520 {
521 HANDLE_TABLE_ENTRY ExEntry;
522 HANDLE Handle;
523 USHORT HandleIndex;
524
525 /* Initialize ex handle table entry */
526 ExEntry.Object = Entry;
527 ExEntry.GrantedAccess = 0x1; /* FIXME - valid handle */
528
529 /* Create ex handle */
530 Handle = ExCreateHandle(AtomTable->ExHandleTable,
531 &ExEntry);
532 if (!Handle) return FALSE;
533
534 /* Calculate HandleIndex (by getting rid of the first two bits) */
535 HandleIndex = (USHORT)((ULONG_PTR)Handle >> 2);
536
537 /* Index must be less than 0xC000 */
538 if (HandleIndex >= 0xC000)
539 {
540 /* Destroy ex handle */
541 ExDestroyHandle(AtomTable->ExHandleTable,
542 Handle,
543 NULL);
544
545 /* Return failure */
546 return FALSE;
547 }
548
549 /* Initialize atom table entry */
550 Entry->HandleIndex = HandleIndex;
551 Entry->Atom = 0xC000 + HandleIndex;
552
553 /* Return success */
554 return TRUE;
555 }
556
557 PRTL_ATOM_TABLE_ENTRY
558 RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index)
559 {
560 PHANDLE_TABLE_ENTRY ExEntry;
561 PRTL_ATOM_TABLE_ENTRY Entry = NULL;
562
563 /* NOTE: There's no need to explicitly enter a critical region because it's
564 guaranteed that we're in a critical region right now (as we hold
565 the atom table lock) */
566
567 ExEntry = ExMapHandleToPointer(AtomTable->ExHandleTable,
568 (HANDLE)((ULONG_PTR)Index << 2));
569 if (ExEntry != NULL)
570 {
571 Entry = ExEntry->Object;
572
573 ExUnlockHandleTableEntry(AtomTable->ExHandleTable,
574 ExEntry);
575 }
576
577 return Entry;
578 }
579
580 /*
581 * Ldr Resource support code
582 */
583
584 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
585 LPCWSTR name, void *root,
586 int want_dir );
587 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
588 USHORT id, void *root, int want_dir );
589 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
590 void *root, int want_dir );
591
592 /**********************************************************************
593 * find_entry
594 *
595 * Find a resource entry
596 */
597 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
598 ULONG level, void **ret, int want_dir )
599 {
600 ULONG size;
601 void *root;
602 IMAGE_RESOURCE_DIRECTORY *resdirptr;
603
604 root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
605 if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
606 resdirptr = root;
607
608 if (!level--) goto done;
609 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
610 return STATUS_RESOURCE_TYPE_NOT_FOUND;
611 if (!level--) return STATUS_SUCCESS;
612
613 resdirptr = *ret;
614 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
615 return STATUS_RESOURCE_NAME_NOT_FOUND;
616 if (!level--) return STATUS_SUCCESS;
617 if (level) return STATUS_INVALID_PARAMETER; /* level > 3 */
618
619 resdirptr = *ret;
620
621 if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
622
623 return STATUS_RESOURCE_DATA_NOT_FOUND;
624
625 done:
626 *ret = resdirptr;
627 return STATUS_SUCCESS;
628 }
629
630
631 /* EOF */