792b763000df3a218d7ed2a4dca0a1b3726d8848
[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 #ifndef _ARM_
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 PKTHREAD Thread = KeGetCurrentThread();
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 = Thread->TrapFrame;
330 Teb = Thread->Teb;
331
332 /* Make sure we can trust the TEB and trap frame */
333 if (!(Teb) ||
334 !((PVOID)((ULONG_PTR)TrapFrame & 0x80000000)) ||
335 ((PVOID)TrapFrame <= (PVOID)Thread->StackLimit) ||
336 ((PVOID)TrapFrame >= (PVOID)Thread->StackBase) ||
337 (KeIsAttachedProcess()) ||
338 (KeGetCurrentIrql() >= DISPATCH_LEVEL))
339 {
340 /* Invalid or unsafe attempt to get the stack */
341 return 0;
342 }
343
344 /* Get the stack limits */
345 StackBegin = (ULONG_PTR)Teb->Tib.StackLimit;
346 StackEnd = (ULONG_PTR)Teb->Tib.StackBase;
347 #ifdef _M_IX86
348 Stack = TrapFrame->Ebp;
349 #elif defined(_M_PPC)
350 Stack = TrapFrame->Gpr1;
351 #else
352 #error Unknown architecture
353 #endif
354
355 /* Validate them */
356 if (StackEnd <= StackBegin) return 0;
357 ProbeForRead((PVOID)StackBegin,
358 StackEnd - StackBegin,
359 sizeof(CHAR));
360 }
361
362 /* Loop the frames */
363 for (i = 0; i < Count; i++)
364 {
365 /*
366 * Leave if we're past the stack,
367 * if we're before the stack,
368 * or if we've reached ourselves.
369 */
370 if ((Stack >= StackEnd) ||
371 (!i ? (Stack < StackBegin) : (Stack <= StackBegin)) ||
372 ((StackEnd - Stack) < (2 * sizeof(ULONG_PTR))))
373 {
374 /* We're done or hit a bad address */
375 break;
376 }
377
378 /* Get new stack and EIP */
379 NewStack = *(PULONG_PTR)Stack;
380 Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
381
382 /* Check if the new pointer is above the oldone and past the end */
383 if (!((Stack < NewStack) && (NewStack < StackEnd)))
384 {
385 /* Stop searching after this entry */
386 StopSearch = TRUE;
387 }
388
389 /* Also make sure that the EIP isn't a stack address */
390 if ((StackBegin < Eip) && (Eip < StackEnd)) break;
391
392 /* Check if we reached a user-mode address */
393 if (!(Flags) && !(Eip & 0x80000000)) break;
394
395 /* Save this frame */
396 Callers[i] = (PVOID)Eip;
397
398 /* Check if we should continue */
399 if (StopSearch)
400 {
401 /* Return the next index */
402 i++;
403 break;
404 }
405
406 /* Move to the next stack */
407 Stack = NewStack;
408 }
409 }
410 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
411 {
412 /* No index */
413 i = 0;
414 }
415 _SEH2_END;
416
417 /* Return frames parsed */
418 return i;
419 }
420
421 #endif
422
423 /* RTL Atom Tables ************************************************************/
424
425 NTSTATUS
426 RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
427 {
428 ExInitializeFastMutex(&AtomTable->FastMutex);
429
430 return STATUS_SUCCESS;
431 }
432
433
434 VOID
435 RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
436 {
437 }
438
439
440 BOOLEAN
441 RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
442 {
443 ExAcquireFastMutex(&AtomTable->FastMutex);
444 return TRUE;
445 }
446
447 VOID
448 RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
449 {
450 ExReleaseFastMutex(&AtomTable->FastMutex);
451 }
452
453 BOOLEAN
454 RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
455 {
456 AtomTable->ExHandleTable = ExCreateHandleTable(NULL);
457 return (AtomTable->ExHandleTable != NULL);
458 }
459
460 VOID
461 RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
462 {
463 if (AtomTable->ExHandleTable)
464 {
465 ExSweepHandleTable(AtomTable->ExHandleTable,
466 NULL,
467 NULL);
468 ExDestroyHandleTable(AtomTable->ExHandleTable, NULL);
469 AtomTable->ExHandleTable = NULL;
470 }
471 }
472
473 PRTL_ATOM_TABLE
474 RtlpAllocAtomTable(ULONG Size)
475 {
476 PRTL_ATOM_TABLE Table = ExAllocatePool(NonPagedPool,
477 Size);
478 if (Table != NULL)
479 {
480 RtlZeroMemory(Table,
481 Size);
482 }
483
484 return Table;
485 }
486
487 VOID
488 RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)
489 {
490 ExFreePool(AtomTable);
491 }
492
493 PRTL_ATOM_TABLE_ENTRY
494 RtlpAllocAtomTableEntry(ULONG Size)
495 {
496 PRTL_ATOM_TABLE_ENTRY Entry;
497
498 Entry = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_ATMT);
499 if (Entry != NULL)
500 {
501 RtlZeroMemory(Entry, Size);
502 }
503
504 return Entry;
505 }
506
507 VOID
508 RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)
509 {
510 ExFreePoolWithTag(Entry, TAG_ATMT);
511 }
512
513 VOID
514 RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
515 {
516 ExDestroyHandle(AtomTable->ExHandleTable,
517 (HANDLE)((ULONG_PTR)Entry->HandleIndex << 2),
518 NULL);
519 }
520
521 BOOLEAN
522 RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
523 {
524 HANDLE_TABLE_ENTRY ExEntry;
525 HANDLE Handle;
526 USHORT HandleIndex;
527
528 /* Initialize ex handle table entry */
529 ExEntry.Object = Entry;
530 ExEntry.GrantedAccess = 0x1; /* FIXME - valid handle */
531
532 /* Create ex handle */
533 Handle = ExCreateHandle(AtomTable->ExHandleTable,
534 &ExEntry);
535 if (!Handle) return FALSE;
536
537 /* Calculate HandleIndex (by getting rid of the first two bits) */
538 HandleIndex = (USHORT)((ULONG_PTR)Handle >> 2);
539
540 /* Index must be less than 0xC000 */
541 if (HandleIndex >= 0xC000)
542 {
543 /* Destroy ex handle */
544 ExDestroyHandle(AtomTable->ExHandleTable,
545 Handle,
546 NULL);
547
548 /* Return failure */
549 return FALSE;
550 }
551
552 /* Initialize atom table entry */
553 Entry->HandleIndex = HandleIndex;
554 Entry->Atom = 0xC000 + HandleIndex;
555
556 /* Return success */
557 return TRUE;
558 }
559
560 PRTL_ATOM_TABLE_ENTRY
561 RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index)
562 {
563 PHANDLE_TABLE_ENTRY ExEntry;
564 PRTL_ATOM_TABLE_ENTRY Entry = NULL;
565
566 /* NOTE: There's no need to explicitly enter a critical region because it's
567 guaranteed that we're in a critical region right now (as we hold
568 the atom table lock) */
569
570 ExEntry = ExMapHandleToPointer(AtomTable->ExHandleTable,
571 (HANDLE)((ULONG_PTR)Index << 2));
572 if (ExEntry != NULL)
573 {
574 Entry = ExEntry->Object;
575
576 ExUnlockHandleTableEntry(AtomTable->ExHandleTable,
577 ExEntry);
578 }
579
580 return Entry;
581 }
582
583 /*
584 * Ldr Resource support code
585 */
586
587 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
588 LPCWSTR name, void *root,
589 int want_dir );
590 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
591 USHORT id, void *root, int want_dir );
592 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
593 void *root, int want_dir );
594
595 /**********************************************************************
596 * find_entry
597 *
598 * Find a resource entry
599 */
600 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
601 ULONG level, void **ret, int want_dir )
602 {
603 ULONG size;
604 void *root;
605 IMAGE_RESOURCE_DIRECTORY *resdirptr;
606
607 root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
608 if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
609 resdirptr = root;
610
611 if (!level--) goto done;
612 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
613 return STATUS_RESOURCE_TYPE_NOT_FOUND;
614 if (!level--) return STATUS_SUCCESS;
615
616 resdirptr = *ret;
617 if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
618 return STATUS_RESOURCE_NAME_NOT_FOUND;
619 if (!level--) return STATUS_SUCCESS;
620 if (level) return STATUS_INVALID_PARAMETER; /* level > 3 */
621
622 resdirptr = *ret;
623
624 if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
625
626 return STATUS_RESOURCE_DATA_NOT_FOUND;
627
628 done:
629 *ret = resdirptr;
630 return STATUS_SUCCESS;
631 }
632
633
634 /* EOF */