This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit ebdb555ed23b18f1315977848b5cb6823d97c054 Author: Peter Kovacs <[email protected]> AuthorDate: Sat Jul 25 10:47:35 2026 +0200 bridges/msvc_win64: fix the x64 C++/UNO bridge (backport origin/windows-amd64) The x64 bridge only linked; it was never actually exercised through a generated vtable snippet (the roundtrip test dispatched via uno_Interface directly), so several real bugs hid until soffice ran. Adopt the validated windows-amd64 versions of the matched set: - call.asm + cpp2uno.cxx: codeSnippet emitted bad machine-code opcodes -- 0x49 where the mov [rsp+8],rcx / mov [rsp+16],rdx opcode must be 0x89 (0x49 is a stray REX prefix, decoding as "and al,8"), so the snippet never homed this/args and cpp_vtable_call got a garbage this (ad7f763788). And callVirtualMethod used .ALLOCSTACK unwind info while adjusting rsp dynamically -> not unwindable -> C++/SEH exceptions could not propagate through it; the reference uses .SETFRAME rbp, required for a frame with dynamic stack allocation (607903dd38). - abi.cxx return_in_hidden_param: the >8-byte-struct case was inverted (returned "register" instead of "hidden memory pointer"), corrupting every struct/exception return larger than 8 bytes -- e.g. getAllExtensions' nested Sequences (0c2e3065a0). - except.cxx: our __type_info helper carried an extra _m_name field, shifting the mangled name off MSVC's type_info layout (offset 24 vs 16). Exact-type catches match by type_info address and worked; cross-DLL BASE-class catches fall back to name strcmp and read the misplaced field -> catch(InteractiveIOException&) never matched a thrown InteractiveAugmentedIOException, so getCaughtException's rethrow escaped uncaught and soffice exited silently after CheckExtensionDependencies. uno2cpp.cxx / abi.hxx / dllinit.cxx / mscx.hxx were already identical to the reference and are unchanged. Co-Authored-By: Claude Opus 4.8 <[email protected]> (cherry picked from commit 4c722fde1cbd72735e4d7bc86cb790e6719790b4) (cherry picked from commit 1040d136d89c8c64bf4ed2991f2779343c1f09d9) --- .../source/cpp_uno/msvc_win64_x86-64/abi.cxx | 10 ++- .../source/cpp_uno/msvc_win64_x86-64/call.asm | 80 ++++++++++-------- .../source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx | 23 ++++-- .../source/cpp_uno/msvc_win64_x86-64/except.cxx | 96 ++++++++++++++++++---- 4 files changed, 143 insertions(+), 66 deletions(-) diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/abi.cxx b/main/bridges/source/cpp_uno/msvc_win64_x86-64/abi.cxx index a1722d4011..a203737a70 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/abi.cxx +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/abi.cxx @@ -66,16 +66,20 @@ bool x86_64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef typelib_TypeDescription * pTypeDescr = 0; TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); - /* If the struct is larger than 8 bytes, pass it on the stack. */ + /* A struct is returned in a register only when its size is + * 1/2/4/8 bytes; otherwise (here: > 8) it is returned via a + * hidden pointer (in memory). This logic was inverted, which + * corrupted struct/exception return marshalling on x64 — e.g. + * getAllExtensions returning nested Sequences (ref 0c2e3065a0). */ if ( pTypeDescr->nSize > 8 ) { TYPELIB_DANGER_RELEASE( pTypeDescr ); - return false; + return true; } else { TYPELIB_DANGER_RELEASE( pTypeDescr ); - return true; + return false; } } diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/call.asm b/main/bridges/source/cpp_uno/msvc_win64_x86-64/call.asm index beb2797521..402d7e2319 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/call.asm +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/call.asm @@ -6,16 +6,16 @@ ; to you under the Apache License, Version 2.0 (the ; "License"); you may not use this file except in compliance ; with the License. You may obtain a copy of the License at -; +; ; http://www.apache.org/licenses/LICENSE-2.0 -; +; ; Unless required by applicable law or agreed to in writing, ; software distributed under the License is distributed on an ; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ; KIND, either express or implied. See the License for the ; specific language governing permissions and limitations ; under the License. -; +; typelib_TypeClass_VOID equ 0 @@ -51,8 +51,8 @@ EXTERN cpp_vtable_call: PROC ; rbp+16 rsp+08 +----------------------------+ ------- ; | return address | ; rbp+08 rsp--> +----------------------------+ -; | old rbp | -; rbp---------> +----------------------------+ +; | caller's rbp | +; rbp---------> +----------------------------+ <------ 16 byte boundary ; | pRegisterReturn memory | ; rbp-08 -----> +----------------------------+ ; | | @@ -72,14 +72,14 @@ EXTERN cpp_vtable_call: PROC ; privateSnippetExecutor PROC FRAME - push rbp + .PUSHREG rbp mov rbp, rsp + .SETFRAME rbp, 0 sub rsp, 48 - .ALLOCSTACK(48) .ENDPROLOG - ; 4th param: sal_uInt64 *pRegisterReturn + ; 4th param: sal_uInt64 *pRegisterReturn lea r9, -8[rbp] ; 3rd param: sal_Int32 nVtableOffset @@ -98,7 +98,8 @@ privateSnippetExecutor PROC FRAME mov rax, -8[rbp] movsd xmm0, qword ptr -8[rbp] - leave + add rsp, 48 + pop rbp ret privateSnippetExecutor ENDP @@ -121,7 +122,7 @@ privateSnippetExecutor ENDP ; rbp+16 -----> +---------------------------------------------+ ------- ; | return address | ; rbp+08 -----> +---------------------------------------------+ -; | old rbp | +; | caller's rbp | ; rbp --------> +---------------------------------------------+ <---- 16 byte boundary ; | (possible 16 byte alignment placeholder) | ; rbp-08 -----> +---------------------------------------------+ @@ -133,8 +134,9 @@ privateSnippetExecutor ENDP callVirtualMethod PROC FRAME push rbp + .PUSHREG rbp mov rbp, rsp - .ENDPROLOG + .SETFRAME rbp, 0 ; Save our register arguments to the shadow space: mov 16[rbp], rcx @@ -142,28 +144,33 @@ callVirtualMethod PROC FRAME mov 32[rbp], r8 mov 40[rbp], r9 - ; We must maintain the stack aligned to a 16 byte boundary: + ; nStack rounded to multiple of 2, so the stack is aligned to a 16 byte boundary: mov eax, 56[rbp] - cmp rax, 0 - je stackIsEmpty - mov r11, rax - test rax,1 - jz stackSizeEven - sub rsp, 8 -stackSizeEven: - mov r10, 48[rbp] - shl rax, 3 ; nStack is in units of sal_uInt64, and sizeof(sal_uInt64) == 8 - add rax, r10 + inc eax + shr eax, 1 + shl eax, 1 + + ; if nStack < 4, add (4 - nStack) empty slots to the stack: + mov r10d, 4 + sub r10d, eax + js copyStack + shl r10, 3 + sub rsp, r10 copyStack: + mov r10, rax + shl rax, 3 + add rax, 48[rbp] +copyStackLoop: sub rax, 8 push [rax] - dec r11 - jne copyStack + dec r10 + jne copyStackLoop +populateArgumentRegisters: ; First 4 args are passed in registers. Floating point args needs to be - ; in floating point registers, but those are free for us to clobber - ; anyway, and the callee knows where to look, so put each arg in both + ; in floating point registers, but those are volatile anyway, + ; and the callee knows where to look, so put each arg in both ; its general purpose and its floating point register: mov rcx, [rsp] movsd xmm0, qword ptr [rsp] @@ -173,11 +180,10 @@ copyStack: movsd xmm2, qword ptr 16[rsp] mov r9, 24[rsp] movsd xmm3, qword ptr 24[rsp] - jmp callMethod -stackIsEmpty: - sub rsp, 32 ; we still need shadow space + .ENDPROLOG + callMethod: ; Find the method pointer mov rax, 16[rbp] @@ -188,6 +194,7 @@ callMethod: call qword ptr [r10] mov r10d, 40[rbp] + mov r11, 32[rbp] cmp r10, typelib_TypeClass_VOID je cleanup cmp r10, typelib_TypeClass_LONG @@ -217,30 +224,31 @@ callMethod: ; https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017 ; "The same pointer must be returned by the callee in RAX." - jmp Lint64 + jmp cleanup Lint64: - mov 32[rbp], rax + mov qword ptr [r11], rax jmp cleanup Lint32: - mov 32[rbp], eax + mov dword ptr [r11], eax jmp cleanup Lint16: - mov 32[rbp], ax + mov word ptr [r11], ax jmp cleanup Lint8: - mov 32[rbp], al + mov byte ptr [r11], al jmp cleanup Lfloat: - movsd qword ptr 32[rbp], xmm0 + movsd qword ptr [r11], xmm0 jmp cleanup cleanup: - leave + lea rsp, 0[rbp] + pop rbp ret callVirtualMethod ENDP diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx b/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx index 5deb06b112..59f250784c 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx @@ -1,5 +1,5 @@ /************************************************************** - * + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -7,16 +7,16 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * + * *************************************************************/ @@ -343,7 +343,7 @@ extern "C" typelib_TypeClass cpp_vtable_call( //================================================================================================== extern "C" void privateSnippetExecutor( ... ); -int const codeSnippetSize = 44; +int const codeSnippetSize = 48; unsigned char * codeSnippet( unsigned char * code, sal_Int32 functionIndex, sal_Int32 vtableOffset, bool isArgFloat[4]) @@ -366,8 +366,13 @@ unsigned char * codeSnippet( // rsp+08 +----------------------------+ ------- // | return address | // rsp--> +----------------------------+ + // // - // + + // When it doubt about correctness, + // uncomment this and disassemble at runtime: + // INT 0x3 + //*p++ = 0xcc; if (isArgFloat[0]) { @@ -377,7 +382,7 @@ unsigned char * codeSnippet( else { // mov QWORD[rsp+8], rcx - *p++ = 0x48; *p++ = 0x49; *p++ = 0x4c; *p++ = 0x24; *p++ = 0x08; + *p++ = 0x48; *p++ = 0x89; *p++ = 0x4c; *p++ = 0x24; *p++ = 0x08; } if (isArgFloat[1]) @@ -388,7 +393,7 @@ unsigned char * codeSnippet( else { // mov QWORD[rsp+16], rdx - *p++ = 0x48; *p++ = 0x49; *p++ = 0x54; *p++ = 0x24; *p++ = 0x10; + *p++ = 0x48; *p++ = 0x89; *p++ = 0x54; *p++ = 0x24; *p++ = 0x10; } if (isArgFloat[2]) @@ -441,7 +446,7 @@ unsigned char * codeSnippet( *p++ = (((sal_uIntPtr)(&privateSnippetExecutor)) >> 56) & 0xff; // jmp r11 - *p++ = 0x49; + *p++ = 0x41; *p++ = 0xff; *p++ = 0xe3; diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/except.cxx b/main/bridges/source/cpp_uno/msvc_win64_x86-64/except.cxx index 55f2aa4779..98218480ab 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/except.cxx +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/except.cxx @@ -1,5 +1,5 @@ /************************************************************** - * + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -7,16 +7,16 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * + * *************************************************************/ @@ -47,6 +47,67 @@ using namespace ::std; using namespace ::osl; using namespace ::rtl; +//############################################################################## +// Windows's C++ exception handling is built on top of SEH (Structured Exception Handling). +// +// SEH documentation: +// https://web.archive.org/web/20060114142024/https://www.microsoft.com/msj/0197/exception/exception.aspx +// +// See this excellent article, +// "Decoding the parameters of a thrown C++ exception (0xE06D7363)" +// by Raymond Chen: +// https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 +// +// Then this one with more details on ExceptionInformation: +// http://workblog.pilin.name/2014/03/decoding-parameters-of-thrown-c.html +// +// Others: +// https://github.com/icestudent/ontl/blob/master/ntl/nt/exception.hxx +// https://www.openrce.org/articles/full_view/21 +// https://www.openrce.org/articles/full_view/23 +// +// For a C++ exception in SEH, on AMD64: +// +// +-----------------------------------+ +-------------------------------------------------------------+ +// |EXCEPTION_POINTERS | |EXCEPTION_RECORD | +// +-----------------------------------+ +-------------------------------------------------------------+ +// |PEXCEPTION_RECORD ExceptionRecord;|----------->|DWORD ExceptionCode; // == 0xE06D7363 for C++ exception | +// |PCONTEXT ContextRecord; | |DWORD ExceptionFlags; | +// +-----------------------------------+ |struct _EXCEPTION_RECORD *ExceptionRecord; | +// |PVOID ExceptionAddress; | +// |DWORD NumberParameters; // == 4 on AMD64, 3 on i386 | +// |ULONG_PTR ExceptionInformation[0]; | +// C++ object being thrown <----------|ULONG_PTR ExceptionInformation[1]; | +// +----------------------------------------------|ULONG_PTR ExceptionInformation[2]; | +// | HINSTANCE of the EXE/DLL where <----------|ULONG_PTR ExceptionInformation[3]; | +// | where the exception was thrown |ULONG_PTR ExceptionInformation[...]; | +// | |ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];| +// | offset from HINSTANCE +-------------------------------------------------------------+ +// | to: +// | +------------------------------+ +// | |ThrowInfo | +// | +------------------------------+ +// +---->|unsigned int attributes; | +----------------------------------+ +// |PMFN pmfnUnwind; | |CatchableTypeArray | +// |__int32 pForwardCompat; | offset from HINSTANCE to: +----------------------------------+ +// |__int32 pCatchableTypeArray;|---------------------------->|int nCatchableTypes; | +// +------------------------------+ +--------|__int32 arrayOfCatchableTypes[];| +// | +----------------------------------+ +// +------------------------+ | +// |CatchableType | | +// +------------------------+ offsets from HINSTANCE to | +// |unsigned int properties;|<--------------------------+ +--------------------------------+ +// |__int32 pType; |---------+ |TypeDescriptor | +// |PMD thisDisplacement;| | offset from HINSTANCE to +--------------------------------+ +// |int sizeOrOffset; | +------------------------->|const void * _EH_PTR64 pVFTable;| +// |PMFN copyFunction; | |void * _EH_PTR64 spare; | +// +------------------------+ |char name[]; | +// +--------------------------------+ +// +// +// +// + namespace CPPU_CURRENT_NAMESPACE { @@ -117,9 +178,8 @@ class __type_info public: virtual ~__type_info() throw (); - inline __type_info( void * m_vtable, const char * m_d_name ) throw () - : _m_vtable( m_vtable ) - , _m_name( NULL ) + inline __type_info( void * m_data, const char * m_d_name ) throw () + : _m_data( m_data ) { ::strcpy( _m_d_name, m_d_name ); } // #100211# - checked size_t length() const @@ -128,8 +188,7 @@ public: } private: - void * _m_vtable; - char * _m_name; // cached copy of unmangled name, NULL initially + void * _m_data; char _m_d_name[1]; // mangled name }; //__________________________________________________________________________________________________ @@ -340,7 +399,7 @@ RaiseInfo::RaiseInfo( typelib_TypeDescription * pTypeDescr ) throw () , _n2( 0 ) { // a must be - OSL_ENSURE( sizeof(sal_Int32) == sizeof(ExceptionType *), "### pointer size differs from sal_Int32!" ); + OSL_ENSURE( sizeof(sal_Int64) == sizeof(ExceptionType *), "### pointer size differs from sal_Int64!" ); ::typelib_typedescription_acquire( pTypeDescr ); this->pTypeDescr = pTypeDescr; @@ -427,7 +486,7 @@ ExceptionInfos::~ExceptionInfos() throw () #if OSL_DEBUG_LEVEL > 1 OSL_TRACE( "> freeing exception infos... <\n" ); #endif - + MutexGuard aGuard( _aMutex ); for ( t_string2PtrMap::const_iterator iPos( _allRaiseInfos.begin() ); iPos != _allRaiseInfos.end(); ++iPos ) @@ -522,8 +581,8 @@ void mscx_raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ) // a must be OSL_ENSURE( - sizeof(sal_Int32) == sizeof(void *), - "### pointer size differs from sal_Int32!" ); + sizeof(sal_Int64) == sizeof(void *), + "### pointer size differs from sal_Int64!" ); RaiseInfo *raiseInfo = ExceptionInfos::getRaiseInfo( pTypeDescr ); ULONG_PTR arFilterArgs[4]; arFilterArgs[0] = MSVC_magic_number; @@ -549,9 +608,9 @@ int mscx_filterCppException( // handle only C++ exceptions: if (pRecord == 0 || pRecord->ExceptionCode != MSVC_ExceptionCode) return EXCEPTION_CONTINUE_SEARCH; - + #if _MSC_VER < 1300 // MSVC -6 - bool rethrow = (pRecord->NumberParameters < 3 || + bool rethrow = (pRecord->NumberParameters < 4 || pRecord->ExceptionInformation[ 2 ] == 0); #else bool rethrow = __CxxDetectRethrow( &pRecord ); @@ -574,7 +633,7 @@ int mscx_filterCppException( // rethrow: handle only C++ exceptions: if (pRecord == 0 || pRecord->ExceptionCode != MSVC_ExceptionCode) return EXCEPTION_CONTINUE_SEARCH; - + if (pRecord->NumberParameters == 4 && // pRecord->ExceptionInformation[ 0 ] == MSVC_magic_number && pRecord->ExceptionInformation[ 1 ] != 0 && @@ -596,7 +655,7 @@ int mscx_filterCppException( baseAddress + pType->_pTypeInfo )->_m_d_name, RTL_TEXTENCODING_ASCII_US ) ); OUString aUNOname( toUNOname( aRTTIname ) ); - + typelib_TypeDescription * pExcTypeDescr = 0; typelib_typedescription_getByName( &pExcTypeDescr, aUNOname.pData ); @@ -640,7 +699,7 @@ int mscx_filterCppException( #endif typelib_typedescription_release( pExcTypeDescr ); } - + return EXCEPTION_EXECUTE_HANDLER; } } @@ -660,3 +719,4 @@ int mscx_filterCppException( } #pragma pack(pop) +
