Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v1 -> v2: - shorten line width - s/SetUserObjectInformation/SetUserObjectInformationW/ - add case for UOI config performed prior to the dispatch - mark UOI_TIMERPROC_EXCEPTION_SUPPRESSION exception handling as broken - deal with EXCEPTION_BREAKPOINT IP rewinding on x86/x64 architectures. - explicitly test for exception handling phase - skip exception tests if on non-PE build or compiler has no SEH support
dlls/user32/tests/msg.c | 189 ++++++++++++++++++++++++++++++++++++++++ include/wine/asm.h | 1 + 2 files changed, 190 insertions(+)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 918ee97bfa5..7fb8469ec2a 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -34,6 +34,8 @@ #include "commctrl.h"
#include "wine/test.h" +#include "wine/asm.h" +#include "wine/exception.h"
#define MDI_FIRST_CHILD_ID 2004
@@ -10647,11 +10649,33 @@ static void CALLBACK callback_count(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWOR count++; }
+enum timer_exception_phase { + TIMER_EXCEPTION_INITIAL, + TIMER_EXCEPTION_RAISED, + TIMER_EXCEPTION_CONTINUE, + TIMER_EXCEPTION_CONTINUE_OK, +}; + static DWORD exception; +static enum timer_exception_phase timer_exc_phase; +static BOOL *tproc_exc_suppress = NULL; static void CALLBACK callback_exception(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { + if (tproc_exc_suppress) + { + BOOL res = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + tproc_exc_suppress, sizeof(*tproc_exc_suppress)); + todo_wine + ok(res, "SetUserObjectInformationW error %lu\n", GetLastError()); + tproc_exc_suppress = NULL; + } + count++; + timer_exc_phase = TIMER_EXCEPTION_RAISED; RaiseException(exception, 0, 0, NULL); + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE, timer_exc_phase); + timer_exc_phase = TIMER_EXCEPTION_CONTINUE_OK; }
static DWORD WINAPI timer_thread_proc(LPVOID x) @@ -10813,10 +10837,127 @@ static void test_timers_no_wnd(void) while (i > 0) KillTimer(NULL, ids[--i]); }
+static LONG WINAPI timer_exception_filter(EXCEPTION_POINTERS *eptr) +{ + if (timer_exc_phase == TIMER_EXCEPTION_RAISED && + eptr->ExceptionRecord->ExceptionCode == exception && + eptr->ExceptionRecord->ExceptionFlags == 0 && + eptr->ExceptionRecord->NumberParameters == 0) + { + if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) + { +#if defined(__i386__) + if ((ULONG_PTR)eptr->ExceptionRecord->ExceptionAddress == eptr->ContextRecord->Eip + 1) + eptr->ContextRecord->Eip++; /* cancel EIP rewinding */ +#elif defined(__x86_64__) + if ((ULONG_PTR)eptr->ExceptionRecord->ExceptionAddress == eptr->ContextRecord->Rip + 1) + eptr->ContextRecord->Rip++; /* cancel RIP rewinding */ +#endif + } + timer_exc_phase = TIMER_EXCEPTION_CONTINUE; + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +extern void dispatch_message_handle_exception(const MSG *msg); + +#if defined(USE_COMPILER_EXCEPTIONS) || defined(__i386__) +void dispatch_message_handle_exception(const MSG *msg) +{ + __TRY + { + DispatchMessageA( msg ); + } + __EXCEPT(timer_exception_filter) + { + } + __ENDTRY +} +#else +EXCEPTION_DISPOSITION WINAPI timer_exception_handler( EXCEPTION_RECORD *rec, + void *frame, + CONTEXT *context, + DISPATCHER_CONTEXT *dispatch ) +{ + EXCEPTION_POINTERS ptrs = { rec, context }; + + if (timer_exception_filter( &ptrs ) == EXCEPTION_CONTINUE_EXECUTION) + return ExceptionContinueExecution; + + return ExceptionContinueSearch; +} +#if defined(__x86_64__) && defined(__ASM_SEH_SUPPORTED) +__ASM_GLOBAL_FUNC( dispatch_message_handle_exception, + __ASM_SEH(".seh_handler " __ASM_NAME("timer_exception_handler") ", @except\n\t") + "subq $0x28,%rsp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 0x28\n\t") + __ASM_SEH(".seh_stackalloc 0x28\n\t") + __ASM_SEH(".seh_endprologue\n\t") + "callq *__imp_DispatchMessageA(%rip)\n\t" + "nop\n\t" + "addq $0x28,%rsp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -0x28\n\t") + "ret" ); +#elif defined(__arm__) && defined(__WINE_PE_BUILD) +__ASM_GLOBAL_FUNC( dispatch_message_handle_exception, + "1:\n\t" + "push {r4,lr}\n\t" + __ASM_CFI(".cfi_def_cfa_offset 8\n\t") + __ASM_CFI(".cfi_offset r4, -8\n\t") + __ASM_CFI(".cfi_offset lr, -4\n\t") + "ldr ip, =__imp_DispatchMessageA\n\t" + "ldr ip, [ip]\n\t" + "blx ip\n\t" + "pop {r4,pc}\n" + "2:\n\t" + ".section ".pdata", "dr"\n\t" + ".rva " __ASM_NAME("dispatch_message_handle_exception") "\n\t" + ".rva .Lunwind__dispatch_message_handle_exception\n\t" + ".section ".xdata", "dr"\n" + ".Lunwind__dispatch_message_handle_exception:\n\t" + ".long 0x10100000 + ((2b - 1b) / 2)\n\t" + ".long 0xfbfbffd4\n\t" + ".rva " __ASM_NAME("timer_exception_handler") "\n\t" + ".text" ); +#elif defined(__aarch64__) && defined(__ASM_SEH_SUPPORTED) +__ASM_GLOBAL_FUNC( dispatch_message_handle_exception, + __ASM_SEH(".seh_handler " __ASM_NAME("timer_exception_handler") ", @except\n\t") + "stp x29, x30, [sp, #-16]!\n\t" + __ASM_CFI(".cfi_def_cfa_offset 16\n\t") + __ASM_CFI(".cfi_offset x29, -16\n\t") + __ASM_CFI(".cfi_offset x30, -8\n\t") + __ASM_SEH(".seh_save_fplr_x 16\n\t") + __ASM_SEH(".seh_endprologue\n\t") + "mov x29, sp\n\t" + "adrp x8, __imp_DispatchMessageA\n\t" + "ldr x8, [x8, :lo12:__imp_DispatchMessageA]\n\t" + "blr x8\n\t" + "nop\n\t" + __ASM_SEH(".seh_startepilogue\n\t") + "ldp x29, x30, [sp], #16\n\t" + __ASM_CFI(".cfi_restore x29\n\t") + __ASM_CFI(".cfi_restore x30\n\t") + __ASM_CFI(".cfi_def_cfa sp, 0\n\t") + __ASM_SEH(".seh_save_fplr_x 16\n\t") + __ASM_SEH(".seh_endepilogue\n\t") + "ret" ); +#else +void dispatch_message_handle_exception(const MSG *msg) +{ + skip("dispatch_message_handle_exception not implemented on this build configuration\n"); + count++; + timer_exc_phase = TIMER_EXCEPTION_CONTINUE_OK; +} +#endif +#endif + static void test_timers_exception(DWORD code) { UINT_PTR id; MSG msg; + BOOL ret, value;
exception = code; id = SetTimer(NULL, 0, 1000, callback_exception); @@ -10828,8 +10969,56 @@ static void test_timers_exception(DWORD code) msg.lParam = (LPARAM)callback_exception;
count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; DispatchMessageA(&msg); ok(count == 1, "did not get one count as expected (%i).\n", count); + ok(timer_exc_phase == TIMER_EXCEPTION_RAISED, + "expected phase %d, got %d\n", TIMER_EXCEPTION_RAISED, timer_exc_phase); + + value = FALSE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + if (!ret && GetLastError() == ERROR_INVALID_FUNCTION) + { + win_skip("UOI_TIMERPROC_EXCEPTION_SUPPRESSION not supported on this platform\n"); + } + else + { + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + + count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; + dispatch_message_handle_exception(&msg); + ok(count == 1, "expected count to be 1, got %d\n", count); + todo_wine + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || + broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); + + value = TRUE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + + value = FALSE; + tproc_exc_suppress = &value; + count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; + dispatch_message_handle_exception(&msg); + ok(count == 1, "expected count to be 1, got %d\n", count); + todo_wine + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || + broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); + + value = TRUE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + }
KillTimer(NULL, id); } diff --git a/include/wine/asm.h b/include/wine/asm.h index 0547ee94b19..9200491afd0 100644 --- a/include/wine/asm.h +++ b/include/wine/asm.h @@ -50,6 +50,7 @@ # define __ASM_SEH(str) # else # define __ASM_SEH(str) str +# define __ASM_SEH_SUPPORTED # endif #else # define __ASM_SEH(str)