Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46479 Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/signal_arm.c | 10 ++++- dlls/ntdll/signal_arm64.c | 9 +++- dlls/ntdll/signal_x86_64.c | 70 +++++++++++++++++++++++++++++-- dlls/ntdll/tests/exception.c | 80 +++++++++++++++++++++++++++++++++++- 5 files changed, 163 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c2e2fb1c66..09f90980a4 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -542,6 +542,7 @@ @ stdcall RtlDeleteAce(ptr long) @ stdcall RtlDeleteAtomFromAtomTable(ptr long) @ stdcall RtlDeleteCriticalSection(ptr) +@ cdecl -arch=arm,arm64,x86_64 RtlDeleteGrowableFunctionTable(ptr) @ stub RtlDeleteElementGenericTable @ stub RtlDeleteElementGenericTableAvl @ cdecl -arch=arm,arm64,x86_64 RtlDeleteFunctionTable(ptr) diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index 88914cd096..e22480878d 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -1066,7 +1066,7 @@ DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functi { FIXME( "(%p, %p, %d, %d, %ld, %ld) stub!\n", table, functions, count, max_count, base, end ); if (table) *table = NULL; - return S_OK; + return STATUS_SUCCESS; }
/************************************************************************* @@ -1077,6 +1077,14 @@ void WINAPI RtlGrowFunctionTable( void *table, DWORD count ) FIXME( "(%p, %d) stub!\n", table, count ); }
+/************************************************************************* + * RtlDeleteGrowableFunctionTable (NTDLL.@) + */ +void WINAPI RtlDeleteGrowableFunctionTable( void *table ) +{ + FIXME( "(%p) stub!\n", table ); +} + /********************************************************************** * RtlDeleteFunctionTable (NTDLL.@) */ diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index e1958890af..c063be5c85 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -986,7 +986,7 @@ DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functi { FIXME( "(%p, %p, %d, %d, %ld, %ld) stub!\n", table, functions, count, max_count, base, end ); if (table) *table = NULL; - return S_OK; + return STATUS_SUCCESS; }
@@ -998,6 +998,13 @@ void WINAPI RtlGrowFunctionTable( void *table, DWORD count ) FIXME( "(%p, %d) stub!\n", table, count ); }
+/************************************************************************* + * RtlDeleteGrowableFunctionTable (NTDLL.@) + */ +void WINAPI RtlDeleteGrowableFunctionTable( void *table ) +{ + FIXME( "(%p) stub!\n", table ); +}
/********************************************************************** * RtlDeleteFunctionTable (NTDLL.@) diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index dd820007d3..cbb3dd7617 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -343,6 +343,7 @@ struct dynamic_unwind_entry /* lookup table */ RUNTIME_FUNCTION *table; DWORD count; + DWORD max_count;
/* user defined callback */ PGET_RUNTIME_FUNCTION_CALLBACK callback; @@ -3460,6 +3461,7 @@ BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, DWORD64 entry->end = addr + table[count - 1].EndAddress; entry->table = table; entry->count = count; + entry->max_count = 0; entry->callback = NULL; entry->context = NULL;
@@ -3495,6 +3497,7 @@ BOOLEAN CDECL RtlInstallFunctionTableCallback( DWORD64 table, DWORD64 base, DWOR entry->end = base + length; entry->table = (RUNTIME_FUNCTION *)table; entry->count = 0; + entry->max_count = 0; entry->callback = callback; entry->context = context;
@@ -3512,9 +3515,29 @@ BOOLEAN CDECL RtlInstallFunctionTableCallback( DWORD64 table, DWORD64 base, DWOR DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functions, DWORD count, DWORD max_count, ULONG_PTR base, ULONG_PTR end ) { - FIXME( "(%p, %p, %d, %d, %ld, %ld) semi-stub!\n", table, functions, count, max_count, base, end ); - if (table) *table = NULL; - return RtlAddFunctionTable(functions, count, base); + struct dynamic_unwind_entry *entry; + + TRACE( "%p, %p, %u, %u, %lx, %lx\n", table, functions, count, max_count, base, end ); + + entry = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*entry) ); + if (!entry) + return STATUS_NO_MEMORY; + + entry->base = base; + entry->end = end; + entry->table = functions; + entry->count = count; + entry->max_count = max_count; + entry->callback = NULL; + entry->context = NULL; + + RtlEnterCriticalSection( &dynamic_unwind_section ); + list_add_tail( &dynamic_unwind_list, &entry->entry ); + RtlLeaveCriticalSection( &dynamic_unwind_section ); + + *table = entry; + + return STATUS_SUCCESS; }
@@ -3523,7 +3546,46 @@ DWORD WINAPI RtlAddGrowableFunctionTable( void **table, RUNTIME_FUNCTION *functi */ void WINAPI RtlGrowFunctionTable( void *table, DWORD count ) { - FIXME( "(%p, %d) stub!\n", table, count ); + struct dynamic_unwind_entry *entry; + + TRACE( "%p, %u\n", table, count ); + + RtlEnterCriticalSection( &dynamic_unwind_section ); + LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry ) + { + if (entry == table) + { + if (count > entry->count && count <= entry->max_count) + entry->count = count; + break; + } + } + RtlLeaveCriticalSection( &dynamic_unwind_section ); +} + + +/************************************************************************* + * RtlDeleteGrowableFunctionTable (NTDLL.@) + */ +void WINAPI RtlDeleteGrowableFunctionTable( void *table ) +{ + struct dynamic_unwind_entry *entry, *to_free = NULL; + + TRACE( "%p\n", table ); + + RtlEnterCriticalSection( &dynamic_unwind_section ); + LIST_FOR_EACH_ENTRY( entry, &dynamic_unwind_list, struct dynamic_unwind_entry, entry ) + { + if (entry == table) + { + to_free = entry; + list_remove( &entry->entry ); + break; + } + } + RtlLeaveCriticalSection( &dynamic_unwind_section ); + + RtlFreeHeap( GetProcessHeap(), 0, to_free ); }
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 5998f54b04..dee8a9fe47 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -146,6 +146,9 @@ static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64 static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*); +static DWORD (CDECL *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR); +static void (CDECL *pRtlGrowFunctionTable)(void*, DWORD); +static void (CDECL *pRtlDeleteGrowableFunctionTable)(void*); static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*); static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*); @@ -2037,9 +2040,11 @@ static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID con static void test_dynamic_unwind(void) { static const int code_offset = 1024; - char buf[sizeof(RUNTIME_FUNCTION) + 4]; + char buf[2 * sizeof(RUNTIME_FUNCTION) + 4]; RUNTIME_FUNCTION *runtime_func, *func; ULONG_PTR table, base; + void *growable_table; + NTSTATUS status; DWORD count;
/* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */ @@ -2136,6 +2141,76 @@ static void test_dynamic_unwind(void) ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
+ if (!pRtlAddGrowableFunctionTable) + { + win_skip("Growable function tables are not supported.\n"); + return; + } + + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->EndAddress = code_offset + 16; + runtime_func->UnwindData = 0; + runtime_func++; + runtime_func->BeginAddress = code_offset + 16; + runtime_func->EndAddress = code_offset + 32; + runtime_func->UnwindData = 0; + runtime_func = (RUNTIME_FUNCTION *)buf; + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem, + (ULONG_PTR)code_mem + code_offset + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + + /* Current count is 0. */ + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 1 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == runtime_func, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base ); + + /* Second function is inaccessible yet. */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 2 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == runtime_func + 1, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base ); + + pRtlDeleteGrowableFunctionTable( growable_table ); }
static int termination_handler_called; @@ -3155,6 +3230,9 @@ START_TEST(exception) "RtlInstallFunctionTableCallback" ); pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll, "RtlLookupFunctionEntry" ); + pRtlAddGrowableFunctionTable = (void *)GetProcAddress( hntdll, "RtlAddGrowableFunctionTable" ); + pRtlGrowFunctionTable = (void *)GetProcAddress( hntdll, "RtlGrowFunctionTable" ); + pRtlDeleteGrowableFunctionTable = (void *)GetProcAddress( hntdll, "RtlDeleteGrowableFunctionTable" ); p__C_specific_handler = (void *)GetProcAddress( hntdll, "__C_specific_handler" ); pRtlCaptureContext = (void *)GetProcAddress( hntdll,