From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 242 ++++++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c2ceef5eb7f..d4b29da30c9 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5264,6 +5264,7 @@ static void test_syscall_clobbered_regs(void) struct regs { UINT64 rcx; + UINT64 r10; UINT64 r11; UINT32 eflags; }; @@ -5281,16 +5282,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 +5315,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 +5326,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 +5334,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 +5349,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 +5458,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, "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, "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) @@ -11574,6 +11809,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 | 11 ++++++++-- dlls/ntdll/unix/signal_x86_64.c | 36 +++++++++++++++++++++++++++++---- dlls/ntdll/unix/unix_private.h | 1 + 4 files changed, 56 insertions(+), 20 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index d4b29da30c9..98f329605d8 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5558,11 +5558,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; @@ -5570,7 +5570,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" ); @@ -5595,8 +5595,8 @@ static void test_instrumentation_callback(void) else if (pass == 3) { data = curr_data; - todo_wine ok( data.call_count == 1, "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, "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 ); @@ -5614,8 +5614,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 ); @@ -5628,8 +5628,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 ); @@ -5637,7 +5637,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) { @@ -5649,14 +5649,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 ); @@ -5668,7 +5668,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..6ec53aaa5b2 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; @@ -1712,10 +1714,15 @@ 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 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);
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 98f329605d8..9fcabac5f3b 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5577,8 +5577,8 @@ static void test_instrumentation_callback(void) init_instrumentation_data( &curr_data ); DbgBreakPoint(); data = curr_data; - todo_wine ok( data.call_count == 1, "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, "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 9fcabac5f3b..adbe0c861b2 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5643,7 +5643,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/signal_x86_64.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index adbe0c861b2..6b8145fb504 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5674,7 +5674,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 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 */
/***********************************************************************
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=148228
Your paranoid android.
=== w864 (64 bit report) ===
ntdll: exception.c:4511: Test failed: cs64: rsp not inside stack 000000000053F560 / 64-bit 0000000000400000-000000000043FD30 32-bit 0000000000000000-0000000000000000
=== w1064v1507 (64 bit report) ===
ntdll: exception.c:5580: Test failed: got 2. exception.c:5598: Test failed: got 2.