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;