-- v3: ntdll: Support old parameter layout for NtSetInformationProcess( ProcessInstrumentationCallback ). ntdll: Call instrumentation callback for KiUserModeCallback on x64. ntdll: Call instrumentation callback for LdrInitializeThunk on x64. ntdll: Call instrumentation callback for KiUserExceptionDispatcher on x64. ntdll: Call instrumentation callback from wine_syscall_dispatcher on x64. ntdll/tests: Add more tests for process instrumentation callback.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 283 ++++++++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c2ceef5eb7f..93b43a02608 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -2248,6 +2248,46 @@ static void test_KiUserCallbackDispatcher(void) VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect ); }
+static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0xff, 0x05, /* inc instrumentation_call_count */ + /* &instrumentation_call_count, offset 2 */ 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe1, /* jmp *ecx */ + }; + + unsigned int instrumentation_call_count; + NTSTATUS status; + + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(volatile void **)((char *)code_mem + 2) = &instrumentation_call_count; + + memset(&info, 0, sizeof(info)); + /* On 32 bit the structure is never used and just a callback pointer is expected. */ + info.Version = (ULONG_PTR)code_mem; + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_NOT_SUPPORTED + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + todo_wine ok( instrumentation_call_count, "got %u.\n", instrumentation_call_count ); + + memset(&info, 0, sizeof(info)); + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !instrumentation_call_count, "got %u.\n", instrumentation_call_count ); +} + #elif defined(__x86_64__)
static LONG consolidate_dummy_called; @@ -5264,6 +5304,7 @@ static void test_syscall_clobbered_regs(void) struct regs { UINT64 rcx; + UINT64 r10; UINT64 r11; UINT32 eflags; }; @@ -5281,16 +5322,19 @@ static void test_syscall_clobbered_regs(void) 0x41, 0x50, /* push %r8 */ 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, /* push %rbx, %rbp, %rdi, %rsi, %r12, %r13, %r14, %r15 */ + 0x49, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, + /* movabs $0xdeadbeef,%r10 */ 0x41, 0xff, 0xd1, /* callq *r9 */ 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5e, 0x5f, 0x5d, 0x5b, /* pop %r15, %r14, %r13, %r12, %rsi, %rdi, %rbp, %rbx */ 0x41, 0x58, /* pop %r8 */ 0x49, 0x89, 0x48, 0x00, /* mov %rcx,(%r8) */ - 0x4d, 0x89, 0x58, 0x08, /* mov %r11,0x8(%r8) */ + 0x4d, 0x89, 0x50, 0x08, /* mov %r10,0x8(%r8) */ + 0x4d, 0x89, 0x58, 0x10, /* mov %r11,0x10(%r8) */ 0x9c, /* pushfq */ 0x59, /* pop %rcx */ 0xfc, /* cld */ - 0x41, 0x89, 0x48, 0x10, /* mov %ecx,0x10(%r8) */ + 0x41, 0x89, 0x48, 0x18, /* mov %ecx,0x18(%r8) */ 0x5c, /* pop %rsp */ 0xc3, /* ret */ }; @@ -5311,6 +5355,7 @@ static void test_syscall_clobbered_regs(void) status = func((HANDLE)0xdeadbeef, NULL, ®s, pNtCancelTimer); ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
/* After the syscall instruction rcx contains the address of the instruction next after syscall. */ ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, @@ -5321,6 +5366,7 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
context.ContextFlags = CONTEXT_CONTROL; status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); @@ -5328,12 +5374,14 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok((regs.r11 | 0x2) == regs.eflags, "Expected r11 (%#I64x) | 0x2 to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
context.ContextFlags = CONTEXT_INTEGER; status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); @@ -5341,13 +5389,14 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); ok((BYTE *)regs.rcx > (BYTE *)pNtSetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtSetContextThread + 0x20, "Got unexpected rcx %s, pNtSetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtSetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); - + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); }
static CONTEXT test_raiseexception_regs_context; @@ -5449,6 +5498,232 @@ static void test_raiseexception_regs(void) RemoveVectoredExceptionHandler(vectored_handler); }
+static LONG CALLBACK test_instrumentation_callback_handler( EXCEPTION_POINTERS *exception_info ) +{ + EXCEPTION_RECORD *rec = exception_info->ExceptionRecord; + CONTEXT *c = exception_info->ContextRecord; + + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ++c->Rip; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static HANDLE instrumentation_callback_thread_ready, instrumentation_callback_thread_wait; + +static DWORD WINAPI test_instrumentation_callback_thread( void *arg ) +{ + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + return 0; +} + +struct instrumentation_callback_data +{ + unsigned int call_count; + struct + { + char *r10; + char *rcx; + } + call_data[256]; +}; + +static void init_instrumentation_data(struct instrumentation_callback_data *d) +{ + memset( d, 0xcc, sizeof(*d) ); + d->call_count = 0; +} + +static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0x50, 0x52, /* push %rax, %rdx */ + + 0x48, 0xba, /* movabs instrumentation_call_count, %rdx */ + /* &instrumentation_call_count, offset 4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%eax */ + 0xf0, 0x0f, 0xc1, 0x02, /* lock xadd %eax,(%rdx) */ + 0x0f, 0xb6, 0xc0, /* movzx %al,%eax */ + 0x48, 0xba, /* movabs instrumentation_call_data, %rdx */ + /* instrumentation_call_data, offset 26 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x01, 0xc0, /* add %rax,%rax */ + 0x48, 0x8d, 0x14, 0xc2, /* lea (%rdx,%rax,8),%rdx */ + 0x4c, 0x89, 0x12, /* mov %r10,(%rdx) */ + 0x48, 0x89, 0x4a, 0x08, /* mov %rcx,0x8(%rdx) */ + + 0x5a, 0x58, /* pop %rdx, %rax */ + 0x41, 0xff, 0xe2, /* jmp *r10 */ + }; + + struct instrumentation_callback_data curr_data, data; + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + void *pLdrInitializeThunk; + EXCEPTION_RECORD record; + void *vectored_handler; + unsigned int i, count; + NTSTATUS status; + HANDLE thread; + CONTEXT ctx; + HWND hwnd; + LONG pass; + + if (is_arm64ec) return; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(void **)((char *)code_mem + 4) = &curr_data.call_count; + *(void **)((char *)code_mem + 26) = curr_data.call_data; + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + /* If instrumentation callback is not yet set during syscall entry it won't be called on exit. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + + init_instrumentation_data( &curr_data ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info), NULL ); + data = curr_data; + ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess + && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20, + "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess ); + todo_wine ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + + vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler ); + ok( !!vectored_handler, "failed.\n" ); + init_instrumentation_data( &curr_data ); + DbgBreakPoint(); + data = curr_data; + todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + + pass = 0; + InterlockedIncrement( &pass ); + pRtlCaptureContext( &ctx ); + if (InterlockedIncrement( &pass ) == 2) /* interlocked to prevent compiler from moving before capture */ + { + record.ExceptionCode = 0xceadbeef; + record.NumberParameters = 0; + init_instrumentation_data( &curr_data ); + status = pNtRaiseException( &record, &ctx, TRUE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 3) + { + data = curr_data; + todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + init_instrumentation_data( &curr_data ); + NtContinue( &ctx, FALSE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 4) + { + data = curr_data; + /* Not called for NtContinue. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + init_instrumentation_data( &curr_data ); + NtSetContextThread( GetCurrentThread(), &ctx ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 5) + { + data = curr_data; + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); + init_instrumentation_data( &curr_data ); + } + ok( pass == 5, "got %ld.\n", pass ); + RemoveVectoredExceptionHandler( vectored_handler ); + + status = pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); + init_instrumentation_data( &curr_data ); + apc_called = FALSE; + SleepEx( 0, TRUE ); + data = curr_data; + ok( apc_called, "APC was not called.\n" ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); + + instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL ); + instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + init_instrumentation_data( &curr_data ); + thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pLdrInitializeThunk) break; + } + todo_wine ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); + + init_instrumentation_data( &curr_data ); + SetEvent( instrumentation_callback_thread_wait ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + count = 0; + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20) + ++count; + } + todo_wine ok( count == 2, "got %u.\n", count ); + + SetEvent( instrumentation_callback_thread_wait ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( instrumentation_callback_thread_ready ); + CloseHandle( instrumentation_callback_thread_wait ); + + hwnd = CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + init_instrumentation_data( &curr_data ); + DestroyWindow( hwnd ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pKiUserCallbackDispatcher) + break; + } + todo_wine ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); + + init_instrumentation_data( &curr_data ); + memset(&info, 0, sizeof(info)); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( !status, "got %#lx.\n", status ); + ok( !data.call_count, "got %u.\n", data.call_count ); +} + #elif defined(__arm__)
static void test_thread_context(void) @@ -11544,6 +11819,7 @@ START_TEST(exception) test_copy_context(); test_set_live_context(); test_hwbpt_in_syscall(); + test_instrumentation_callback();
#elif defined(__x86_64__)
@@ -11574,6 +11850,7 @@ START_TEST(exception) test_syscall_clobbered_regs(); test_raiseexception_regs(); test_hwbpt_in_syscall(); + test_instrumentation_callback();
#elif defined(__aarch64__)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 28 ++++++++++++------------- dlls/ntdll/unix/process.c | 8 ++++++++ dlls/ntdll/unix/signal_x86_64.c | 36 +++++++++++++++++++++++++++++---- dlls/ntdll/unix/unix_private.h | 1 + dlls/wow64/process.c | 2 +- 5 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 93b43a02608..4d8439ec673 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5598,11 +5598,11 @@ static void test_instrumentation_callback(void) status = NtQueryInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info), NULL ); data = curr_data; ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20, "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess ); - todo_wine ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); + ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx );
memset(&info, 0, sizeof(info)); info.Callback = code_mem; @@ -5610,7 +5610,7 @@ static void test_instrumentation_callback(void) status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); data = curr_data; ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_count == 1, "got %u.\n", data.call_count );
vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler ); ok( !!vectored_handler, "failed.\n" ); @@ -5635,8 +5635,8 @@ static void test_instrumentation_callback(void) else if (pass == 3) { data = curr_data; - todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserExceptionDispatcher ); init_instrumentation_data( &curr_data ); NtContinue( &ctx, FALSE ); @@ -5654,8 +5654,8 @@ static void test_instrumentation_callback(void) else if (pass == 5) { data = curr_data; - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); init_instrumentation_data( &curr_data ); } ok( pass == 5, "got %ld.\n", pass ); @@ -5668,8 +5668,8 @@ static void test_instrumentation_callback(void) SleepEx( 0, TRUE ); data = curr_data; ok( apc_called, "APC was not called.\n" ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher );
instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL ); instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL ); @@ -5677,7 +5677,7 @@ static void test_instrumentation_callback(void) thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL ); NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" ); for (i = 0; i < data.call_count; ++i) { @@ -5689,14 +5689,14 @@ static void test_instrumentation_callback(void) SetEvent( instrumentation_callback_thread_wait ); NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); count = 0; for (i = 0; i < data.call_count; ++i) { if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20) ++count; } - todo_wine ok( count == 2, "got %u.\n", count ); + ok( count == 2, "got %u.\n", count );
SetEvent( instrumentation_callback_thread_wait ); WaitForSingleObject( thread, INFINITE ); @@ -5708,7 +5708,7 @@ static void test_instrumentation_callback(void) init_instrumentation_data( &curr_data ); DestroyWindow( hwnd ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); for (i = 0; i < data.call_count; ++i) { if (data.call_data[i].r10 == pKiUserCallbackDispatcher) diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index e00b5ca6ca6..915bf04c412 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -78,6 +78,8 @@ static ULONG execute_flags = MEM_EXECUTE_OPTION_DISABLE;
static UINT process_error_mode;
+void *instrumentation_callback; + static char **build_argv( const UNICODE_STRING *cmdline, int reserved ) { char **argv, *arg, *src, *dst; @@ -1716,6 +1718,12 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class,
if (size < sizeof(*instr)) return STATUS_INFO_LENGTH_MISMATCH; ret = STATUS_SUCCESS; + if (handle != GetCurrentProcess()) + { + FIXME( "Setting ProcessInstrumentationCallback is not yet supported for other process.\n" ); + break; + } + InterlockedExchangePointer( &instrumentation_callback, instr->Callback ); break; }
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 1557d7bcb43..c57df9d916e 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -392,6 +392,8 @@ C_ASSERT( sizeof(struct callback_stack_layout) == 0x58 ); #define SYSCALL_HAVE_XSAVEC 2 #define SYSCALL_HAVE_PTHREAD_TEB 4 #define SYSCALL_HAVE_WRFSGSBASE 8 +#define SYSCALL_INSTRUMENTATION 16 /* set in wine_syscall_dispatcher / wine_syscall_dispatcher_return, + * never stored in syscall_frame->syscall_flags. */
static unsigned int syscall_flags;
@@ -421,7 +423,7 @@ struct syscall_frame void *syscall_cfa; /* 00a8 */ DWORD syscall_flags; /* 00b0 */ DWORD restore_flags; /* 00b4 */ - DWORD align[2]; /* 00b8 */ + void **instrumentation_callback; /* 00b8 */ XMM_SAVE_AREA32 xsave; /* 00c0 */ DECLSPEC_ALIGN(64) XSAVE_AREA_HEADER xstate; /* 02c0 */ }; @@ -1604,6 +1606,8 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movl 0xb0(%r10),%r14d\n\t" /* prev_frame->syscall_flags */ "movl %r14d,0xb0(%rsp)\n\t" /* frame->syscall_flags */ "movq %r10,0xa0(%rsp)\n\t" /* frame->prev_frame */ + "movq 0xb8(%r10),%r10\n\t" /* prev_frame->instrumentation_callback */ + "movq %r10,0xb8(%rsp)\n\t" /* frame->instrumentation_callback */ "movq %rsp,0x328(%r8)\n\t" /* amd64_thread_data()->syscall_frame */ /* switch to user stack */ "movq %rdi,%rsp\n\t" /* user_rsp */ @@ -2593,6 +2597,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB frame->restore_flags |= CONTEXT_INTEGER; frame->syscall_flags = syscall_flags; frame->syscall_cfa = syscall_cfa; + frame->instrumentation_callback = &instrumentation_callback;
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL ); __wine_syscall_dispatcher_return( frame, 0 ); @@ -2679,7 +2684,12 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, * depends on us returning to it. Adjust the return address accordingly. */ "subq $0xb,0x70(%rcx)\n\t" "movl 0xb0(%rcx),%r14d\n\t" /* frame->syscall_flags */ - "testl $3,%r14d\n\t" /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */ + "movq 0xb8(%rcx),%rdx\n\t" /* frame->instrumentation_callback */ + "movq (%rdx),%rdx\n\t" + "test %rdx,%rdx\n\t" + "jz 1f\n\t" + "orl $16,%r14d\n\t" /* SYSCALL_INSTRUMENTATION */ + "1\t:testl $3,%r14d\n\t" /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */ "jz 2f\n\t" #ifdef __APPLE__ "movq %gs:0x30,%rdx\n\t" @@ -2832,8 +2842,10 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movw %gs:0x338,%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif + "testl $16,%r14d\n\t" /* SYSCALL_INSTRUMENTATION */ "movq 0x60(%rcx),%r14\n\t" - "testl $0x3,%edx\n\t" /* CONTEXT_CONTROL | CONTEXT_INTEGER */ + "jnz 2f\n\t" + "3:\ttestl $0x3,%edx\n\t" /* CONTEXT_CONTROL | CONTEXT_INTEGER */ "jnz 1f\n\t"
/* switch to user stack */ @@ -2864,8 +2876,10 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher,
"1:\ttestl $0x2,%edx\n\t" /* CONTEXT_INTEGER */ "jnz 1f\n\t" + /* CONTEXT_CONTROL */ "movq (%rsp),%rcx\n\t" /* frame->rip */ "iretq\n" + /* CONTEXT_INTEGER */ "1:\tmovq 0x00(%rcx),%rax\n\t" "movq 0x18(%rcx),%rdx\n\t" "movq 0x30(%rcx),%r8\n\t" @@ -2874,6 +2888,15 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movq 0x48(%rcx),%r11\n\t" "movq 0x10(%rcx),%rcx\n\t" "iretq\n" + /* SYSCALL_INSTRUMENTATION */ + "2:\tmovq 0xb8(%rcx),%r10\n\t" + "movq (%r10),%r10\n\t" + "test %r10,%r10\n\t" + "jz 3b\n\t" + "testl $0x2,%edx\n\t" /* CONTEXT_INTEGER */ + "jnz 1b\n\t" + "xchgq %r10,(%rsp)\n\t" + "iretq\n\t"
/* pop rbp-based kernel stack cfi */ __ASM_CFI("\t.cfi_restore_state\n") @@ -2884,7 +2907,12 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher_return, "movq %rdi,%rcx\n\t" "movl 0xb0(%rcx),%r14d\n\t" /* frame->syscall_flags */ - "movq %rsi,%rax\n\t" + "movq 0xb8(%rcx),%rdx\n\t" /* frame->instrumentation_callback */ + "movq (%rdx),%rdx\n\t" + "test %rdx,%rdx\n\t" + "jz 1f\n\t" + "orl $16,%r14d\n\t" /* SYSCALL_INSTRUMENTATION */ + "1\t:movq %rsi,%rax\n\t" "jmp " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") )
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 8ce45dfa0bc..23305df2080 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -186,6 +186,7 @@ extern SYSTEM_CPU_INFORMATION cpu_info; #ifdef __i386__ extern struct ldt_copy __wine_ldt_copy; #endif +extern void *instrumentation_callback;
extern void init_environment(void); extern void init_startup_info(void); diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 14b9307b447..9b2a6de01b3 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -894,7 +894,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) else return STATUS_INVALID_PARAMETER;
case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ - if (len == sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) + if (len >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) { FIXME( "ProcessInstrumentationCallback stub\n" ); return STATUS_SUCCESS;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 4 ++-- dlls/ntdll/unix/signal_x86_64.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 4d8439ec673..19326f3cc18 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5617,8 +5617,8 @@ static void test_instrumentation_callback(void) init_instrumentation_data( &curr_data ); DbgBreakPoint(); data = curr_data; - todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserExceptionDispatcher );
pass = 0; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index c57df9d916e..8aab736d63a 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1410,6 +1410,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec NTSTATUS status; XSAVE_AREA_HEADER *src_xs; unsigned int xstate_size; + void *callback;
if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP) { @@ -1465,6 +1466,11 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec RSP_sig(sigcontext) = (ULONG_PTR)stack; /* clear single-step, direction, and align check flag */ EFL_sig(sigcontext) &= ~(0x100|0x400|0x40000); + if ((callback = instrumentation_callback)) + { + R10_sig(sigcontext) = RIP_sig(sigcontext); + RIP_sig(sigcontext) = (ULONG64)callback; + } leave_handler( sigcontext ); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 19326f3cc18..582df9729c3 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5683,7 +5683,7 @@ static void test_instrumentation_callback(void) { if (data.call_data[i].r10 == pLdrInitializeThunk) break; } - todo_wine ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); + ok( i < data.call_count, "LdrInitializeThunk not found.\n" );
init_instrumentation_data( &curr_data ); SetEvent( instrumentation_callback_thread_wait ); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 8aab736d63a..d811d014fd4 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2526,6 +2526,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB struct amd64_thread_data *thread_data = (struct amd64_thread_data *)&teb->GdiTebBatch; CONTEXT *ctx, context = { 0 }; I386_CONTEXT *wow_context; + void *callback;
thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; @@ -2604,6 +2605,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB frame->syscall_flags = syscall_flags; frame->syscall_cfa = syscall_cfa; frame->instrumentation_callback = &instrumentation_callback; + if ((callback = instrumentation_callback)) + { + frame->r10 = frame->rip; + frame->rip = (ULONG64)callback; + }
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL ); __wine_syscall_dispatcher_return( frame, 0 );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 2 +- dlls/ntdll/unix/process.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 582df9729c3..5d692334c0a 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5714,7 +5714,7 @@ static void test_instrumentation_callback(void) if (data.call_data[i].r10 == pKiUserCallbackDispatcher) break; } - todo_wine ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); + ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" );
init_instrumentation_data( &curr_data ); memset(&info, 0, sizeof(info)); diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 915bf04c412..fa8785897c3 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1714,7 +1714,7 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info;
- FIXME( "ProcessInstrumentationCallback stub.\n" ); + if (!is_win64 || is_wow64() || is_arm64ec()) FIXME( "ProcessInstrumentationCallback stub.\n" );
if (size < sizeof(*instr)) return STATUS_INFO_LENGTH_MISMATCH; ret = STATUS_SUCCESS; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index d811d014fd4..46d92356e7a 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1614,6 +1614,7 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movq %r10,0xa0(%rsp)\n\t" /* frame->prev_frame */ "movq 0xb8(%r10),%r10\n\t" /* prev_frame->instrumentation_callback */ "movq %r10,0xb8(%rsp)\n\t" /* frame->instrumentation_callback */ + "movq (%r10),%r10\n\t" "movq %rsp,0x328(%r8)\n\t" /* amd64_thread_data()->syscall_frame */ /* switch to user stack */ "movq %rdi,%rsp\n\t" /* user_rsp */ @@ -1623,7 +1624,10 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movw 0x338(%r8),%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif - "jmpq *%rcx" ) /* func */ + "test %r10,%r10\n\t" + "jz 1f\n\t" + "xchgq %rcx,%r10\n\t" + "1\t:jmpq *%rcx" ) /* func */
/***********************************************************************
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/info.c | 16 ++++++++++++++++ dlls/ntdll/unix/process.c | 7 +++++-- dlls/wow64/process.c | 2 +- dlls/wow64/struct32.h | 7 ------- 4 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index ba19b248e43..b2a373ce1b3 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -3723,6 +3723,22 @@ static void test_process_instrumentation_callback(void) ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, "Got unexpected status %#lx.\n", status ); + + if (status) + { + win_skip( "NtSetInformationProcess failed, skipping further tests.\n" ); + return; + } + + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) + 4 ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) / 2 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); }
static void test_debuggee_dbgport(int argc, char **argv) diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index fa8785897c3..e4b74ef364a 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1713,17 +1713,20 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, case ProcessInstrumentationCallback: { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info; + void *callback;
if (!is_win64 || is_wow64() || is_arm64ec()) FIXME( "ProcessInstrumentationCallback stub.\n" );
- if (size < sizeof(*instr)) return STATUS_INFO_LENGTH_MISMATCH; + if (size < sizeof(callback)) return STATUS_INFO_LENGTH_MISMATCH; + if (size >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION)) callback = instr->Callback; + else callback = *(void **)info; ret = STATUS_SUCCESS; if (handle != GetCurrentProcess()) { FIXME( "Setting ProcessInstrumentationCallback is not yet supported for other process.\n" ); break; } - InterlockedExchangePointer( &instrumentation_callback, instr->Callback ); + InterlockedExchangePointer( &instrumentation_callback, callback ); break; }
diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 9b2a6de01b3..653422a5013 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -894,7 +894,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) else return STATUS_INVALID_PARAMETER;
case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ - if (len >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) + if (len >= sizeof(void *)) { FIXME( "ProcessInstrumentationCallback stub\n" ); return STATUS_SUCCESS; diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index b196bdd53ca..fe2bbc758ee 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -228,13 +228,6 @@ typedef struct ULONG InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION32;
-typedef struct -{ - ULONG Version; - ULONG Reserved; - ULONG Callback; -} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32; - typedef struct { ULONG ReserveSize;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148398
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 00000000004500D6, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
v2:
- add a basic test for x86 / 32bit. Turns out Windows never uses PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION structure on 32 bit and expects just the callback pointer to be passed, that's why my initial local tests weren't showing any effect from setting the callback on 32 bit; - fix wow64 thunk so it returns status / prints fixme based on correct size check; - keep FIXME in NtSetInformationProcess around for archs where that is not supported; - add a patch which allows setting the callback using old-style parameter (just the callback pointer) which also works on x64.
On Thu Sep 12 01:37:06 2024 +0000, Paul Gofman wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/6446/diffs?diff_id=131754&start_sha=7a4aded9e8ce42d47770246a632b1c047d058577)
Thanks, I now see the added check for arm64ec. I still wonder if we need explicit check for arm64 (not ec).
@julliard Alexandre, are there any other generic (or specific) concerns about this MR? If there are, I'd probably avoid iterating it with this FIXME changes alone.