https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8521f6d7b5849d3b07b60…
commit 8521f6d7b5849d3b07b6032f616d9fb48b8bc7ef
Author: Timo Kreuzer <timo.kreuzer(a)reactos.org>
AuthorDate: Fri Jun 10 18:44:51 2022 +0200
Commit: Timo Kreuzer <timo.kreuzer(a)reactos.org>
CommitDate: Sat Jun 25 21:45:47 2022 +0200
[RTL] Implement dynamic function tables for x64
---
dll/ntdll/CMakeLists.txt | 2 +-
dll/ntdll/def/ntdll.spec | 2 +-
ntoskrnl/rtl/libsupp.c | 17 +++
sdk/lib/rtl/CMakeLists.txt | 1 +
sdk/lib/rtl/amd64/dynfntbl.c | 326 +++++++++++++++++++++++++++++++++++++++++++
sdk/lib/rtl/amd64/unwind.c | 45 ++----
6 files changed, 355 insertions(+), 38 deletions(-)
diff --git a/dll/ntdll/CMakeLists.txt b/dll/ntdll/CMakeLists.txt
index be810e324f8..fbcd5272eb1 100644
--- a/dll/ntdll/CMakeLists.txt
+++ b/dll/ntdll/CMakeLists.txt
@@ -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>)
diff --git a/dll/ntdll/def/ntdll.spec b/dll/ntdll/def/ntdll.spec
index 57ba0653d7c..80d27637413 100644
--- a/dll/ntdll/def/ntdll.spec
+++ b/dll/ntdll/def/ntdll.spec
@@ -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()
diff --git a/ntoskrnl/rtl/libsupp.c b/ntoskrnl/rtl/libsupp.c
index e8d7b68df88..173944a0573 100644
--- a/ntoskrnl/rtl/libsupp.c
+++ b/ntoskrnl/rtl/libsupp.c
@@ -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 */
diff --git a/sdk/lib/rtl/CMakeLists.txt b/sdk/lib/rtl/CMakeLists.txt
index a8d2fdbe348..8550fe9fb8e 100644
--- a/sdk/lib/rtl/CMakeLists.txt
+++ b/sdk/lib/rtl/CMakeLists.txt
@@ -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
diff --git a/sdk/lib/rtl/amd64/dynfntbl.c b/sdk/lib/rtl/amd64/dynfntbl.c
new file mode 100644
index 00000000000..67c76e7f640
--- /dev/null
+++ b/sdk/lib/rtl/amd64/dynfntbl.c
@@ -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(a)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/rtlgetfunctiontable…
+ */
+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;
+}
diff --git a/sdk/lib/rtl/amd64/unwind.c b/sdk/lib/rtl/amd64/unwind.c
index 4fe42801281..49e3740bec4 100644
--- a/sdk/lib/rtl/amd64/unwind.c
+++ b/sdk/lib/rtl/amd64/unwind.c
@@ -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