We expect it to fail because the ITfUIElementSink interface is flagged as "local" and cannot be marshalled, and because ITfThreadMgr is flagged with "threading(apartment)" and cannot be instanciated in the MTA.
Note: The real purpose of the test is to reproduce an unwinding issue that has nothing to do with msctf or ole, but that is triggered by this specific code with WINEDEBUG=-all. --- dlls/msctf/tests/inputprocessor.c | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/dlls/msctf/tests/inputprocessor.c b/dlls/msctf/tests/inputprocessor.c index d73a3f94510..1222085fe0a 100644 --- a/dlls/msctf/tests/inputprocessor.c +++ b/dlls/msctf/tests/inputprocessor.c @@ -2511,6 +2511,53 @@ static void test_profile_mgr(void) ITfInputProcessorProfileMgr_Release(ipp_mgr); }
+static DWORD WINAPI test_MultiThreadApartment_Thread(void *param) { + ITfThreadMgrEx *thmgr; + ITfSource *source; + DWORD cookie; + HRESULT hr; + + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + ok(SUCCEEDED(hr), "Failed to initialize multi-threaded apartment\n"); + + hr = CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&thmgr); + ok(SUCCEEDED(hr), "Failed to create ITfThreadMgrEx instance\n"); + + hr = ITfThreadMgrEx_QueryInterface(thmgr, &IID_ITfSource, (LPVOID *)&source); + ok(SUCCEEDED(hr), "Failed to query ITfSource interface\n"); + + hr = ITfSource_AdviseSink(source, &IID_ITfUIElementSink, (IUnknown*)&TfUIElementSink, &cookie); + ok(hr == REGDB_E_IIDNOTREG /* native */ || hr == E_NOINTERFACE /* wine */, + "Advise ITfUIElementSink should return marshalling failure: %08x\n", hr); + + hr = ITfSource_Release(source); + ok(SUCCEEDED(hr), "Failed to Release source\n"); + + hr = ITfThreadMgrEx_Release(thmgr); + ok(SUCCEEDED(hr), "Failed to Release thread manager\n"); + + CoUninitialize(); + + return 0xdeadcafe; +} + +static void test_MultiThreadApartment(void) +{ + DWORD ret; + HANDLE thread; + + thread = CreateThread(0, 0, test_MultiThreadApartment_Thread, 0, 0, 0); + ok(thread != NULL, "Failed to create test thread\n"); + + ret = WaitForSingleObject(thread, INFINITE); + ok(ret == WAIT_OBJECT_0, "Failed to wait for thread completion\n"); + + GetExitCodeThread(thread, &ret); + ok(ret == 0xdeadcafe, "Thread terminated in an unexpected way\n"); + + CloseHandle(thread); +} + START_TEST(inputprocessor) { if (SUCCEEDED(initialize())) @@ -2540,6 +2587,7 @@ START_TEST(inputprocessor) test_UnregisterCategory(); test_Unregister(); test_profile_mgr(); + test_MultiThreadApartment();
ITextStoreACPSink_Release(ACPSink); ITfDocumentMgr_Release(g_dm);
Without it, __wine_rtl_unwind confuses the unwinding code on x64 and it then triggers an infinite exception loop.
This can be reproduced for example in msctf inputprocessor's test, with the test_MultiThreadApartment test case that raises an E_NOINTERFACE exception. It fails to unwind correctly on x64 with WINEDEBUG=-all,+seh. ---
I don't understand very well what is going on here, the issue looks to be a misaligned stack somewhere that leads to invalid rsp/rbp when unwinded. It happens when __widl_exception_handler calls __wine_rtl_unwind to unwind the stack to the RpcFinally/RpcExcept statements, for example when an RPC error is raised (in this case, when E_NOINTERFACE is raised because of impossible marshalling).
These .seh_* statements are generated on cross-compiled C functions, and adding them for assembly functions seems to solve the issue.
dlls/winecrt0/exception.c | 1 + include/wine/asm.h | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/dlls/winecrt0/exception.c b/dlls/winecrt0/exception.c index df7315e6d40..66c7efbb0e0 100644 --- a/dlls/winecrt0/exception.c +++ b/dlls/winecrt0/exception.c @@ -122,6 +122,7 @@ __ASM_GLOBAL_FUNC( __wine_rtl_unwind, "movq %rsp,%rbp\n\t" __ASM_CFI(".cfi_def_cfa_register %rbp\n\t") "subq $0x20,%rsp\n\t" + __ASM_SEH(".seh_stackalloc 0x20\n\t") "movq %r8,%r9\n\t" /* retval = final target */ "movq %rdx,%r8\n\t" /* record */ "leaq __wine_unwind_trampoline(%rip),%rdx\n\t" /* target = trampoline */ diff --git a/include/wine/asm.h b/include/wine/asm.h index c1a6819cd52..691a8aeb98b 100644 --- a/include/wine/asm.h +++ b/include/wine/asm.h @@ -49,14 +49,20 @@ # define __ASM_FUNC_TYPE(name) ".type " name ",@function" #endif
+#if defined(_WIN64) && defined(__MINGW32__) +#define __ASM_SEH(code) code +#else +#define __ASM_SEH(code) "" +#endif + #ifdef __GNUC__ # define __ASM_DEFINE_FUNC(name,code) \ - asm(".text\n\t.align 4\n\t.globl " name "\n\t" __ASM_FUNC_TYPE(name) "\n" name ":\n\t" \ - __ASM_CFI(".cfi_startproc\n\t") code __ASM_CFI("\n\t.cfi_endproc") ); + asm(".text\n\t.align 4\n\t.globl " name "\n\t" __ASM_FUNC_TYPE(name) __ASM_SEH("\n\t.seh_proc " name) "\n" name ":\n\t" \ + __ASM_CFI(".cfi_startproc\n\t") code __ASM_CFI("\n\t.cfi_endproc") __ASM_SEH("\n\t.seh_endproc") ); #else # define __ASM_DEFINE_FUNC(name,code) void __asm_dummy_##__LINE__(void) { \ - asm(".text\n\t.align 4\n\t.globl " name "\n\t" __ASM_FUNC_TYPE(name) "\n" name ":\n\t" \ - __ASM_CFI(".cfi_startproc\n\t") code __ASM_CFI("\n\t.cfi_endproc") ); } + asm(".text\n\t.align 4\n\t.globl " name "\n\t" __ASM_FUNC_TYPE(name) __ASM_SEH("\n\t.seh_proc " name) "\n" name ":\n\t" \ + __ASM_CFI(".cfi_startproc\n\t") code __ASM_CFI("\n\t.cfi_endproc") __ASM_SEH("\n\t.seh_endproc") ); } #endif
#define __ASM_GLOBAL_FUNC(name,code) __ASM_DEFINE_FUNC(__ASM_NAME(#name),code) -- 2.23.0.rc1
Signed-off-by: Rémi Bernon rbernon@codeweavers.com