mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +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)
|
||||
################# 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)
|
||||
target_sources(ntdll PRIVATE $<TARGET_OBJECTS:gcc_ssp_nt>)
|
||||
|
|
|
@ -881,7 +881,7 @@
|
|||
@ stdcall RtlGetFrame()
|
||||
@ stdcall RtlGetFullPathName_U(wstr long 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)
|
||||
@ stub -version=0x600+ RtlGetIntegerAtom
|
||||
@ stdcall RtlGetLastNtStatus()
|
||||
|
|
|
@ -823,4 +823,21 @@ RtlCallVectoredContinueHandlers(_In_ PEXCEPTION_RECORD ExceptionRecord,
|
|||
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 */
|
||||
|
|
|
@ -94,6 +94,7 @@ elseif(ARCH STREQUAL "amd64")
|
|||
list(APPEND SOURCE
|
||||
bitmap64.c
|
||||
byteswap.c
|
||||
amd64/dynfntbl.c
|
||||
amd64/except.c
|
||||
amd64/unwind.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;
|
||||
}
|
||||
|
||||
PRUNTIME_FUNCTION
|
||||
NTAPI
|
||||
RtlpLookupDynamicFunctionEntry(
|
||||
_In_ DWORD64 ControlPc,
|
||||
_Out_ PDWORD64 ImageBase,
|
||||
_In_ PUNWIND_HISTORY_TABLE HistoryTable);
|
||||
|
||||
/*! RtlLookupFunctionEntry
|
||||
* \brief Locates the RUNTIME_FUNCTION entry corresponding to a code address.
|
||||
* \ref http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx
|
||||
|
@ -126,10 +133,10 @@ RtlLookupFunctionEntry(
|
|||
/* Find the corresponding table */
|
||||
FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength);
|
||||
|
||||
/* Fail, if no table is found */
|
||||
/* If no table is found, try dynamic function tables */
|
||||
if (!FunctionTable)
|
||||
{
|
||||
return NULL;
|
||||
return RtlpLookupDynamicFunctionEntry(ControlPc, ImageBase, HistoryTable);
|
||||
}
|
||||
|
||||
/* Use relative virtual address */
|
||||
|
@ -164,40 +171,6 @@ RtlLookupFunctionEntry(
|
|||
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
|
||||
__inline
|
||||
ULONG
|
||||
|
|
Loading…
Reference in a new issue