[PATCH 3/3] ntdll: Save unwind information in KiUserApcDispatcher() on x64.

Paul Gofman pgofman at codeweavers.com
Mon Sep 7 06:34:13 CDT 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/tests/exception.c    | 43 +++++++++++++++++++++++++++++++++
 dlls/ntdll/thread.c             | 28 ++++++++++++++++++++-
 dlls/ntdll/unix/signal_x86_64.c | 27 ++++++++++++++++++---
 3 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 340469dd12f..56ddb8d0900 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -37,6 +37,8 @@ static void *code_mem;
 
 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
+static NTSTATUS  (WINAPI *pNtQueueApcThread)(HANDLE handle, PNTAPCFUNC func,
+        ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3);
 static NTSTATUS  (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
 static PVOID     (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
 static VOID      (WINAPI *pRtlCaptureContext)(CONTEXT*);
@@ -3951,6 +3953,45 @@ static void test_nested_exception(void)
     ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n");
 }
 
+static CONTEXT test_unwind_apc_context;
+static BOOL test_unwind_apc_called;
+
+static void CALLBACK test_unwind_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)
+{
+    EXCEPTION_RECORD rec;
+
+    test_unwind_apc_called = TRUE;
+    memset(&rec, 0, sizeof(rec));
+    pRtlUnwind((void *)test_unwind_apc_context.Rsp, (void *)test_unwind_apc_context.Rip, &rec, (void *)0xdeadbeef);
+    ok(0, "Should not get here.\n");
+}
+
+static void test_unwind_from_apc(void)
+{
+    NTSTATUS status;
+    int pass;
+
+    if (!pNtQueueApcThread)
+    {
+        win_skip("NtQueueApcThread is not available.\n");
+        return;
+    }
+
+    pass = 0;
+    InterlockedIncrement(&pass);
+    RtlCaptureContext(&test_unwind_apc_context);
+    InterlockedIncrement(&pass);
+
+    if (pass == 2)
+    {
+        test_unwind_apc_called = FALSE;
+        status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0);
+        ok(!status, "Got unexpected status %#x.\n", status);
+        SleepEx(0, TRUE);
+    }
+    ok(pass == 3, "Got unexpected pass %d.\n", pass);
+    ok(test_unwind_apc_called, "Test user APC was not called.\n");
+}
 #elif defined(__arm__)
 
 static void test_thread_context(void)
@@ -7473,6 +7514,7 @@ START_TEST(exception)
 #define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
     X(NtGetContextThread);
     X(NtSetContextThread);
+    X(NtQueueApcThread);
     X(NtReadVirtualMemory);
     X(NtClose);
     X(RtlUnwind);
@@ -7634,6 +7676,7 @@ START_TEST(exception)
       skip( "Dynamic unwind functions not found\n" );
     test_extended_context();
     test_copy_context();
+    test_unwind_from_apc();
 
 #elif defined(__aarch64__)
 
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 0f31fe18a7d..5364afe6436 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -77,13 +77,39 @@ int __cdecl __wine_dbg_output( const char *str )
 /*******************************************************************
  *		KiUserApcDispatcher (NTDLL.@)
  */
-void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2,
+#ifdef __x86_64__
+void WINAPI dispatch_apc( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2,
                                  PNTAPCFUNC func )
 {
     func( ctx, arg1, arg2 );
     NtContinue( context, TRUE );
 }
 
+__ASM_GLOBAL_FUNC( KiUserApcDispatcher,
+                  "addq $0x8,%rsp\n\t"
+                  "mov 0x98(%rcx),%r10\n\t" /* context->Rsp */
+                  "mov 0xf8(%rcx),%r11\n\t" /* context->Rip */
+                  "mov %rdx,-0x8(%r10)\n\t"
+                  "mov %rbp,-0x10(%r10)\n\t"
+                  "lea -0x10(%r10),%rbp\n\t"
+                  __ASM_SEH(".seh_pushreg %rbp\n\t")
+                  __ASM_SEH(".seh_setframe %rbp,0\n\t")
+                  __ASM_SEH(".seh_endprologue\n\t")
+                  __ASM_CFI(".cfi_signal_frame\n\t")
+                  __ASM_CFI(".cfi_adjust_cfa_offset 0x10\n\t")
+                  __ASM_CFI(".cfi_def_cfa %rbp,0x10\n\t")
+                  __ASM_CFI(".cfi_rel_offset %rip,0x8\n\t")
+                  __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
+                   "call " __ASM_NAME("dispatch_apc") "\n\t"
+                   "int3")
+#else
+void WINAPI KiUserApcDispatcher( CONTEXT *context, ULONG_PTR ctx, ULONG_PTR arg1, ULONG_PTR arg2,
+                                 PNTAPCFUNC func )
+{
+    func( ctx, arg1, arg2 );
+    NtContinue( context, TRUE );
+}
+#endif
 
 /***********************************************************************
  *           RtlExitUserThread  (NTDLL.@)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index e11b2c70b59..70c321b4012 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -1543,6 +1543,23 @@ static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcon
 }
 
 
+/***********************************************************************
+ *		     set_nonvolatile_regs
+ */
+extern void WINAPI set_nonvolatile_regs( const CONTEXT *context );
+__ASM_GLOBAL_FUNC( set_nonvolatile_regs,
+                   "movq 0x90(%rcx),%rbx\n\t"       /* context->Rbx */
+                   "movq 0xa0(%rcx),%rbp\n\t"       /* context->Rbp */
+                   "movq 0xa8(%rcx),%rsi\n\t"       /* context->Rsi */
+                   "movq 0xb0(%rcx),%rdi\n\t"       /* context->Rdi */
+                   "movq 0xd8(%rcx),%r12\n\t"       /* context->R12 */
+                   "movq 0xe0(%rcx),%r13\n\t"       /* context->R13 */
+                   "movq 0xe8(%rcx),%r14\n\t"       /* context->R14 */
+                   "movq 0xf0(%rcx),%r15\n\t"       /* context->R15 */
+                   "fxrstor 0x100(%rcx)\n\t"        /* context->FltSave */
+                   "ret" );
+
+
 /***********************************************************************
  *           set_full_cpu_context
  *
@@ -2006,14 +2023,15 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
  */
 __ASM_GLOBAL_FUNC( call_user_apc_dispatcher,
                    "movq 0x28(%rsp),%rsi\n\t"       /* func */
-                   "movq 0x30(%rsp),%rdi\n\t"       /* dispatcher */
+                   "movq 0x30(%rsp),%r10\n\t"       /* dispatcher */
                    "movq %gs:0x30,%rbx\n\t"
                    "jrcxz 1f\n\t"
                    "movq 0x98(%rcx),%rax\n\t"       /* context_ptr->Rsp */
-                   "leaq -0x5c0(%rax),%rsp\n\t"     /* sizeof(CONTEXT) + offsetof(frame,ret_addr) */
+                   "leaq -0x5e0(%rax),%rsp\n\t"     /* 0x20 (unwind regs space )
+                                                     * + sizeof(CONTEXT) + offsetof(frame,ret_addr) */
                    "jmp 2f\n"
                    "1:\tmovq 0x328(%rbx),%rax\n\t"  /* amd64_thread_data()->syscall_frame */
-                   "leaq -0x4d0(%rax),%rsp\n\t"
+                   "leaq -0x4f0(%rax),%rsp\n\t"     /* 0x20 (unwind regs space ) + sizeof(CONTEXT) */
                    "andq $~15,%rsp\n\t"
                    "movq %rdx,%r12\n\t"             /* ctx */
                    "movq %r8,%r13\n\t"              /* arg1 */
@@ -2031,7 +2049,8 @@ __ASM_GLOBAL_FUNC( call_user_apc_dispatcher,
                    "movq %rsi,0x20(%rsp)\n\t"       /* func */
                    "movq 0xa0(%rcx),%rbp\n\t"       /* context.Rbp */
                    "pushq 0xf8(%rcx)\n\t"           /* context.Rip */
-                   "jmp *%rdi" )
+                   "call " __ASM_NAME("set_nonvolatile_regs") "\n\t"
+                   "jmp *%r10" )
 
 
 /***********************************************************************
-- 
2.26.2




More information about the wine-devel mailing list