Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/rtl/libsupp.c * PURPOSE: RTL Support Routines * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) * Gunnar Dalsnes */
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>#define NDEBUG#include <debug.h>
extern ULONG NtGlobalFlag;
typedef struct _RTL_RANGE_ENTRY{ LIST_ENTRY Entry; RTL_RANGE Range;} RTL_RANGE_ENTRY, *PRTL_RANGE_ENTRY;
PAGED_LOOKASIDE_LIST RtlpRangeListEntryLookasideList;SIZE_T RtlpAllocDeallocQueryBufferSize = 128;
/* FUNCTIONS *****************************************************************/
PVOIDNTAPIRtlPcToFileHeader( IN PVOID PcValue, OUT PVOID *BaseOfImage){ PLDR_DATA_TABLE_ENTRY LdrEntry; BOOLEAN InSystem; KIRQL OldIrql;
/* Get the base for this file */ if ((ULONG_PTR)PcValue > (ULONG_PTR)MmHighestUserAddress) { /* Acquire the loaded module spinlock */ KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
/* We are in kernel */ *BaseOfImage = KiPcToFileHeader(PcValue, &LdrEntry, FALSE, &InSystem);
/* Release lock */ KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql); } else { /* User mode is not handled here! */ *BaseOfImage = NULL; }
return *BaseOfImage;}
VOIDNTAPIRtlInitializeRangeListPackage(VOID){ /* Setup the lookaside list for allocations (not used yet) */ ExInitializePagedLookasideList(&RtlpRangeListEntryLookasideList, NULL, NULL, POOL_COLD_ALLOCATION, sizeof(RTL_RANGE_ENTRY), 'elRR', 16);}
BOOLEANNTAPIRtlpCheckForActiveDebugger(VOID){ /* This check is meaningless in kernel-mode */ return FALSE;}
BOOLEANNTAPIRtlpSetInDbgPrint(VOID){ /* Nothing to set in kernel mode */ return FALSE;}
VOIDNTAPIRtlpClearInDbgPrint(VOID){ /* Nothing to clear in kernel mode */}
KPROCESSOR_MODENTAPIRtlpGetMode(VOID){ return KernelMode;}
PVOIDNTAPIRtlpAllocateMemory(ULONG Bytes, ULONG Tag){ return ExAllocatePoolWithTag(PagedPool, (SIZE_T)Bytes, Tag);}
VOIDNTAPIRtlpFreeMemory(PVOID Mem, ULONG Tag){ if (Tag == TAG_ASTR || Tag == TAG_OSTR || Tag == TAG_USTR) ExFreePool(Mem); else ExFreePoolWithTag(Mem, Tag);}
/* * @implemented */VOID NTAPIRtlAcquirePebLock(VOID){
}
/* * @implemented */VOID NTAPIRtlReleasePebLock(VOID){
}
NTSTATUSNTAPILdrShutdownThread(VOID){ return STATUS_SUCCESS;}
PPEBNTAPIRtlGetCurrentPeb(VOID){ return ((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->Peb;}
NTSTATUSNTAPIRtlDeleteHeapLock(IN OUT PHEAP_LOCK Lock){ ExDeleteResourceLite(&Lock->Resource); ExFreePoolWithTag(Lock, TAG_RTHL);
return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive){ KeEnterCriticalRegion();
if (Exclusive) ExAcquireResourceExclusiveLite(&Lock->Resource, TRUE); else ExAcquireResourceSharedLite(&Lock->Resource, TRUE);
return STATUS_SUCCESS;}
BOOLEANNTAPIRtlTryEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive){ BOOLEAN Success; KeEnterCriticalRegion();
if (Exclusive) Success = ExAcquireResourceExclusiveLite(&Lock->Resource, FALSE); else Success = ExAcquireResourceSharedLite(&Lock->Resource, FALSE);
if (!Success) KeLeaveCriticalRegion();
return Success;}
NTSTATUSNTAPIRtlInitializeHeapLock(IN OUT PHEAP_LOCK *Lock){ PHEAP_LOCK HeapLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(HEAP_LOCK), TAG_RTHL); if (HeapLock == NULL) return STATUS_NO_MEMORY;
ExInitializeResourceLite(&HeapLock->Resource); *Lock = HeapLock;
return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlLeaveHeapLock(IN OUT PHEAP_LOCK Lock){ ExReleaseResourceLite(&Lock->Resource); KeLeaveCriticalRegion();
return STATUS_SUCCESS;}
struct _HEAP;
VOIDNTAPIRtlpAddHeapToProcessList(struct _HEAP *Heap){ UNREFERENCED_PARAMETER(Heap);}
VOIDNTAPIRtlpRemoveHeapFromProcessList(struct _HEAP *Heap){ UNREFERENCED_PARAMETER(Heap);}
VOIDRtlInitializeHeapManager(VOID){}
#if DBGVOID FASTCALLCHECK_PAGED_CODE_RTL(char *file, int line){ if(KeGetCurrentIrql() > APC_LEVEL) { DbgPrint("%s:%i: Pagable code called at IRQL > APC_LEVEL (%u)\n", file, line, KeGetCurrentIrql()); ASSERT(FALSE); }}#endif
VOIDNTAPIRtlpSetHeapParameters(IN PRTL_HEAP_PARAMETERS Parameters){ /* Apply defaults for non-set parameters */ if (!Parameters->SegmentCommit) Parameters->SegmentCommit = MmHeapSegmentCommit; if (!Parameters->SegmentReserve) Parameters->SegmentReserve = MmHeapSegmentReserve; if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold; if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;}
VOIDNTAPIRtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN PVOID ContextData, IN ULONG Size){ /* Check the global flag */ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { /* FIXME: Log this exception */ }}
BOOLEANNTAPIRtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, IN ULONG_PTR RegistrationFrameEnd, IN OUT PULONG_PTR StackLow, IN OUT PULONG_PTR StackHigh){ PKPRCB Prcb; ULONG_PTR DpcStack;
/* Check if we are at DISPATCH or higher */ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { /* Get the PRCB and DPC Stack */ Prcb = KeGetCurrentPrcb(); DpcStack = (ULONG_PTR)Prcb->DpcStack;
/* Check if we are in a DPC and the stack matches */ if ((Prcb->DpcRoutineActive) && (RegistrationFrameEnd <= DpcStack) && ((ULONG_PTR)RegistrationFrame >= DpcStack - KERNEL_STACK_SIZE)) { /* Update the limits to the DPC Stack's */ *StackHigh = DpcStack; *StackLow = DpcStack - KERNEL_STACK_SIZE; return TRUE; } }
/* Not in DPC stack */ return FALSE;}
#if !defined(_ARM_) && !defined(_AMD64_)
BOOLEANNTAPIRtlpCaptureStackLimits(IN ULONG_PTR Ebp, IN ULONG_PTR *StackBegin, IN ULONG_PTR *StackEnd){ PKTHREAD Thread = KeGetCurrentThread();
/* Don't even try at ISR level or later */ if (KeGetCurrentIrql() > DISPATCH_LEVEL) return FALSE;
/* Start with defaults */ *StackBegin = Thread->StackLimit; *StackEnd = (ULONG_PTR)Thread->StackBase;
/* Check if EBP is inside the stack */ if ((*StackBegin <= Ebp) && (Ebp <= *StackEnd)) { /* Then make the stack start at EBP */ *StackBegin = Ebp; } else { /* Now we're going to assume we're on the DPC stack */ *StackEnd = (ULONG_PTR)(KeGetPcr()->Prcb->DpcStack); *StackBegin = *StackEnd - KERNEL_STACK_SIZE;
/* Check if we seem to be on the DPC stack */ if ((*StackEnd) && (*StackBegin < Ebp) && (Ebp <= *StackEnd)) { /* We're on the DPC stack */ *StackBegin = Ebp; } else { /* We're somewhere else entirely... use EBP for safety */ *StackBegin = Ebp; *StackEnd = (ULONG_PTR)PAGE_ALIGN(*StackBegin); } }
/* Return success */ return TRUE;}
/* * @implemented */ULONGNTAPIRtlWalkFrameChain(OUT PVOID *Callers, IN ULONG Count, IN ULONG Flags){ ULONG_PTR Stack, NewStack, StackBegin, StackEnd = 0; ULONG Eip; BOOLEAN Result, StopSearch = FALSE; ULONG i = 0; PETHREAD Thread = PsGetCurrentThread(); PTEB Teb; PKTRAP_FRAME TrapFrame;
/* Get current EBP */#if defined(_M_IX86)#if defined __GNUC__ __asm__("mov %%ebp, %0" : "=r" (Stack) : );#elif defined(_MSC_VER) __asm mov Stack, ebp#endif#elif defined(_M_MIPS) __asm__("move $sp, %0" : "=r" (Stack) : );#elif defined(_M_PPC) __asm__("mr %0,1" : "=r" (Stack) : );#elif defined(_M_ARM) __asm__("mov sp, %0" : "=r"(Stack) : );#else#error Unknown architecture#endif
/* Set it as the stack begin limit as well */ StackBegin = (ULONG_PTR)Stack;
/* Check if we're called for non-logging mode */ if (!Flags) { /* Get the actual safe limits */ Result = RtlpCaptureStackLimits((ULONG_PTR)Stack, &StackBegin, &StackEnd); if (!Result) return 0; }
/* Use a SEH block for maximum protection */ _SEH2_TRY { /* Check if we want the user-mode stack frame */ if (Flags == 1) { /* Get the trap frame and TEB */ TrapFrame = KeGetTrapFrame(&Thread->Tcb); Teb = Thread->Tcb.Teb;
/* Make sure we can trust the TEB and trap frame */ if (!(Teb) || (KeIsAttachedProcess()) || (KeGetCurrentIrql() >= DISPATCH_LEVEL)) { /* Invalid or unsafe attempt to get the stack */ _SEH2_YIELD(return 0;) }
/* Get the stack limits */ StackBegin = (ULONG_PTR)Teb->NtTib.StackLimit; StackEnd = (ULONG_PTR)Teb->NtTib.StackBase;#ifdef _M_IX86 Stack = TrapFrame->Ebp;#elif defined(_M_PPC) Stack = TrapFrame->Gpr1;#else#error Unknown architecture#endif
/* Validate them */ if (StackEnd <= StackBegin) _SEH2_YIELD(return 0); ProbeForRead((PVOID)StackBegin, StackEnd - StackBegin, sizeof(CHAR)); }
/* Loop the frames */ for (i = 0; i < Count; i++) { /* * Leave if we're past the stack, * if we're before the stack, * or if we've reached ourselves. */ if ((Stack >= StackEnd) || (!i ? (Stack < StackBegin) : (Stack <= StackBegin)) || ((StackEnd - Stack) < (2 * sizeof(ULONG_PTR)))) { /* We're done or hit a bad address */ break; }
/* Get new stack and EIP */ NewStack = *(PULONG_PTR)Stack; Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
/* Check if the new pointer is above the oldone and past the end */ if (!((Stack < NewStack) && (NewStack < StackEnd))) { /* Stop searching after this entry */ StopSearch = TRUE; }
/* Also make sure that the EIP isn't a stack address */ if ((StackBegin < Eip) && (Eip < StackEnd)) break;
/* Check if we reached a user-mode address */ if (!(Flags) && !(Eip & 0x80000000)) break; // FIXME: 3GB breakage
/* Save this frame */ Callers[i] = (PVOID)Eip;
/* Check if we should continue */ if (StopSearch) { /* Return the next index */ i++; break; }
/* Move to the next stack */ Stack = NewStack; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* No index */ i = 0; } _SEH2_END;
/* Return frames parsed */ return i;}
#endif
VOIDNTAPIRtlpGetStackLimits( OUT PULONG_PTR LowLimit, OUT PULONG_PTR HighLimit){ PKTHREAD CurrentThread = KeGetCurrentThread(); *LowLimit = (ULONG_PTR)CurrentThread->StackLimit;#ifdef _M_IX86 *HighLimit = (ULONG_PTR)CurrentThread->InitialStack - sizeof(FX_SAVE_AREA);#else *HighLimit = (ULONG_PTR)CurrentThread->InitialStack;#endif}
/* RTL Atom Tables ************************************************************/
NTSTATUSRtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable){ ExInitializeFastMutex(&AtomTable->FastMutex);
return STATUS_SUCCESS;}
VOIDRtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable){}
BOOLEANRtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable){ ExAcquireFastMutex(&AtomTable->FastMutex); return TRUE;}
VOIDRtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable){ ExReleaseFastMutex(&AtomTable->FastMutex);}
BOOLEANRtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable){ AtomTable->ExHandleTable = ExCreateHandleTable(NULL); return (AtomTable->ExHandleTable != NULL);}
BOOLEANNTAPIRtlpCloseHandleCallback( IN PHANDLE_TABLE_ENTRY HandleTableEntry, IN HANDLE Handle, IN PVOID HandleTable){ /* Destroy and unlock the handle entry */ return ExDestroyHandle(HandleTable, Handle, HandleTableEntry);}
VOIDRtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable){ if (AtomTable->ExHandleTable) { ExSweepHandleTable(AtomTable->ExHandleTable, RtlpCloseHandleCallback, AtomTable->ExHandleTable); ExDestroyHandleTable(AtomTable->ExHandleTable, NULL); AtomTable->ExHandleTable = NULL; }}
PRTL_ATOM_TABLERtlpAllocAtomTable(ULONG Size){ PRTL_ATOM_TABLE Table = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_ATMT); if (Table != NULL) { RtlZeroMemory(Table, Size); }
return Table;}
VOIDRtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable){ ExFreePoolWithTag(AtomTable, TAG_ATMT);}
PRTL_ATOM_TABLE_ENTRYRtlpAllocAtomTableEntry(ULONG Size){ PRTL_ATOM_TABLE_ENTRY Entry;
Entry = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_ATMT); if (Entry != NULL) { RtlZeroMemory(Entry, Size); }
return Entry;}
VOIDRtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry){ ExFreePoolWithTag(Entry, TAG_ATMT);}
VOIDRtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry){ ExDestroyHandle(AtomTable->ExHandleTable, (HANDLE)((ULONG_PTR)Entry->HandleIndex << 2), NULL);}
BOOLEANRtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry){ HANDLE_TABLE_ENTRY ExEntry; HANDLE Handle; USHORT HandleIndex;
/* Initialize ex handle table entry */ ExEntry.Object = Entry; ExEntry.GrantedAccess = 0x1; /* FIXME - valid handle */
/* Create ex handle */ Handle = ExCreateHandle(AtomTable->ExHandleTable, &ExEntry); if (!Handle) return FALSE;
/* Calculate HandleIndex (by getting rid of the first two bits) */ HandleIndex = (USHORT)((ULONG_PTR)Handle >> 2);
/* Index must be less than 0xC000 */ if (HandleIndex >= 0xC000) { /* Destroy ex handle */ ExDestroyHandle(AtomTable->ExHandleTable, Handle, NULL);
/* Return failure */ return FALSE; }
/* Initialize atom table entry */ Entry->HandleIndex = HandleIndex; Entry->Atom = 0xC000 + HandleIndex;
/* Return success */ return TRUE;}
PRTL_ATOM_TABLE_ENTRYRtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index){ PHANDLE_TABLE_ENTRY ExEntry; PRTL_ATOM_TABLE_ENTRY Entry = NULL;
/* NOTE: There's no need to explicitly enter a critical region because it's guaranteed that we're in a critical region right now (as we hold the atom table lock) */
ExEntry = ExMapHandleToPointer(AtomTable->ExHandleTable, (HANDLE)((ULONG_PTR)Index << 2)); if (ExEntry != NULL) { Entry = ExEntry->Object;
ExUnlockHandleTableEntry(AtomTable->ExHandleTable, ExEntry); }
return Entry;}
/* Ldr SEH-Protected access to IMAGE_NT_HEADERS */
/* Rtl SEH-Free version of this */NTSTATUSNTAPIRtlpImageNtHeaderEx( _In_ ULONG Flags, _In_ PVOID Base, _In_ ULONG64 Size, _Out_ PIMAGE_NT_HEADERS *OutHeaders);
/* * @implemented * @note: This is here, so that we do not drag SEH into rosload, freeldr and bootmgfw */NTSTATUSNTAPIRtlImageNtHeaderEx( _In_ ULONG Flags, _In_ PVOID Base, _In_ ULONG64 Size, _Out_ PIMAGE_NT_HEADERS *OutHeaders){ NTSTATUS Status;
/* Assume failure. This is also done in RtlpImageNtHeaderEx, but this is guarded by SEH. */ if (OutHeaders != NULL) *OutHeaders = NULL;
_SEH2_TRY { Status = RtlpImageNtHeaderEx(Flags, Base, Size, OutHeaders); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Fail with the SEH error */ Status = _SEH2_GetExceptionCode(); } _SEH2_END;
return Status;}
/* * Ldr Resource support code */
IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir, LPCWSTR name, void *root, int want_dir );IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir, USHORT id, void *root, int want_dir );IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir, void *root, int want_dir );
/********************************************************************** * find_entry * * Find a resource entry */NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info, ULONG level, void **ret, int want_dir ){ ULONG size; void *root; IMAGE_RESOURCE_DIRECTORY *resdirptr;
root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size ); if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND; if (size < sizeof(*resdirptr)) return STATUS_RESOURCE_DATA_NOT_FOUND; resdirptr = root;
if (!level--) goto done; if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level ))) return STATUS_RESOURCE_TYPE_NOT_FOUND; if (!level--) return STATUS_SUCCESS;
resdirptr = *ret; if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level ))) return STATUS_RESOURCE_NAME_NOT_FOUND; if (!level--) return STATUS_SUCCESS; if (level) return STATUS_INVALID_PARAMETER; /* level > 3 */
resdirptr = *ret;
if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
return STATUS_RESOURCE_DATA_NOT_FOUND;
done: *ret = resdirptr; return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlpSafeCopyMemory( _Out_writes_bytes_all_(Length) VOID UNALIGNED *Destination, _In_reads_bytes_(Length) CONST VOID UNALIGNED *Source, _In_ SIZE_T Length){ _SEH2_TRY { RtlCopyMemory(Destination, Source, Length); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END;
return STATUS_SUCCESS;}
BOOLEANNTAPIRtlCallVectoredExceptionHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord, _In_ PCONTEXT Context){ /* In the kernel we don't have vectored exception handlers */ return FALSE;}
VOIDNTAPIRtlCallVectoredContinueHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord, _In_ PCONTEXT Context){ /* No vectored continue handlers either in kernel mode */ return;}
#ifdef _M_AMD64
PRUNTIME_FUNCTIONNTAPIRtlpLookupDynamicFunctionEntry( _In_ DWORD64 ControlPc, _Out_ PDWORD64 ImageBase, _In_ PUNWIND_HISTORY_TABLE HistoryTable){ /* No support for dynamic function tables in the kernel */ return NULL;}
#endif
/* EOF */