-- v5: 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_arm.c | 6 ++++ dlls/ntdll/unix/signal_arm64.c | 6 ++++ dlls/ntdll/unix/signal_i386.c | 6 ++++ dlls/ntdll/unix/signal_x86_64.c | 63 +++++++++++++++++++++++++++++++-- dlls/ntdll/unix/unix_private.h | 2 ++ dlls/wow64/process.c | 2 +- 8 files changed, 102 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..c2b3b769c8e 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1712,10 +1712,14 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info;
- FIXME( "ProcessInstrumentationCallback stub.\n" ); - 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; + } + set_process_instrumentation_callback( instr->Callback ); break; }
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 9cc1a49f6c1..bda2d5c7500 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -257,6 +257,12 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) }
+void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not suppored.\n" ); +} + + /*********************************************************************** * unwind_builtin_dll */ diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 24bbdc584cd..10a469b6657 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -204,6 +204,12 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) }
+void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not suppored.\n" ); +} + + /*********************************************************************** * unwind_builtin_dll * diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index b727a4d5d6a..aa179ffd60d 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -603,6 +603,12 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) }
+void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not suppored.\n" ); +} + + struct xcontext { CONTEXT c; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 1557d7bcb43..d6f44ed8dbb 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -395,6 +395,8 @@ C_ASSERT( sizeof(struct callback_stack_layout) == 0x58 );
static unsigned int syscall_flags;
+#define RESTORE_FLAGS_INSTRUMENTATION CONTEXT_i386 + struct syscall_frame { ULONG64 rax; /* 0000 */ @@ -444,6 +446,7 @@ struct amd64_thread_data DWORD fs; /* 0338 WOW TEB selector */ DWORD xstate_features_size; /* 033c */ UINT64 xstate_features_mask; /* 0340 */ + void **instrumentation_callback; /* 0348 */ };
C_ASSERT( sizeof(struct amd64_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) ); @@ -475,6 +478,23 @@ static BOOL is_inside_syscall( const ucontext_t *sigcontext ) }
+extern void __wine_syscall_dispatcher_instrumentation(void); +static void *instrumentation_callback; +static pthread_mutex_t instrumentation_callback_mutex = PTHREAD_MUTEX_INITIALIZER; + +void set_process_instrumentation_callback( void *callback ) +{ + void *ptr = (char *)user_shared_data + page_size; + sigset_t sigset; + void *old; + + server_enter_uninterrupted_section( &instrumentation_callback_mutex, &sigset ); + old = InterlockedExchangePointer( &instrumentation_callback, callback ); + if (!old && callback) InterlockedExchangePointer( ptr, __wine_syscall_dispatcher_instrumentation ); + else if (old && !callback) InterlockedExchangePointer( ptr, __wine_syscall_dispatcher ); + server_leave_uninterrupted_section( &instrumentation_callback_mutex, &sigset ); +} + struct xcontext { CONTEXT c; @@ -1900,7 +1920,8 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo )
/* disallow single-stepping through a syscall */
- if ((void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher) + if ((void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher + || (void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher_instrumentation) { extern const void *__wine_syscall_dispatcher_prolog_end_ptr;
@@ -1926,6 +1947,7 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo ) frame->rip = *(ULONG64 *)RSP_sig( sigcontext ); frame->eflags = EFL_sig(sigcontext); frame->restore_flags = CONTEXT_CONTROL; + if (instrumentation_callback) frame->restore_flags |= RESTORE_FLAGS_INSTRUMENTATION;
RCX_sig( sigcontext ) = (ULONG64)frame; RSP_sig( sigcontext ) += sizeof(ULONG64); @@ -2520,6 +2542,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; assert( thread_data->xstate_features_size == xstate_features_size ); + thread_data->instrumentation_callback = &instrumentation_callback;
#if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); @@ -2832,8 +2855,10 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movw %gs:0x338,%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif + "testl $0x10000,%edx\n\t" /* RESTORE_FLAGS_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 +2889,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 +2901,20 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movq 0x48(%rcx),%r11\n\t" "movq 0x10(%rcx),%rcx\n\t" "iretq\n" + /* RESTORE_FLAGS_INSTRUMENTATION */ +#ifdef __APPLE__ + "2:\tmovq %gs:0x30,%r10\n\t" + "movq 0x348(%r10),%r10\n\t" +#else + "2:\tmovq %gs:0x348,%r10\n\t" /* amd64_thread_data()->instrumentation_callback */ +#endif + "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") @@ -2888,6 +2929,24 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher_return, "jmp " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") )
+__ASM_GLOBAL_FUNC( __wine_syscall_dispatcher_instrumentation, +#ifdef __APPLE__ + "movq %gs:0x30,%rcx\n\t" + "movq 0x328(%rcx),%rcx\n\t" +#else + "movq %gs:0x328,%rcx\n\t" /* amd64_thread_data()->syscall_frame */ +#endif + "popq 0x70(%rcx)\n\t" /* frame->rip */ + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + __ASM_CFI_REG_IS_AT2(rip, rcx, 0xf0,0x00) + "pushfq\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") + "popq 0x80(%rcx)\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + "movl $0x10000,0xb4(%rcx)\n\t" /* frame->restore_flags <- RESTORE_FLAGS_INSTRUMENTATION */ + "jmp " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_prolog_end") ) + + /*********************************************************************** * __wine_unix_call_dispatcher */ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 8ce45dfa0bc..8815f174de9 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -248,6 +248,8 @@ static inline UINT64 xstate_extended_features(void) return xstate_supported_features_mask & ~(UINT64)3; }
+extern void set_process_instrumentation_callback( void *callback ); + extern void *get_cpu_area( USHORT machine ); extern void set_thread_id( TEB *teb, DWORD pid, DWORD tid ); extern NTSTATUS init_thread_stack( TEB *teb, ULONG_PTR limit, SIZE_T reserve_size, SIZE_T commit_size ); 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 d6f44ed8dbb..6f0f58ba31e 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1428,6 +1428,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) { @@ -1483,6 +1484,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 6f0f58ba31e..b7a4bb736bb 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2544,6 +2544,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; @@ -2622,6 +2623,11 @@ 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; + 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/signal_x86_64.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 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/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index b7a4bb736bb..7a7e82077e2 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1639,7 +1639,12 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movw 0x338(%r8),%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif - "jmpq *%rcx" ) /* func */ + "movq 0x348(%r8),%r10\n\t" /* amd64_thread_data()->instrumentation_callback */ + "movq (%r10),%r10\n\t" + "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 c2b3b769c8e..f5c230e9dc1 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1711,15 +1711,18 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, case ProcessInstrumentationCallback: { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info; + void *callback;
- 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; } - set_process_instrumentation_callback( instr->Callback ); + set_process_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=148610
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 0000000000A300F2, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
v4:
- move callback pointer to thread data; - remove superfluous asm label.