mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 06:55:55 +00:00
[RTL] Implement dynamic function tables for x64
This commit is contained in:
parent
ef1311b7a4
commit
8521f6d7b5
6 changed files with 355 additions and 38 deletions
|
@ -60,7 +60,7 @@ set_module_type(ntdll win32dll ENTRYPOINT 0)
|
||||||
set_subsystem(ntdll console)
|
set_subsystem(ntdll console)
|
||||||
################# END HACK #################
|
################# END HACK #################
|
||||||
|
|
||||||
target_link_libraries(ntdll rtl ntdllsys libcntpr uuid ${PSEH_LIB})
|
target_link_libraries(ntdll rtl rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB})
|
||||||
|
|
||||||
if (STACK_PROTECTOR)
|
if (STACK_PROTECTOR)
|
||||||
target_sources(ntdll PRIVATE $<TARGET_OBJECTS:gcc_ssp_nt>)
|
target_sources(ntdll PRIVATE $<TARGET_OBJECTS:gcc_ssp_nt>)
|
||||||
|
|
|
@ -881,7 +881,7 @@
|
||||||
@ stdcall RtlGetFrame()
|
@ stdcall RtlGetFrame()
|
||||||
@ stdcall RtlGetFullPathName_U(wstr long ptr ptr)
|
@ stdcall RtlGetFullPathName_U(wstr long ptr ptr)
|
||||||
@ stdcall RtlGetFullPathName_UstrEx(ptr ptr ptr ptr ptr ptr ptr ptr)
|
@ stdcall RtlGetFullPathName_UstrEx(ptr ptr ptr ptr ptr ptr ptr ptr)
|
||||||
@ stub -version=0x600+ -arch=x86_64 RtlGetFunctionTableListHead
|
@ stdcall -arch=x86_64 RtlGetFunctionTableListHead()
|
||||||
@ stdcall RtlGetGroupSecurityDescriptor(ptr ptr ptr)
|
@ stdcall RtlGetGroupSecurityDescriptor(ptr ptr ptr)
|
||||||
@ stub -version=0x600+ RtlGetIntegerAtom
|
@ stub -version=0x600+ RtlGetIntegerAtom
|
||||||
@ stdcall RtlGetLastNtStatus()
|
@ stdcall RtlGetLastNtStatus()
|
||||||
|
|
|
@ -823,4 +823,21 @@ RtlCallVectoredContinueHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _M_AMD64
|
||||||
|
|
||||||
|
typedef PVOID PRUNTIME_FUNCTION, PUNWIND_HISTORY_TABLE;
|
||||||
|
|
||||||
|
PRUNTIME_FUNCTION
|
||||||
|
NTAPI
|
||||||
|
RtlpLookupDynamicFunctionEntry(
|
||||||
|
_In_ DWORD64 ControlPc,
|
||||||
|
_Out_ PDWORD64 ImageBase,
|
||||||
|
_In_ PUNWIND_HISTORY_TABLE HistoryTable)
|
||||||
|
{
|
||||||
|
/* No support for dynamic function tables in the kernel */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -94,6 +94,7 @@ elseif(ARCH STREQUAL "amd64")
|
||||||
list(APPEND SOURCE
|
list(APPEND SOURCE
|
||||||
bitmap64.c
|
bitmap64.c
|
||||||
byteswap.c
|
byteswap.c
|
||||||
|
amd64/dynfntbl.c
|
||||||
amd64/except.c
|
amd64/except.c
|
||||||
amd64/unwind.c
|
amd64/unwind.c
|
||||||
amd64/stubs.c
|
amd64/stubs.c
|
||||||
|
|
326
sdk/lib/rtl/amd64/dynfntbl.c
Normal file
326
sdk/lib/rtl/amd64/dynfntbl.c
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS RTL
|
||||||
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||||
|
* PURPOSE: Dynamic function table support routines
|
||||||
|
* COPYRIGHT: Copyright 2022 Timo Kreuzer (timo.kreuzer@reactos.org)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtl.h>
|
||||||
|
|
||||||
|
#define NDEBUG
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
#define TAG_RTLDYNFNTBL 'tfDP'
|
||||||
|
|
||||||
|
typedef
|
||||||
|
_Function_class_(GET_RUNTIME_FUNCTION_CALLBACK)
|
||||||
|
PRUNTIME_FUNCTION
|
||||||
|
GET_RUNTIME_FUNCTION_CALLBACK(
|
||||||
|
_In_ DWORD64 ControlPc,
|
||||||
|
_In_opt_ PVOID Context);
|
||||||
|
typedef GET_RUNTIME_FUNCTION_CALLBACK *PGET_RUNTIME_FUNCTION_CALLBACK;
|
||||||
|
|
||||||
|
typedef
|
||||||
|
_Function_class_(OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)
|
||||||
|
DWORD
|
||||||
|
OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK(
|
||||||
|
_In_ HANDLE Process,
|
||||||
|
_In_ PVOID TableAddress,
|
||||||
|
_Out_ PDWORD Entries,
|
||||||
|
_Out_ PRUNTIME_FUNCTION* Functions);
|
||||||
|
typedef OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK *POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK;
|
||||||
|
|
||||||
|
typedef enum _FUNCTION_TABLE_TYPE
|
||||||
|
{
|
||||||
|
RF_SORTED = 0x0,
|
||||||
|
RF_UNSORTED = 0x1,
|
||||||
|
RF_CALLBACK = 0x2,
|
||||||
|
RF_KERNEL_DYNAMIC = 0x3,
|
||||||
|
} FUNCTION_TABLE_TYPE;
|
||||||
|
|
||||||
|
typedef struct _DYNAMIC_FUNCTION_TABLE
|
||||||
|
{
|
||||||
|
LIST_ENTRY ListEntry;
|
||||||
|
PRUNTIME_FUNCTION FunctionTable;
|
||||||
|
LARGE_INTEGER TimeStamp;
|
||||||
|
ULONG64 MinimumAddress;
|
||||||
|
ULONG64 MaximumAddress;
|
||||||
|
ULONG64 BaseAddress;
|
||||||
|
PGET_RUNTIME_FUNCTION_CALLBACK Callback;
|
||||||
|
PVOID Context;
|
||||||
|
PWCHAR OutOfProcessCallbackDll;
|
||||||
|
FUNCTION_TABLE_TYPE Type;
|
||||||
|
ULONG EntryCount;
|
||||||
|
#if (NTDDI_VERSION <= NTDDI_WIN10)
|
||||||
|
// FIXME: RTL_BALANCED_NODE is defined in ntdef.h, it's impossible to get included here due to precompiled header
|
||||||
|
//RTL_BALANCED_NODE TreeNode;
|
||||||
|
#else
|
||||||
|
//RTL_BALANCED_NODE TreeNodeMin;
|
||||||
|
//RTL_BALANCED_NODE TreeNodeMax;
|
||||||
|
#endif
|
||||||
|
} DYNAMIC_FUNCTION_TABLE, *PDYNAMIC_FUNCTION_TABLE;
|
||||||
|
|
||||||
|
RTL_SRWLOCK RtlpDynamicFunctionTableLock = { 0 };
|
||||||
|
LIST_ENTRY RtlpDynamicFunctionTableList = { &RtlpDynamicFunctionTableList, &RtlpDynamicFunctionTableList };
|
||||||
|
|
||||||
|
static __inline
|
||||||
|
VOID
|
||||||
|
AcquireDynamicFunctionTableLockExclusive()
|
||||||
|
{
|
||||||
|
RtlAcquireSRWLockExclusive(&RtlpDynamicFunctionTableLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline
|
||||||
|
VOID
|
||||||
|
ReleaseDynamicFunctionTableLockExclusive()
|
||||||
|
{
|
||||||
|
RtlReleaseSRWLockExclusive(&RtlpDynamicFunctionTableLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline
|
||||||
|
VOID
|
||||||
|
AcquireDynamicFunctionTableLockShared()
|
||||||
|
{
|
||||||
|
RtlAcquireSRWLockShared(&RtlpDynamicFunctionTableLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline
|
||||||
|
VOID
|
||||||
|
ReleaseDynamicFunctionTableLockShared()
|
||||||
|
{
|
||||||
|
RtlReleaseSRWLockShared(&RtlpDynamicFunctionTableLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlgetfunctiontablelisthead
|
||||||
|
*/
|
||||||
|
PLIST_ENTRY
|
||||||
|
NTAPI
|
||||||
|
RtlGetFunctionTableListHead(void)
|
||||||
|
{
|
||||||
|
return &RtlpDynamicFunctionTableList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
VOID
|
||||||
|
RtlpInsertDynamicFunctionTable(PDYNAMIC_FUNCTION_TABLE DynamicTable)
|
||||||
|
{
|
||||||
|
//LARGE_INTEGER TimeStamp;
|
||||||
|
|
||||||
|
AcquireDynamicFunctionTableLockExclusive();
|
||||||
|
|
||||||
|
/* Insert it into the list */
|
||||||
|
InsertTailList(&RtlpDynamicFunctionTableList, &DynamicTable->ListEntry);
|
||||||
|
|
||||||
|
// TODO: insert into RB-trees
|
||||||
|
|
||||||
|
ReleaseDynamicFunctionTableLockExclusive();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
NTAPI
|
||||||
|
RtlAddFunctionTable(
|
||||||
|
_In_ PRUNTIME_FUNCTION FunctionTable,
|
||||||
|
_In_ DWORD EntryCount,
|
||||||
|
_In_ DWORD64 BaseAddress)
|
||||||
|
{
|
||||||
|
PDYNAMIC_FUNCTION_TABLE dynamicTable;
|
||||||
|
ULONG i;
|
||||||
|
|
||||||
|
/* Allocate a dynamic function table */
|
||||||
|
dynamicTable = RtlpAllocateMemory(sizeof(*dynamicTable), TAG_RTLDYNFNTBL);
|
||||||
|
if (dynamicTable == NULL)
|
||||||
|
{
|
||||||
|
DPRINT1("Failed to allocate dynamic function table\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize fields */
|
||||||
|
dynamicTable->FunctionTable = FunctionTable;
|
||||||
|
dynamicTable->EntryCount = EntryCount;
|
||||||
|
dynamicTable->BaseAddress = BaseAddress;
|
||||||
|
dynamicTable->Callback = NULL;
|
||||||
|
dynamicTable->Context = NULL;
|
||||||
|
dynamicTable->Type = RF_UNSORTED;
|
||||||
|
|
||||||
|
/* Loop all entries to find the margins */
|
||||||
|
dynamicTable->MinimumAddress = ULONG64_MAX;
|
||||||
|
dynamicTable->MaximumAddress = 0;
|
||||||
|
for (i = 0; i < EntryCount; i++)
|
||||||
|
{
|
||||||
|
dynamicTable->MinimumAddress = min(dynamicTable->MinimumAddress,
|
||||||
|
FunctionTable[i].BeginAddress);
|
||||||
|
dynamicTable->MaximumAddress = max(dynamicTable->MaximumAddress,
|
||||||
|
FunctionTable[i].EndAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert the table into the list */
|
||||||
|
RtlpInsertDynamicFunctionTable(dynamicTable);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
NTAPI
|
||||||
|
RtlInstallFunctionTableCallback(
|
||||||
|
_In_ DWORD64 TableIdentifier,
|
||||||
|
_In_ DWORD64 BaseAddress,
|
||||||
|
_In_ DWORD Length,
|
||||||
|
_In_ PGET_RUNTIME_FUNCTION_CALLBACK Callback,
|
||||||
|
_In_ PVOID Context,
|
||||||
|
_In_opt_z_ PCWSTR OutOfProcessCallbackDll)
|
||||||
|
{
|
||||||
|
PDYNAMIC_FUNCTION_TABLE dynamicTable;
|
||||||
|
SIZE_T stringLength, allocationSize;
|
||||||
|
|
||||||
|
/* Make sure the identifier is valid */
|
||||||
|
if ((TableIdentifier & 3) != 3)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we have a DLL name */
|
||||||
|
if (OutOfProcessCallbackDll != NULL)
|
||||||
|
{
|
||||||
|
stringLength = wcslen(OutOfProcessCallbackDll) + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate required size */
|
||||||
|
allocationSize = sizeof(DYNAMIC_FUNCTION_TABLE) + stringLength * sizeof(WCHAR);
|
||||||
|
|
||||||
|
/* Allocate a dynamic function table */
|
||||||
|
dynamicTable = RtlpAllocateMemory(allocationSize, TAG_RTLDYNFNTBL);
|
||||||
|
if (dynamicTable == NULL)
|
||||||
|
{
|
||||||
|
DPRINT1("Failed to allocate dynamic function table\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize fields */
|
||||||
|
dynamicTable->FunctionTable = (PRUNTIME_FUNCTION)TableIdentifier;
|
||||||
|
dynamicTable->EntryCount = 0;
|
||||||
|
dynamicTable->BaseAddress = BaseAddress;
|
||||||
|
dynamicTable->Callback = Callback;
|
||||||
|
dynamicTable->Context = Context;
|
||||||
|
dynamicTable->Type = RF_CALLBACK;
|
||||||
|
dynamicTable->MinimumAddress = BaseAddress;
|
||||||
|
dynamicTable->MaximumAddress = BaseAddress + Length;
|
||||||
|
|
||||||
|
/* If we have a DLL name, copy that, too */
|
||||||
|
if (OutOfProcessCallbackDll != NULL)
|
||||||
|
{
|
||||||
|
dynamicTable->OutOfProcessCallbackDll = (PWCHAR)(dynamicTable + 1);
|
||||||
|
RtlCopyMemory(dynamicTable->OutOfProcessCallbackDll,
|
||||||
|
OutOfProcessCallbackDll,
|
||||||
|
stringLength * sizeof(WCHAR));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dynamicTable->OutOfProcessCallbackDll = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert the table into the list */
|
||||||
|
RtlpInsertDynamicFunctionTable(dynamicTable);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
NTAPI
|
||||||
|
RtlDeleteFunctionTable(
|
||||||
|
_In_ PRUNTIME_FUNCTION FunctionTable)
|
||||||
|
{
|
||||||
|
PLIST_ENTRY listLink;
|
||||||
|
PDYNAMIC_FUNCTION_TABLE dynamicTable;
|
||||||
|
BOOL removed = FALSE;
|
||||||
|
|
||||||
|
AcquireDynamicFunctionTableLockExclusive();
|
||||||
|
|
||||||
|
/* Loop all tables to find the one to delete */
|
||||||
|
for (listLink = RtlpDynamicFunctionTableList.Flink;
|
||||||
|
listLink != &RtlpDynamicFunctionTableList;
|
||||||
|
listLink = listLink->Flink)
|
||||||
|
{
|
||||||
|
dynamicTable = CONTAINING_RECORD(listLink, DYNAMIC_FUNCTION_TABLE, ListEntry);
|
||||||
|
|
||||||
|
if (dynamicTable->FunctionTable == FunctionTable)
|
||||||
|
{
|
||||||
|
RemoveEntryList(&dynamicTable->ListEntry);
|
||||||
|
removed = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseDynamicFunctionTableLockExclusive();
|
||||||
|
|
||||||
|
/* If we were successful, free the memory */
|
||||||
|
if (removed)
|
||||||
|
{
|
||||||
|
RtlpFreeMemory(dynamicTable, TAG_RTLDYNFNTBL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUNTIME_FUNCTION
|
||||||
|
NTAPI
|
||||||
|
RtlpLookupDynamicFunctionEntry(
|
||||||
|
_In_ DWORD64 ControlPc,
|
||||||
|
_Out_ PDWORD64 ImageBase,
|
||||||
|
_In_ PUNWIND_HISTORY_TABLE HistoryTable)
|
||||||
|
{
|
||||||
|
PLIST_ENTRY listLink;
|
||||||
|
PDYNAMIC_FUNCTION_TABLE dynamicTable;
|
||||||
|
PRUNTIME_FUNCTION functionTable, foundEntry = NULL;
|
||||||
|
PGET_RUNTIME_FUNCTION_CALLBACK callback;
|
||||||
|
ULONG i;
|
||||||
|
|
||||||
|
AcquireDynamicFunctionTableLockShared();
|
||||||
|
|
||||||
|
/* Loop all tables to find the one matching ControlPc */
|
||||||
|
for (listLink = RtlpDynamicFunctionTableList.Flink;
|
||||||
|
listLink != &RtlpDynamicFunctionTableList;
|
||||||
|
listLink = listLink->Flink)
|
||||||
|
{
|
||||||
|
dynamicTable = CONTAINING_RECORD(listLink, DYNAMIC_FUNCTION_TABLE, ListEntry);
|
||||||
|
|
||||||
|
if ((ControlPc >= dynamicTable->MinimumAddress) &&
|
||||||
|
(ControlPc < dynamicTable->MaximumAddress))
|
||||||
|
{
|
||||||
|
/* Check if there is a callback */
|
||||||
|
callback = dynamicTable->Callback;
|
||||||
|
if (callback != NULL)
|
||||||
|
{
|
||||||
|
PVOID context = dynamicTable->Context;
|
||||||
|
|
||||||
|
*ImageBase = dynamicTable->BaseAddress;
|
||||||
|
ReleaseDynamicFunctionTableLockShared();
|
||||||
|
return callback(ControlPc, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop all entries in the function table */
|
||||||
|
functionTable = dynamicTable->FunctionTable;
|
||||||
|
for (i = 0; i < dynamicTable->EntryCount; i++)
|
||||||
|
{
|
||||||
|
/* Check if this entry contains the address */
|
||||||
|
if ((ControlPc >= functionTable[i].BeginAddress) &&
|
||||||
|
(ControlPc < functionTable[i].EndAddress))
|
||||||
|
{
|
||||||
|
foundEntry = &functionTable[i];
|
||||||
|
*ImageBase = dynamicTable->BaseAddress;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
|
||||||
|
ReleaseDynamicFunctionTableLockShared();
|
||||||
|
|
||||||
|
return foundEntry;
|
||||||
|
}
|
|
@ -107,6 +107,13 @@ RtlLookupFunctionTable(
|
||||||
return Table;
|
return Table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUNTIME_FUNCTION
|
||||||
|
NTAPI
|
||||||
|
RtlpLookupDynamicFunctionEntry(
|
||||||
|
_In_ DWORD64 ControlPc,
|
||||||
|
_Out_ PDWORD64 ImageBase,
|
||||||
|
_In_ PUNWIND_HISTORY_TABLE HistoryTable);
|
||||||
|
|
||||||
/*! RtlLookupFunctionEntry
|
/*! RtlLookupFunctionEntry
|
||||||
* \brief Locates the RUNTIME_FUNCTION entry corresponding to a code address.
|
* \brief Locates the RUNTIME_FUNCTION entry corresponding to a code address.
|
||||||
* \ref http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx
|
* \ref http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx
|
||||||
|
@ -126,10 +133,10 @@ RtlLookupFunctionEntry(
|
||||||
/* Find the corresponding table */
|
/* Find the corresponding table */
|
||||||
FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength);
|
FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength);
|
||||||
|
|
||||||
/* Fail, if no table is found */
|
/* If no table is found, try dynamic function tables */
|
||||||
if (!FunctionTable)
|
if (!FunctionTable)
|
||||||
{
|
{
|
||||||
return NULL;
|
return RtlpLookupDynamicFunctionEntry(ControlPc, ImageBase, HistoryTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use relative virtual address */
|
/* Use relative virtual address */
|
||||||
|
@ -164,40 +171,6 @@ RtlLookupFunctionEntry(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOLEAN
|
|
||||||
NTAPI
|
|
||||||
RtlAddFunctionTable(
|
|
||||||
IN PRUNTIME_FUNCTION FunctionTable,
|
|
||||||
IN DWORD EntryCount,
|
|
||||||
IN DWORD64 BaseAddress)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOLEAN
|
|
||||||
NTAPI
|
|
||||||
RtlDeleteFunctionTable(
|
|
||||||
IN PRUNTIME_FUNCTION FunctionTable)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOLEAN
|
|
||||||
NTAPI
|
|
||||||
RtlInstallFunctionTableCallback(
|
|
||||||
IN DWORD64 TableIdentifier,
|
|
||||||
IN DWORD64 BaseAddress,
|
|
||||||
IN DWORD Length,
|
|
||||||
IN PGET_RUNTIME_FUNCTION_CALLBACK Callback,
|
|
||||||
IN PVOID Context,
|
|
||||||
IN PCWSTR OutOfProcessCallbackDll)
|
|
||||||
{
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
static
|
||||||
__inline
|
__inline
|
||||||
ULONG
|
ULONG
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue