This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch AOO42X in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit a230972184deb427a995e0eb922a5743673d1d16 Author: Jim Jagielski <[email protected]> AuthorDate: Fri Aug 7 06:07:30 2026 -0400 Step One of AOO42X macOS build backports. Backport the macOS/arm64 C++-UNO bridge hardening from trunk Cherry-picks d9eb145fce, 79670aa18e, 245a583a7b, adce10bba5 and 9ff3e22531; AOO42X had only the original arm64 port. Covers JIT-protected vtable writes, HFA argument and return marshalling, and synthesising exception RTTI, which clang emits hidden for keyless classes on arm64 Darwin. Apply in that order: d9eb145fce defines JitWriteGuard, which the later commits use. --- .../source/cpp_uno/s5abi_macosx_aarch64/abi.cxx | 115 ++++++- .../source/cpp_uno/s5abi_macosx_aarch64/abi.hxx | 14 + .../source/cpp_uno/s5abi_macosx_aarch64/call.s | 84 ++++- .../cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx | 46 ++- .../source/cpp_uno/s5abi_macosx_aarch64/except.cxx | 370 ++++++++++++--------- .../source/cpp_uno/s5abi_macosx_aarch64/share.hxx | 92 ++--- .../cpp_uno/s5abi_macosx_aarch64/uno2cpp.cxx | 177 +++++++--- .../source/cpp_uno/shared/vtablefactory.cxx | 41 +++ main/solenv/bin/addsym-macosx.sh | 11 +- main/solenv/src/component.map | 19 ++ .../com/sun/star/comp/bridge/TestComponent.java | 43 +++ main/testtools/source/bridgetest/bridgetest.cxx | 54 +++ .../source/bridgetest/cli/cli_cs_testobj.cs | 43 +++ main/testtools/source/bridgetest/cppobj.cxx | 25 ++ .../testtools/source/bridgetest/idl/bridgetest.idl | 48 +++ 15 files changed, 881 insertions(+), 301 deletions(-) diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx index d51c5ccf12..486e5fd466 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx @@ -46,7 +46,10 @@ #include "abi.hxx" +#include "bridges/cpp_uno/shared/types.hxx" + #include <rtl/ustring.hxx> +#include <string.h> using namespace aarch64; @@ -71,6 +74,27 @@ HfaKind mergeHfa( HfaKind running, HfaKind seen ) return ( running == seen ) ? running : HFA_NONE; } +bool isComplexAggregate( typelib_TypeDescriptionReference *pTypeRef ) +{ + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + const typelib_CompoundTypeDescription *pComp = + reinterpret_cast<const typelib_CompoundTypeDescription *>( pTypeDescr ); + bool complex = pComp->pBaseTypeDescription != 0 && + isComplexAggregate( pComp->pBaseTypeDescription->aBase.pWeakRef ); + for ( sal_Int32 i = 0; !complex && i < pComp->nMembers; ++i ) + { + typelib_TypeClass typeClass = pComp->ppTypeRefs[i]->eTypeClass; + if ( typeClass == typelib_TypeClass_STRUCT || + typeClass == typelib_TypeClass_EXCEPTION ) + complex = isComplexAggregate( pComp->ppTypeRefs[i] ); + else + complex = !bridges::cpp_uno::shared::isSimpleType( typeClass ); + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return complex; +} + // Recursively determine whether pTypeRef is (part of) a homogeneous // floating-point aggregate, accumulating the element kind and member count. // @@ -100,6 +124,9 @@ bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int const typelib_CompoundTypeDescription *pComp = reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr ); + // rCount is cumulative over the whole recursion, so remember where + // this aggregate started in order to size-check it below. + const int nCountAtEntry = rCount; bool bOk = true; // Flatten base class first (its members precede ours in layout). @@ -112,6 +139,17 @@ bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i ) bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount ); + if ( bOk ) + { + // Reject anything the elements do not tile exactly: only the + // elements contributed by THIS aggregate count towards its size. + sal_Int32 elementSize = rKind == HFA_FLOAT ? 4 : 8; + bOk = pTypeDescr->nSize == + ( rCount - nCountAtEntry ) * elementSize; + for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i ) + bOk = pComp->pMemberOffsets[i] % elementSize == 0; + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); return bOk; } @@ -158,7 +196,7 @@ bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGP } // anonymous namespace -bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool /*bInReturn*/, int &nUsedGPR, int &nUsedFPR ) +bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR ) { nUsedGPR = 0; nUsedFPR = 0; @@ -199,7 +237,10 @@ bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool case typelib_TypeClass_STRUCT: case typelib_TypeClass_EXCEPTION: - return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR ); + if ( bInReturn ) + return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR ); + nUsedGPR = 1; // generated UNO C++ bindings pass aggregates by const reference + return true; default: #if OSL_DEBUG_LEVEL > 1 @@ -212,12 +253,49 @@ bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bool aarch64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) { + switch ( pTypeRef->eTypeClass ) + { + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + case typelib_TypeClass_TYPEDEF: + case typelib_TypeClass_UNION: + case typelib_TypeClass_ARRAY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + // These are C++ wrapper objects, not pointer-sized scalar values. + // Apple's arm64 C++ ABI returns them through the buffer in x8. + return true; + default: + break; + } + + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || + pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + if ( isComplexAggregate( pTypeRef ) ) + return true; + } + int g, s; // Returned in registers iff examine_argument() says it fits; otherwise the // caller must pass an indirect-result buffer in x8. return !examine_argument( pTypeRef, true, g, s ); } +sal_uInt32 aarch64::get_return_kind( typelib_TypeDescriptionReference *pTypeRef ) +{ + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || + pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + HfaKind kind = HFA_NONE; + int count = 0; + if ( collectHfa( pTypeRef, kind, count ) && count >= 1 && count <= 4 ) + return kind == HFA_FLOAT ? RETURN_KIND_HFA_FLOAT : RETURN_KIND_HFA_DOUBLE; + } + return pTypeRef->eTypeClass; +} + void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pFPR, void *pStruct ) { int nUsedGPR = 0; @@ -242,7 +320,7 @@ void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal { float *pDest = reinterpret_cast<float *>( pStruct ); for ( int i = 0; i < nUsedFPR; ++i ) - pDest[i] = static_cast<float>( pFPR[i] ); + pDest[i] = *reinterpret_cast<const float *>( pFPR + i ); } else // HFA_DOUBLE { @@ -253,9 +331,32 @@ void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal } else { - // Non-HFA aggregate <= 16 bytes: raw copy of the 1-2 GPRs. - sal_uInt64 *pDest = reinterpret_cast<sal_uInt64 *>( pStruct ); - for ( int i = 0; i < nUsedGPR; ++i ) - pDest[i] = pGPR[i]; + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + // The registers contain up to 16 bytes, but the destination has the + // aggregate's exact size and need not be 64-bit aligned. + memcpy( pStruct, pGPR, pTypeDescr->nSize ); + TYPELIB_DANGER_RELEASE( pTypeDescr ); } } + +sal_uInt32 aarch64::align_stack_offset( + sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef ) +{ + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + sal_uInt32 alignment = pTypeDescr->nAlignment; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + if ( alignment == 0 ) + alignment = 1; + return (offset + alignment - 1) & ~(alignment - 1); +} + +sal_uInt32 aarch64::stack_size( typelib_TypeDescriptionReference *pTypeRef ) +{ + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + sal_uInt32 size = pTypeDescr->nSize; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return size; +} diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.hxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.hxx index 7d55ac076c..8bdd0d9a9d 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.hxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.hxx @@ -48,6 +48,12 @@ const sal_uInt32 MAX_FPR_REGS = 8; most 2 (16 bytes / 8). */ const sal_uInt32 MAX_AGGREGATE_REGS = 4; +enum ReturnKind +{ + RETURN_KIND_HFA_FLOAT = 0x100, + RETURN_KIND_HFA_DOUBLE = 0x101 +}; + /* Count the number of registers required to pass the given type. Examines the argument and sets the number of GPR (x) and FPR (v) registers @@ -72,6 +78,9 @@ bool examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInRetur */ bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ); +/** Return the assembly return kind for an HFA, or the type class otherwise. */ +sal_uInt32 get_return_kind( typelib_TypeDescriptionReference *pTypeRef ); + /** Scatter a register-resident return value (an HFA returned in v0..v3, or a non-HFA aggregate <= 16 bytes returned in x0,x1) into the caller's struct. @@ -81,6 +90,11 @@ bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ); */ void fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64* pGPR, const double* pFPR, void *pStruct ); +sal_uInt32 align_stack_offset( + sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef ); + +sal_uInt32 stack_size( typelib_TypeDescriptionReference *pTypeRef ); + } // namespace aarch64 #endif // _BRIDGES_CPP_UNO_AARCH64_ABI_HXX_ diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/call.s b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/call.s index 035b38555a..d0f6388ea7 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/call.s +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/call.s @@ -32,8 +32,8 @@ // sal_uInt64 pIndirectRet, // x1: value for x8 (indirect result ptr), 0 if none // sal_uInt64 *pGPR, // x2: 8 words -> x0..x7 // double *pFPR, // x3: 8 doubles -> d0..d7 -// sal_uInt64 *pStack, // x4: overflow-arg words -// sal_uInt32 nStackWords, // x5: number of 8-byte overflow words +// unsigned char *pStack, // x4: packed overflow-argument bytes +// sal_uInt32 nStackBytes, // x5: byte count // sal_uInt64 *pGPRReturn, // x6: [out] x0,x1 // double *pFPRReturn); // x7: [out] d0..d3 (HFA up to 4 elements) @@ -41,12 +41,22 @@ .globl _callVirtualFunction .p2align 2 _callVirtualFunction: + .cfi_startproc // prologue: save fp/lr and the callee-saved registers we use stp x29, x30, [sp, #-16]! stp x19, x20, [sp, #-16]! stp x21, x22, [sp, #-16]! stp x23, x24, [sp, #-16]! mov x29, sp + .cfi_def_cfa x29, 64 + .cfi_offset x29, -16 + .cfi_offset x30, -8 + .cfi_offset x19, -32 + .cfi_offset x20, -24 + .cfi_offset x21, -48 + .cfi_offset x22, -40 + .cfi_offset x23, -64 + .cfi_offset x24, -56 // stash inputs that must survive the call into callee-saved registers mov x19, x0 // pFunction @@ -57,17 +67,16 @@ _callVirtualFunction: mov x24, x1 // x8 indirect-result value // allocate and copy the outgoing overflow stack arguments. - // bytes = ((nStackWords + 1) & ~1) * 8, to keep sp 16-byte aligned. - add x9, x5, #1 - bic x9, x9, #1 - lsl x9, x9, #3 + // Round the packed byte area up to preserve 16-byte SP alignment. + add x9, x5, #15 + bic x9, x9, #15 sub sp, sp, x9 mov x10, #0 Lcvf_copy: cmp x10, x5 b.ge Lcvf_copied - ldr x11, [x4, x10, lsl #3] - str x11, [sp, x10, lsl #3] + ldrb w11, [x4, x10] + strb w11, [sp, x10] add x10, x10, #1 b Lcvf_copy Lcvf_copied: @@ -103,6 +112,7 @@ Lcvf_copied: ldp x19, x20, [sp], #16 ldp x29, x30, [sp], #16 ret + .cfi_endproc // --------------------------------------------------------------------------- // privateSnippetExecutor: the incoming (cpp2uno) register-spill executor. @@ -124,13 +134,17 @@ Lcvf_copied: // void* pIndirectReturn, sal_uInt64* pRegisterReturn); // // Frame (176 bytes): [0]=x29,x30 [16..79]=x0..x7 [80..143]=d0..d7 -// [144..159]=return buffer. +// [144..175]=return buffer (up to four HFA doubles). .globl _privateSnippetExecutor .p2align 2 _privateSnippetExecutor: + .cfi_startproc mov x17, sp // x17 = ovrflw (incoming stack args) stp x29, x30, [sp, #-176]! mov x29, sp + .cfi_def_cfa x29, 176 + .cfi_offset x29, -176 + .cfi_offset x30, -168 stp x0, x1, [sp, #16] // save GP argument registers x0..x7 stp x2, x3, [sp, #32] @@ -148,21 +162,65 @@ _privateSnippetExecutor: add x3, sp, #80 // fpreg mov x4, x17 // ovrflw mov x5, x8 // pIndirectReturn (x8 indirect-result reg) - add x6, sp, #144 // pRegisterReturn (16-byte buffer) + add x6, sp, #144 // pRegisterReturn (32-byte buffer) bl _cpp_vtable_call + cmp w0, #0x100 // RETURN_KIND_HFA_FLOAT + b.eq Lpse_hfa_float + cmp w0, #0x101 // RETURN_KIND_HFA_DOUBLE + b.eq Lpse_hfa_double cmp w0, #10 // typelib_TypeClass_FLOAT b.eq Lpse_float cmp w0, #11 // typelib_TypeClass_DOUBLE b.eq Lpse_float - // integer / pointer / <=16B aggregate: load both banks; caller reads the - // ones that matter for its return type. + cmp w0, #3 // typelib_TypeClass_BYTE + b.eq Lpse_signed_byte + cmp w0, #4 // typelib_TypeClass_SHORT + b.eq Lpse_signed_short + cmp w0, #1 // typelib_TypeClass_CHAR + b.eq Lpse_unsigned_short + cmp w0, #5 // typelib_TypeClass_UNSIGNED_SHORT + b.eq Lpse_unsigned_short + cmp w0, #2 // typelib_TypeClass_BOOLEAN + b.eq Lpse_unsigned_byte + cmp w0, #6 // typelib_TypeClass_LONG + b.eq Lpse_word + cmp w0, #7 // typelib_TypeClass_UNSIGNED_LONG + b.eq Lpse_word + cmp w0, #15 // typelib_TypeClass_ENUM + b.eq Lpse_word + // Integer / pointer / <=16B aggregate. ldp x0, x1, [sp, #144] - ldp d0, d1, [sp, #144] b Lpse_done Lpse_float: ldr d0, [sp, #144] + b Lpse_done +Lpse_hfa_float: + ldr s0, [sp, #144] + ldr s1, [sp, #148] + ldr s2, [sp, #152] + ldr s3, [sp, #156] + b Lpse_done +Lpse_hfa_double: + ldp d0, d1, [sp, #144] + ldp d2, d3, [sp, #160] + b Lpse_done +Lpse_signed_byte: + ldrsb w0, [sp, #144] + b Lpse_done +Lpse_unsigned_byte: + ldrb w0, [sp, #144] + b Lpse_done +Lpse_signed_short: + ldrsh w0, [sp, #144] + b Lpse_done +Lpse_unsigned_short: + ldrh w0, [sp, #144] + b Lpse_done +Lpse_word: + ldr w0, [sp, #144] Lpse_done: mov sp, x29 ldp x29, x30, [sp], #176 ret + .cfi_endproc diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx index 6fa6bac7a2..c6bb914985 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx @@ -27,6 +27,7 @@ #include <stdio.h> #include <stdlib.h> #include <hash_map> +#include <libkern/OSCacheControl.h> #include <rtl/alloc.h> #include <osl/mutex.hxx> @@ -68,12 +69,13 @@ static typelib_TypeClass cpp2uno_call( const typelib_TypeDescription * pMemberTypeDescr, typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return sal_Int32 nParams, typelib_MethodParameter * pParams, - void ** gpreg, void ** fpreg, void ** ovrflw, + void ** gpreg, void ** fpreg, unsigned char * ovrflw, void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none) sal_uInt64 * pRegisterReturn /* space for register return */ ) { unsigned int nr_gpr = 0; //number of gpr registers used unsigned int nr_fpr = 0; //number of fpr registers used + sal_uInt32 stackOffset = 0; // return typelib_TypeDescription * pReturnTypeDescr = 0; @@ -122,11 +124,11 @@ static typelib_TypeClass cpp2uno_call( int nUsedGPR = 0; int nUsedFPR = 0; - bool bFitsRegisters = aarch64::examine_argument( rParam.pTypeRef, false, nUsedGPR, nUsedFPR ); + aarch64::examine_argument( rParam.pTypeRef, false, nUsedGPR, nUsedFPR ); if ( !rParam.bOut && bridges::cpp_uno::shared::isSimpleType( rParam.pTypeRef ) ) // value { // A simple UNO type occupies exactly one register, GPR or FPR. - OSL_ASSERT( bFitsRegisters && ( ( nUsedFPR == 1 && nUsedGPR == 0 ) || ( nUsedFPR == 0 && nUsedGPR == 1 ) ) ); + OSL_ASSERT( ( nUsedFPR == 1 && nUsedGPR == 0 ) || ( nUsedFPR == 0 && nUsedGPR == 1 ) ); if ( nUsedFPR == 1 ) { @@ -136,7 +138,12 @@ static typelib_TypeClass cpp2uno_call( nr_fpr++; } else - pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++; + { + stackOffset = aarch64::align_stack_offset( + stackOffset, rParam.pTypeRef ); + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw + stackOffset; + stackOffset += aarch64::stack_size( rParam.pTypeRef ); + } } else if ( nUsedGPR == 1 ) { @@ -146,7 +153,12 @@ static typelib_TypeClass cpp2uno_call( nr_gpr++; } else - pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++; + { + stackOffset = aarch64::align_stack_offset( + stackOffset, rParam.pTypeRef ); + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw + stackOffset; + stackOffset += aarch64::stack_size( rParam.pTypeRef ); + } } } else // struct <= 16 bytes || ptr to complex value || ref @@ -154,14 +166,20 @@ static typelib_TypeClass cpp2uno_call( typelib_TypeDescription * pParamTypeDescr = 0; TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); - void *pCppStack; + void *pCppStack = 0; if ( nr_gpr < aarch64::MAX_GPR_REGS ) { pCppArgs[nPos] = pCppStack = *gpreg++; nr_gpr++; } else - pCppArgs[nPos] = pCppStack = *ovrflw++; + { + stackOffset = (stackOffset + sizeof(void *) - 1) & + ~(sizeof(void *) - 1); + pCppArgs[nPos] = pCppStack = + *reinterpret_cast<void **>( ovrflw + stackOffset ); + stackOffset += sizeof(void *); + } if (! rParam.bIn) // is pure out { @@ -261,9 +279,9 @@ static typelib_TypeClass cpp2uno_call( //================================================================================================== -extern "C" typelib_TypeClass cpp_vtable_call( +extern "C" sal_uInt32 cpp_vtable_call( sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, - void ** gpreg, void ** fpreg, void ** ovrflw, + void ** gpreg, void ** fpreg, unsigned char * ovrflw, void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none) sal_uInt64 * pRegisterReturn /* space for register return */ ) { @@ -299,7 +317,7 @@ extern "C" typelib_TypeClass cpp_vtable_call( TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] ); - typelib_TypeClass eRet; + sal_uInt32 eRet; switch ( aMemberDescr.get()->eTypeClass ) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: @@ -310,9 +328,10 @@ extern "C" typelib_TypeClass cpp_vtable_call( if ( pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex ) { // is GET method - eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef, + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef, 0, 0, // no params gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn ); + eRet = aarch64::get_return_kind( pAttrTypeRef ); } else { @@ -384,6 +403,7 @@ extern "C" typelib_TypeClass cpp_vtable_call( pMethodTD->nParams, pMethodTD->pParams, gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn ); + eRet = aarch64::get_return_kind( pMethodTD->pReturnTypeRef ); } } break; @@ -538,6 +558,8 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( //================================================================================================== void bridges::cpp_uno::shared::VtableFactory::flushCode( - unsigned char const *, unsigned char const * ) + unsigned char const * begin, unsigned char const * end ) { + sys_icache_invalidate( + const_cast<unsigned char *>(begin), end - begin ); } diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx index 60784a4172..238cdb2f68 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx @@ -29,6 +29,7 @@ #endif #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <dlfcn.h> #include <cxxabi.h> @@ -58,6 +59,67 @@ using namespace ::__cxxabiv1; namespace CPPU_CURRENT_NAMESPACE { +namespace { + +typedef hash_map< void *, typelib_TypeDescription * > ThrownTypes; +typedef hash_map< OUString, type_info *, OUStringHash > ObservedRttiMap; + +ThrownTypes & thrownTypes() +{ + static ThrownTypes types; + return types; +} + +ObservedRttiMap & observedRttis() +{ + static ObservedRttiMap map; + return map; +} + +// Guards BOTH thrownTypes() and observedRttis(). Every access to either map +// must hold this one mutex; they are plain hash_maps, so an insertion racing a +// find or erase is undefined behaviour. RTTI::m_mutex may be held while +// acquiring this one (see RTTI::getRTTI), never the other way round. +Mutex & exceptionMapsMutex() +{ + static Mutex mutex; + return mutex; +} + +// libc++ marks a type_info whose object is not unique across images by setting +// the top bit of type_info::__type_name; comparison then falls back to strcmp +// of the mangled name (see __non_unique_arm_rtti_bit_impl in <typeinfo>). On +// arm64 Darwin clang emits the typeinfo of every keyless class -- which is every +// UNO exception -- hidden and therefore non-unique, so a synthesised object must +// set the bit too, or std::type_info::operator== degenerates to an address +// comparison and never matches the handler's real typeinfo. +sal_uIntPtr const NON_UNIQUE_RTTI_BIT = + static_cast< sal_uIntPtr >(1) << (8 * sizeof (sal_uIntPtr) - 1); + +RttiSiClassLayout const * siDonor() +{ + return reinterpret_cast< RttiSiClassLayout const * >( &typeid(RttiDonorDerived) ); +} +RttiClassLayout const * classDonor() +{ + return reinterpret_cast< RttiClassLayout const * >( &typeid(RttiDonorBase) ); +} + +// Refuse to synthesise unless the donors really have the layout we assume. +bool rttiDonorsUsable() +{ + return sizeof (void *) == 8 + && siDonor()->pBase == static_cast< void const * >( classDonor() ); +} + +// Mirror the platform's own convention rather than assuming it. +bool rttiIsNonUnique() +{ + return (siDonor()->nName & NON_UNIQUE_RTTI_BIT) != 0; +} + +} + void dummy_can_throw_anything( char const * ) { } @@ -100,6 +162,24 @@ static OUString toUNOname( char const * p ) SAL_THROW( () ) #endif } +//================================================================================================== +static OString mangledRttiSymbol( OUString const & unoName ) SAL_THROW( () ) +{ + OStringBuffer buf( 64 ); + buf.append( RTL_CONSTASCII_STRINGPARAM("_ZTIN") ); + sal_Int32 index = 0; + do + { + OUString token( unoName.getToken( 0, '.', index ) ); + buf.append( token.getLength() ); + OString c_token( OUStringToOString( token, RTL_TEXTENCODING_ASCII_US ) ); + buf.append( c_token ); + } + while (index >= 0); + buf.append( 'E' ); + return buf.makeStringAndClear(); +} + //================================================================================================== class RTTI { @@ -109,7 +189,9 @@ class RTTI t_rtti_map m_rttis; t_rtti_map m_generatedRttis; - void * m_hApp; + type_info * synthesiseRTTI( + OString const & rSymbolName, + typelib_CompoundTypeDescription * pTypeDescr ) SAL_THROW( () ); public: RTTI() SAL_THROW( () ); @@ -120,121 +202,123 @@ public: //__________________________________________________________________________________________________ RTTI::RTTI() SAL_THROW( () ) - : m_hApp( dlopen( 0, RTLD_LAZY ) ) { } //__________________________________________________________________________________________________ RTTI::~RTTI() SAL_THROW( () ) { - dlclose( m_hApp ); } //__________________________________________________________________________________________________ type_info * RTTI::getRTTI( typelib_CompoundTypeDescription *pTypeDescr ) SAL_THROW( () ) { - type_info * rtti; - OUString const & unoName = *(OUString const *)&pTypeDescr->aBase.pTypeName; + // Recursive: synthesiseRTTI() re-enters getRTTI() for the base chain. + // osl::Mutex is a PTHREAD_MUTEX_RECURSIVE (sal/osl/unx/mutex.c), so this + // is safe. Lock order against exceptionMapsMutex() is unchanged. MutexGuard guard( m_mutex ); + + { + MutexGuard observedGuard( exceptionMapsMutex() ); + ObservedRttiMap::const_iterator observed( observedRttis().find( unoName ) ); + if ( observed != observedRttis().end() ) + return observed->second; // a real typeinfo always wins + } + t_rtti_map::const_iterator iFind( m_rttis.find( unoName ) ); - if (iFind == m_rttis.end()) + if (iFind != m_rttis.end()) + return iFind->second; + + OString symName( mangledRttiSymbol( unoName ) ); + type_info * rtti = static_cast<std::type_info *>(dlsym( RTLD_DEFAULT, symName.getStr() )); + if (rtti != 0) { - // RTTI symbol - OStringBuffer buf( 64 ); - buf.append( RTL_CONSTASCII_STRINGPARAM("_ZTIN") ); - sal_Int32 index = 0; - do - { - OUString token( unoName.getToken( 0, '.', index ) ); - buf.append( token.getLength() ); - OString c_token( OUStringToOString( token, RTL_TEXTENCODING_ASCII_US ) ); - buf.append( c_token ); - } - while (index >= 0); - buf.append( 'E' ); + m_rttis.insert( t_rtti_map::value_type( unoName, rtti ) ); + return rtti; + } - OString symName( buf.makeStringAndClear() ); - rtti = static_cast<std::type_info *>(dlsym( m_hApp, symName.getStr() )); + t_rtti_map::const_iterator iGen( m_generatedRttis.find( unoName ) ); + if (iGen != m_generatedRttis.end()) + return iGen->second; + + // On arm64 Darwin, clang emits the typeinfo of every keyless class (which + // is every UNO exception) hidden, so the dlsym() lookup above can never + // succeed here -- see solenv/src/component.map for the full explanation. + // Synthesise one instead of degrading straight to a RuntimeException. + rtti = synthesiseRTTI( symName, pTypeDescr ); + if (rtti != 0) + m_generatedRttis.insert( t_rtti_map::value_type( unoName, rtti ) ); + return rtti; +} - if (rtti) - { - pair< t_rtti_map::iterator, bool > insertion( - m_rttis.insert( t_rtti_map::value_type( unoName, rtti ) ) ); - OSL_ENSURE( insertion.second, "### inserting new rtti failed?!" ); - } - else - { - // try to lookup the symbol in the generated rtti map - t_rtti_map::const_iterator iFind2( m_generatedRttis.find( unoName ) ); - if (iFind2 == m_generatedRttis.end()) - { - // we must generate it ! - // symbol and rtti-name is nearly identical, - // the symbol is prefixed with _ZTI - char const * rttiName = symName.getStr() +4; -#if OSL_DEBUG_LEVEL > 1 - fprintf( stderr,"generated rtti for %s\n", rttiName ); - const OString aCUnoName = OUStringToOString( unoName, RTL_TEXTENCODING_UTF8); - OSL_TRACE( "TypeInfo for \"%s\" not found and cannot be generated.\n", aCUnoName.getStr()); -#endif -#ifndef AOO_BYPASS_RTTI - if (pTypeDescr->pBaseTypeDescription) - { - // ensure availability of base - type_info * base_rtti = getRTTI( - (typelib_CompoundTypeDescription *)pTypeDescr->pBaseTypeDescription ); - rtti = new __si_class_type_info( - strdup( rttiName ), (__class_type_info *)base_rtti ); - } - else - { - // this class has no base class - rtti = new __class_type_info( strdup( rttiName ) ); - } -#else - rtti = NULL; -#endif - bool bOK = m_generatedRttis.insert( t_rtti_map::value_type( unoName, rtti )).second; - OSL_ENSURE( bOK, "### inserting new generated rtti failed?!" ); - } - else // taking already generated rtti - { - rtti = iFind2->second; - } - } +//__________________________________________________________________________________________________ +type_info * RTTI::synthesiseRTTI( + OString const & rSymbolName, + typelib_CompoundTypeDescription * pTypeDescr ) SAL_THROW( () ) +{ + if (! rttiDonorsUsable()) + return 0; // keep the loud RuntimeException fallback + + type_info * pBaseRtti = 0; + if (pTypeDescr->pBaseTypeDescription != 0) + { + // The whole chain must resolve: libc++abi walks __base_type when matching + // a handler for a base class and would dereference a null link. + pBaseRtti = getRTTI( + (typelib_CompoundTypeDescription *) pTypeDescr->pBaseTypeDescription ); + if (pBaseRtti == 0) + return 0; } - else + + // The mangled type name is the symbol name without its "_ZTI" prefix. + char * pName = strdup( rSymbolName.getStr() + 4 ); + if (pName == 0) + return 0; + sal_uIntPtr nName = reinterpret_cast< sal_uIntPtr >( pName ); + if (rttiIsNonUnique()) + nName |= NON_UNIQUE_RTTI_BIT; + + // Deliberately never freed; these live for the life of the process + // (the module already builds with -DLEAK_STATIC_DATA). + if (pBaseRtti != 0) { - rtti = iFind->second; + RttiSiClassLayout * p = static_cast< RttiSiClassLayout * >( + calloc( 1, sizeof (RttiSiClassLayout) ) ); + if (p == 0) { free( pName ); return 0; } + p->pVtable = siDonor()->pVtable; + p->nName = nName; + p->pBase = pBaseRtti; + return reinterpret_cast< type_info * >( p ); } - return rtti; + RttiClassLayout * p = static_cast< RttiClassLayout * >( + calloc( 1, sizeof (RttiClassLayout) ) ); + if (p == 0) { free( pName ); return 0; } + p->pVtable = classDonor()->pVtable; + p->nName = nName; + return reinterpret_cast< type_info * >( p ); } //-------------------------------------------------------------------------------------------------- static void deleteException( void * pExc ) { - __cxa_exception const * header = static_cast<__cxa_exception const *>(pExc) - 1; - /* More __cxa_exception mumbo-jumbo. See share.hxx and fillUnoException() below */ - if (header->exceptionDestructor != &deleteException) + typelib_TypeDescription * pTD = 0; { - header = reinterpret_cast<__cxa_exception const *>(reinterpret_cast<char const *>(header) - 8); + MutexGuard guard( exceptionMapsMutex() ); + ThrownTypes::iterator i = thrownTypes().find( pExc ); + if ( i != thrownTypes().end() ) + { + pTD = i->second; + thrownTypes().erase( i ); + } } - if( !header->exceptionType) + if ( pTD ) { - return; // NOTE: leak for now + ::uno_destructData( pExc, pTD, cpp_release ); + ::typelib_typedescription_release( pTD ); } - typelib_TypeDescription * pTD = 0; - OUString unoName( toUNOname( header->exceptionType->name() ) ); - ::typelib_typedescription_getByName( &pTD, unoName.pData ); - OSL_ENSURE( pTD, "### unknown exception type! leaving out destruction => leaking!!!" ); - if (pTD) - { - ::uno_destructData( pExc, pTD, cpp_release ); - ::typelib_typedescription_release( pTD ); - } } //================================================================================================== @@ -249,6 +333,19 @@ void raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ) #endif void * pCppExc; type_info * rtti; + OUString typeName( + *reinterpret_cast< OUString const * >( &pUnoExc->pType->pTypeName ) ); + + // Every UNO exception derives from com.sun.star.uno.Exception, whose first + // member is the Message string. Keep a copy: if the throw below cannot be + // completed we substitute a RuntimeException, and without this the original + // diagnostic would be lost silently. + OUString message; + if ( pUnoExc->pData != 0 && + *reinterpret_cast< rtl_uString * const * >( pUnoExc->pData ) != 0 ) + { + message = *reinterpret_cast< OUString const * >( pUnoExc->pData ); + } { // construct cpp exception object @@ -257,100 +354,77 @@ void raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ) OSL_ASSERT( pTypeDescr ); if (! pTypeDescr) { + // NOTE: pUnoExc is deliberately left alone here. Destructing an any + // whose type description cannot be resolved is not safe, so this path + // leaks it rather than risking a null dereference. It only fires if + // the type system has already lost the type being thrown. throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get typedescription for type ") ) + - *reinterpret_cast< OUString const * >( &pUnoExc->pType->pTypeName ), + typeName + OUString( RTL_CONSTASCII_USTRINGPARAM(": ") ) + message, Reference< XInterface >() ); } pCppExc = __cxa_allocate_exception( pTypeDescr->nSize ); ::uno_copyAndConvertData( pCppExc, pUnoExc->pData, pTypeDescr, pUno2Cpp ); - // destruct uno exception - ::uno_any_destruct( pUnoExc, 0 ); - // avoiding locked counts - static RTTI * s_rtti = 0; - if (! s_rtti) - { - MutexGuard guard( Mutex::getGlobalMutex() ); - if (! s_rtti) - { -#ifdef LEAK_STATIC_DATA - s_rtti = new RTTI(); -#else - static RTTI rtti_data; - s_rtti = &rtti_data; -#endif - } - } - rtti = (type_info *)s_rtti->getRTTI( (typelib_CompoundTypeDescription *) pTypeDescr ); - TYPELIB_DANGER_RELEASE( pTypeDescr ); + static RTTI rtti_data; + rtti = rtti_data.getRTTI( + (typelib_CompoundTypeDescription *) pTypeDescr ); OSL_ENSURE( rtti, "### no rtti for throwing exception!" ); if (! rtti) { + // Undo everything done above: the payload was constructed into the + // __cxa buffer, so it has to be destructed before the buffer is + // released, and raiseException still owes its caller the destruction + // of the incoming any. + ::uno_destructData( pCppExc, pTypeDescr, cpp_release ); + __cxa_free_exception( pCppExc ); + TYPELIB_DANGER_RELEASE( pTypeDescr ); + ::uno_any_destruct( pUnoExc, 0 ); throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("no rtti for type ") ) + - *reinterpret_cast< OUString const * >( &pUnoExc->pType->pTypeName ), + typeName + OUString( RTL_CONSTASCII_USTRINGPARAM(": ") ) + message, Reference< XInterface >() ); } + + { + MutexGuard guard( exceptionMapsMutex() ); + typelib_typedescription_acquire( pTypeDescr ); + thrownTypes()[pCppExc] = pTypeDescr; + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); + + // The C++ payload and its retained type description now own the value. + ::uno_any_destruct( pUnoExc, 0 ); } __cxa_throw( pCppExc, rtti, deleteException ); } -//================================================================================================== -void fillUnoException( __cxa_exception * header, uno_Any * pUnoExc, uno_Mapping * pCpp2Uno ) +void fillUnoException( + std::type_info const & type, void * exception, uno_Any * pUnoExc, + uno_Mapping * pCpp2Uno ) { - if (! header) + typelib_TypeDescription * pExcTypeDescr = 0; + OUString unoName( toUNOname( type.name() ) ); { - RuntimeException aRE( - OUString( RTL_CONSTASCII_USTRINGPARAM("no exception header!") ), - Reference< XInterface >() ); - Type const & rType = ::getCppuType( &aRE ); - uno_type_any_constructAndConvert( pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno ); -#if OSL_DEBUG_LEVEL > 0 - OString cstr( OUStringToOString( aRE.Message, RTL_TEXTENCODING_ASCII_US ) ); - OSL_ENSURE( 0, cstr.getStr() ); -#endif - return; + MutexGuard guard( exceptionMapsMutex() ); + observedRttis()[unoName] = const_cast<std::type_info *>( &type ); } - - /* - * Handle the case where we are built on llvm 10 (or later) but are running - * on an earlier version (eg, community builds). In this situation the - * reserved ptr doesn't exist in the struct returned and so the offsets - * that header uses are wrong. This assumes that reserved isn't used - * and that referenceCount is always >0 in the cases we handle. - * See share.hxx for the definition of __cxa_exception - */ - if (*reinterpret_cast<void **>(header) == 0) - { - header = reinterpret_cast<__cxa_exception *>(reinterpret_cast<char *>(header) + 8); - } - - typelib_TypeDescription * pExcTypeDescr = 0; - OUString unoName( toUNOname( header->exceptionType->name() ) ); -#if OSL_DEBUG_LEVEL > 1 - OString cstr_unoName( OUStringToOString( unoName, RTL_TEXTENCODING_ASCII_US ) ); - fprintf( stderr, "> c++ exception occurred: %s\n", cstr_unoName.getStr() ); -#endif - typelib_typedescription_getByName( &pExcTypeDescr, unoName.pData ); - if (0 == pExcTypeDescr) + typelib_typedescription_getByName( &pExcTypeDescr, unoName.pData ); + if ( pExcTypeDescr == 0 ) { RuntimeException aRE( OUString( RTL_CONSTASCII_USTRINGPARAM("exception type not found: ") ) + unoName, Reference< XInterface >() ); Type const & rType = ::getCppuType( &aRE ); - uno_type_any_constructAndConvert( pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno ); -#if OSL_DEBUG_LEVEL > 0 - OString cstr( OUStringToOString( aRE.Message, RTL_TEXTENCODING_ASCII_US ) ); - OSL_ENSURE( 0, cstr.getStr() ); -#endif + uno_type_any_constructAndConvert( + pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno ); } else { - // construct uno exception any - uno_any_constructAndConvert( pUnoExc, header->adjustedPtr, pExcTypeDescr, pCpp2Uno ); + uno_any_constructAndConvert( + pUnoExc, exception, pExcTypeDescr, pCpp2Uno ); typelib_typedescription_release( pExcTypeDescr ); } } diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/share.hxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/share.hxx index e4fcf4dbf9..cfc71d2869 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/share.hxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/share.hxx @@ -32,85 +32,37 @@ namespace CPPU_CURRENT_NAMESPACE void dummy_can_throw_anything( char const * ); -typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); - -// ----- the following structure is compatible with the one declared in libunwind's unwind.h -// (use forced types) - -struct _Unwind_Exception -{ - uint64_t exception_class; - void * exception_cleanup; - uintptr_t private_1; - uintptr_t private_2; -}; - -struct __cxa_exception -{ - /* From LLVM 10 a reserved member was added at the top of the struct on - 64-bit targets. Who the hell does that? - https://reviews.llvm.org/rG674ec1eb16678b8addc02a4b0534ab383d22fa77 - It is required on arm64 (Apple Silicon): the trailing _Unwind_Exception - must be 16-byte aligned within the allocation, and only WITH this member - does unwindHeader land at a 16-byte boundary (offset 96, vs a misaligned - 88 without it). Verified empirically against this host's libc++abi. - NOTE: Apple clang version != upstream LLVM version. */ - void *reserved; - size_t referenceCount; - ::std::type_info *exceptionType; - void (*exceptionDestructor)(void *); - ::std::unexpected_handler unexpectedHandler; - ::std::terminate_handler terminateHandler; - __cxa_exception *nextException; - int handlerCount; - int handlerSwitchValue; - const unsigned char *actionRecord; - const unsigned char *languageSpecificData; - void *catchTemp; - void *adjustedPtr; - _Unwind_Exception unwindHeader; -}; +// Donor types for RTTI synthesis. Their type_info objects are emitted by the +// compiler, so they carry the real libc++abi vtables and the platform's own +// uniqueness convention. They must stay ordinary namespace-scope classes with +// no virtual functions and a single public non-virtual base -- exactly the +// shape of a generated UNO exception -- so that typeid(RttiDonorDerived) is a +// __si_class_type_info and typeid(RttiDonorBase) a __class_type_info. +// Do not move them into an anonymous namespace. +struct RttiDonorBase { sal_Int32 dummy; }; +struct RttiDonorDerived : public RttiDonorBase { sal_Int32 dummy2; }; + +// Itanium ABI object layouts (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti). +// libc++abi does not publish __cxxabiv1::__class_type_info, and declaring a +// look-alike class is not an option: it would get its own vtable, and +// __class_type_info::can_catch() dynamic_casts the thrown type to the real +// libc++abi class, so no typed handler would ever match. We therefore build +// raw storage in the ABI layout and install a borrowed, genuine vtable. +struct RttiClassLayout { void const * pVtable; sal_uIntPtr nName; }; +struct RttiSiClassLayout { void const * pVtable; sal_uIntPtr nName; void const * pBase; }; extern "C" void *__cxa_allocate_exception( std::size_t thrown_size ) throw(); +extern "C" void __cxa_free_exception( void *thrown_exception ) throw(); extern "C" void __cxa_throw ( void *thrown_exception, std::type_info *tinfo, void (*dest) (void *) ) __attribute__((noreturn)); - -struct __cxa_eh_globals -{ - __cxa_exception *caughtExceptions; - unsigned int uncaughtExceptions; -}; -extern "C" __cxa_eh_globals *__cxa_get_globals () throw(); - -// ----- - -// on OSX 64bit the class_type_info classes are specified -// in http://refspecs.linuxbase.org/cxxabi-1.86.html#rtti but -// these details are not generally available in a public header -// of most development environments. So we define them here. -// NOTE: https://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf -class __class_type_info : public std::type_info -{ -public: - explicit __class_type_info( const char* pRttiName) - : std::type_info( pRttiName) - {} -}; - -class __si_class_type_info : public __class_type_info -{ - const __class_type_info* mpBaseType; -public: - explicit __si_class_type_info( const char* pRttiName, __class_type_info* pBaseType) - : __class_type_info( pRttiName), mpBaseType( pBaseType) - {} -}; +extern "C" std::type_info *__cxa_current_exception_type(); //================================================================================================== void raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ); //================================================================================================== void fillUnoException( - __cxa_exception * header, uno_Any *, uno_Mapping * pCpp2Uno ); + std::type_info const & type, void * exception, uno_Any *, + uno_Mapping * pCpp2Uno ); } diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/uno2cpp.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/uno2cpp.cxx index adaa3f2a5f..ca5ec8adfd 100644 --- a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/uno2cpp.cxx +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/uno2cpp.cxx @@ -56,20 +56,20 @@ using namespace ::com::sun::star::uno; extern "C" void callVirtualFunction( sal_uInt64 pFunction, sal_uInt64 pIndirectRet, sal_uInt64 *pGPR, double *pFPR, - sal_uInt64 *pStack, sal_uInt32 nStackWords, + unsigned char *pStack, sal_uInt32 nStackBytes, sal_uInt64 *pGPRReturn, double *pFPRReturn ); static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, void * pRegisterReturn, typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, void * pIndirectReturn, - sal_uInt64 *pStack, sal_uInt32 nStack, + unsigned char *pStack, sal_uInt32 nStack, sal_uInt64 *pGPR, sal_uInt32 nGPR, double *pFPR, sal_uInt32 nFPR) __attribute__((noinline)); static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, void * pRegisterReturn, typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, void * pIndirectReturn, - sal_uInt64 *pStack, sal_uInt32 nStack, + unsigned char *pStack, sal_uInt32 nStack, sal_uInt64 *pGPR, sal_uInt32 nGPR, double *pFPR, sal_uInt32 nFPR) { @@ -82,9 +82,10 @@ static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, fprintf( stderr, "\nFPR's (%d): ", nFPR ); for ( unsigned int i = 0; i < nFPR; ++i ) fprintf( stderr, "%f, ", pFPR[i] ); - fprintf( stderr, "\nStack (%d): ", nStack ); + // The overflow area is a packed byte image, not an array of words. + fprintf( stderr, "\nStack (%d bytes): ", nStack ); for ( unsigned int i = 0; i < nStack; ++i ) - fprintf( stderr, "0x%lx, ", pStack[i] ); + fprintf( stderr, "%02x ", pStack[i] ); fprintf( stderr, "\n" ); } #endif @@ -136,27 +137,40 @@ static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, *reinterpret_cast<sal_uInt64 *>( pRegisterReturn ) = gpReturn[0]; break; case typelib_TypeClass_LONG: + *reinterpret_cast<sal_Int32 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int32 *>( &gpReturn[0] ); + break; case typelib_TypeClass_UNSIGNED_LONG: case typelib_TypeClass_ENUM: - *reinterpret_cast<sal_uInt32 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt32*>( &gpReturn[0] ); + *reinterpret_cast<sal_uInt32 *>( pRegisterReturn ) = + *reinterpret_cast<sal_uInt32 *>( &gpReturn[0] ); break; case typelib_TypeClass_CHAR: - case typelib_TypeClass_SHORT: case typelib_TypeClass_UNSIGNED_SHORT: *reinterpret_cast<sal_uInt16 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt16*>( &gpReturn[0] ); break; + case typelib_TypeClass_SHORT: + *reinterpret_cast<sal_Int16 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int16 *>( &gpReturn[0] ); + break; case typelib_TypeClass_BOOLEAN: - case typelib_TypeClass_BYTE: *reinterpret_cast<sal_uInt8 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt8*>( &gpReturn[0] ); break; + case typelib_TypeClass_BYTE: + *reinterpret_cast<sal_Int8 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int8 *>( &gpReturn[0] ); + break; case typelib_TypeClass_FLOAT: + *reinterpret_cast<float *>( pRegisterReturn ) = + *reinterpret_cast<float *>( &fpReturn[0] ); + break; case typelib_TypeClass_DOUBLE: *reinterpret_cast<double *>( pRegisterReturn ) = fpReturn[0]; break; default: { sal_Int32 const nRetSize = pReturnTypeRef->pType->nSize; - if (bSimpleReturn && nRetSize <= 16 && nRetSize > 0) + if (bSimpleReturn && nRetSize > 0) { // Register-returned aggregate: an HFA arrives in d0..d3, a // non-HFA <= 16 bytes in x0,x1. fill_struct picks the right one. @@ -169,43 +183,52 @@ static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, //================================================================================================== -// Macros for easier insertion of values to registers or stack +// Macros for inserting values into registers. Stack arguments are handled +// separately with a byte cursor because Apple packs them by size/alignment. // pSV - pointer to the source // nr - order of the value [will be increased if stored to register] // pFPR, pGPR - pointer to the registers -// pDS - pointer to the stack [will be increased if stored here] -// The pFPR slot holds the value to be loaded into a v register; the trampoline -// loads it with LDR d<n>, so float and double are stored the same way here. -#define INSERT_FLOAT_DOUBLE( pSV, nr, pFPR, pDS ) \ +// Each pFPR slot is the low 64 bits of a v register. A float occupies only +// the low 32 bits, while a double occupies all 64 bits. +#define INSERT_FLOAT( pSV, nr, pFPR ) \ + if ( nr < aarch64::MAX_FPR_REGS ) \ + { \ + pFPR[nr] = 0; \ + *reinterpret_cast<float *>( pFPR + nr++ ) = *reinterpret_cast<const float *>( pSV ); \ + } + +#define INSERT_DOUBLE( pSV, nr, pFPR ) \ if ( nr < aarch64::MAX_FPR_REGS ) \ - pFPR[nr++] = *reinterpret_cast<double *>( pSV ); \ - else \ - *pDS++ = *reinterpret_cast<sal_uInt64 *>( pSV ); // verbatim! + pFPR[nr++] = *reinterpret_cast<const double *>( pSV ); + +#define INSERT_INT64( pSV, nr, pGPR ) \ + if ( nr < aarch64::MAX_GPR_REGS ) \ + pGPR[nr++] = *reinterpret_cast<const sal_uInt64 *>( pSV ); + +#define INSERT_INT32( pSV, nr, pGPR ) \ + if ( nr < aarch64::MAX_GPR_REGS ) \ + pGPR[nr++] = *reinterpret_cast<const sal_uInt32 *>( pSV ); -#define INSERT_INT64( pSV, nr, pGPR, pDS ) \ +#define INSERT_SIGNED_INT32( pSV, nr, pGPR ) \ if ( nr < aarch64::MAX_GPR_REGS ) \ - pGPR[nr++] = *reinterpret_cast<sal_uInt64 *>( pSV ); \ - else \ - *pDS++ = *reinterpret_cast<sal_uInt64 *>( pSV ); + pGPR[nr++] = static_cast<sal_Int64>( *reinterpret_cast<const sal_Int32 *>( pSV ) ); -#define INSERT_INT32( pSV, nr, pGPR, pDS ) \ +#define INSERT_INT16( pSV, nr, pGPR ) \ if ( nr < aarch64::MAX_GPR_REGS ) \ - pGPR[nr++] = *reinterpret_cast<sal_uInt32 *>( pSV ); \ - else \ - *pDS++ = *reinterpret_cast<sal_uInt32 *>( pSV ); + pGPR[nr++] = *reinterpret_cast<const sal_uInt16 *>( pSV ); -#define INSERT_INT16( pSV, nr, pGPR, pDS ) \ +#define INSERT_SIGNED_INT16( pSV, nr, pGPR ) \ if ( nr < aarch64::MAX_GPR_REGS ) \ - pGPR[nr++] = *reinterpret_cast<sal_uInt16 *>( pSV ); \ - else \ - *pDS++ = *reinterpret_cast<sal_uInt16 *>( pSV ); + pGPR[nr++] = static_cast<sal_Int64>( *reinterpret_cast<const sal_Int16 *>( pSV ) ); -#define INSERT_INT8( pSV, nr, pGPR, pDS ) \ +#define INSERT_INT8( pSV, nr, pGPR ) \ if ( nr < aarch64::MAX_GPR_REGS ) \ - pGPR[nr++] = *reinterpret_cast<sal_uInt8 *>( pSV ); \ - else \ - *pDS++ = *reinterpret_cast<sal_uInt8 *>( pSV ); + pGPR[nr++] = *reinterpret_cast<const sal_uInt8 *>( pSV ); + +#define INSERT_SIGNED_INT8( pSV, nr, pGPR ) \ + if ( nr < aarch64::MAX_GPR_REGS ) \ + pGPR[nr++] = static_cast<sal_Int64>( *reinterpret_cast<const sal_Int8 *>( pSV ) ); //================================================================================================== @@ -228,10 +251,9 @@ static void cpp_call( sal_Int32 nParams, typelib_MethodParameter * pParams, void * pUnoReturn, void * pUnoArgs[], uno_Any ** ppUnoExc ) { - // Maxium space for [complex ret ptr], values | ptr ... - // (but will be used less - some of the values will be in pGPR and pFPR) - sal_uInt64 *pStack = (sal_uInt64 *)__builtin_alloca( (nParams + 3) * sizeof(sal_uInt64) ); - sal_uInt64 *pStackStart = pStack; + unsigned char *pStackStart = static_cast<unsigned char *>( + __builtin_alloca( (nParams + 3) * sizeof(sal_uInt64) ) ); + sal_uInt32 nStack = 0; sal_uInt64 pGPR[aarch64::MAX_GPR_REGS]; sal_uInt32 nGPR = 0; @@ -271,7 +293,7 @@ static void cpp_call( // Push "this" pointer void * pAdjustedThisPtr = reinterpret_cast< void ** >( pThis->getCppI() ) + aVtableSlot.offset; - INSERT_INT64( &pAdjustedThisPtr, nGPR, pGPR, pStack ); + INSERT_INT64( &pAdjustedThisPtr, nGPR, pGPR ); // Args void ** pCppArgs = (void **)alloca( 3 * sizeof(void *) * nParams ); @@ -292,34 +314,59 @@ static void cpp_call( { uno_copyAndConvertData( pCppArgs[nPos] = alloca( 8 ), pUnoArgs[nPos], pParamTypeDescr, pThis->getBridge()->getUno2Cpp() ); + bool onStack = false; switch (pParamTypeDescr->eTypeClass) { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: - INSERT_INT64( pCppArgs[nPos], nGPR, pGPR, pStack ); + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_INT64( pCppArgs[nPos], nGPR, pGPR ); break; case typelib_TypeClass_LONG: + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_SIGNED_INT32( pCppArgs[nPos], nGPR, pGPR ); + break; case typelib_TypeClass_UNSIGNED_LONG: case typelib_TypeClass_ENUM: - INSERT_INT32( pCppArgs[nPos], nGPR, pGPR, pStack ); + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_INT32( pCppArgs[nPos], nGPR, pGPR ); break; case typelib_TypeClass_SHORT: + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_SIGNED_INT16( pCppArgs[nPos], nGPR, pGPR ); + break; case typelib_TypeClass_CHAR: case typelib_TypeClass_UNSIGNED_SHORT: - INSERT_INT16( pCppArgs[nPos], nGPR, pGPR, pStack ); + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_INT16( pCppArgs[nPos], nGPR, pGPR ); break; - case typelib_TypeClass_BOOLEAN: case typelib_TypeClass_BYTE: - INSERT_INT8( pCppArgs[nPos], nGPR, pGPR, pStack ); + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_SIGNED_INT8( pCppArgs[nPos], nGPR, pGPR ); + break; + case typelib_TypeClass_BOOLEAN: + onStack = nGPR >= aarch64::MAX_GPR_REGS; + INSERT_INT8( pCppArgs[nPos], nGPR, pGPR ); break; case typelib_TypeClass_FLOAT: + onStack = nFPR >= aarch64::MAX_FPR_REGS; + INSERT_FLOAT( pCppArgs[nPos], nFPR, pFPR ); + break; case typelib_TypeClass_DOUBLE: - INSERT_FLOAT_DOUBLE( pCppArgs[nPos], nFPR, pFPR, pStack ); + onStack = nFPR >= aarch64::MAX_FPR_REGS; + INSERT_DOUBLE( pCppArgs[nPos], nFPR, pFPR ); break; default: break; } + if ( onStack ) + { + nStack = aarch64::align_stack_offset( nStack, rParam.pTypeRef ); + sal_uInt32 size = aarch64::stack_size( rParam.pTypeRef ); + memcpy( pStackStart + nStack, pCppArgs[nPos], size ); + nStack += size; + } // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); @@ -353,7 +400,16 @@ static void cpp_call( // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } - INSERT_INT64( &(pCppArgs[nPos]), nGPR, pGPR, pStack ); + if ( nGPR < aarch64::MAX_GPR_REGS ) + { + INSERT_INT64( &(pCppArgs[nPos]), nGPR, pGPR ); + } + else + { + nStack = (nStack + sizeof(void *) - 1) & ~(sizeof(void *) - 1); + memcpy( pStackStart + nStack, &pCppArgs[nPos], sizeof(void *) ); + nStack += sizeof(void *); + } } } @@ -364,7 +420,7 @@ static void cpp_call( pAdjustedThisPtr, aVtableSlot.index, pCppReturn, pReturnTypeRef, bSimpleReturn, pIndirectReturn, - pStackStart, ( pStack - pStackStart ), + pStackStart, nStack, pGPR, nGPR, pFPR, nFPR ); } catch (Exception &) { @@ -421,10 +477,13 @@ static void cpp_call( uno_destructData( pCppReturn, pReturnTypeDescr, cpp_release ); } } - catch (...) - { - // fill uno exception - fillUnoException( CPPU_CURRENT_NAMESPACE::__cxa_get_globals()->caughtExceptions, *ppUnoExc, pThis->getBridge()->getCpp2Uno() ); + catch (Exception & e) + { + // fill uno exception + std::type_info * type = CPPU_CURRENT_NAMESPACE::__cxa_current_exception_type(); + CPPU_CURRENT_NAMESPACE::fillUnoException( + type ? *type : typeid(e), &e, *ppUnoExc, + pThis->getBridge()->getCpp2Uno() ); // temporary params for ( ; nTempIndizes--; ) @@ -438,6 +497,24 @@ static void cpp_call( if (pReturnTypeDescr) TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); } + catch (...) + { + RuntimeException e( + OUString( RTL_CONSTASCII_USTRINGPARAM("C++ code threw unknown exception") ), + Reference< XInterface >() ); + uno_type_any_constructAndConvert( + *ppUnoExc, &e, ::getCppuType( &e ).getTypeLibType(), + pThis->getBridge()->getCpp2Uno() ); + for ( ; nTempIndizes--; ) + { + sal_Int32 nIndex = pTempIndizes[nTempIndizes]; + uno_destructData( + pCppArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], cpp_release ); + TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] ); + } + if (pReturnTypeDescr) + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + } } //================================================================================================== diff --git a/main/bridges/source/cpp_uno/shared/vtablefactory.cxx b/main/bridges/source/cpp_uno/shared/vtablefactory.cxx index 8a2832c314..fa7cd1bb4e 100644 --- a/main/bridges/source/cpp_uno/shared/vtablefactory.cxx +++ b/main/bridges/source/cpp_uno/shared/vtablefactory.cxx @@ -53,6 +53,9 @@ #include <unistd.h> #include <string.h> #include <sys/mman.h> +#if defined MACOSX && defined AARCH64 +#include <pthread.h> +#endif #elif defined SAL_W32 #define WIN32_LEAN_AND_MEAN #ifdef _MSC_VER @@ -74,6 +77,27 @@ using bridges::cpp_uno::shared::VtableFactory; namespace { +#if defined MACOSX && defined AARCH64 +class JitWriteGuard +{ +public: + JitWriteGuard(): m_active(pthread_jit_write_protect_supported_np() != 0) + { + if (m_active) + pthread_jit_write_protect_np(0); + } + + ~JitWriteGuard() + { + if (m_active) + pthread_jit_write_protect_np(1); + } + +private: + bool m_active; +}; +#endif + extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { sal_Size pagesize; #if defined SAL_UNX @@ -96,17 +120,25 @@ extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1); void * p; #if defined SAL_UNX +#if defined MACOSX && defined AARCH64 + p = mmap( + 0, n, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else p = mmap( 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#endif if (p == MAP_FAILED) { p = 0; } +#if !defined MACOSX || !defined AARCH64 else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { munmap (static_cast<char*>(p), n); p = 0; } +#endif #elif defined SAL_W32 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #elif defined(SAL_OS2) @@ -320,12 +352,18 @@ void VtableFactory::freeBlock(Block const & block) const { #else bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const { +#if defined MACOSX && defined AARCH64 + JitWriteGuard jitWriteGuard; +#endif block.size = getBlockSize(slotCount); block.start = rtl_arena_alloc(m_arena, &block.size); return block.start != 0; } void VtableFactory::freeBlock(Block const & block) const { +#if defined MACOSX && defined AARCH64 + JitWriteGuard jitWriteGuard; +#endif rtl_arena_free(m_arena, block.start, block.size); } #endif @@ -342,6 +380,9 @@ void VtableFactory::createVtables( throw std::bad_alloc(); } try { +#if defined MACOSX && defined AARCH64 + JitWriteGuard jitWriteGuard; +#endif Slot * slots = initializeBlock(block.start, slotCount); unsigned char * codeBegin = reinterpret_cast< unsigned char * >(slots); diff --git a/main/solenv/bin/addsym-macosx.sh b/main/solenv/bin/addsym-macosx.sh index a0b2ed6601..2b2d5e9d3b 100755 --- a/main/solenv/bin/addsym-macosx.sh +++ b/main/solenv/bin/addsym-macosx.sh @@ -44,5 +44,14 @@ s#$#$#' | tr '\n' '|' | sed "s#|\$##" >$2 # Please note that the awk expression expects to get the output of 'nm -gx'! # On Panther we have to filter out symbols with a value "1f" otherwise external # symbols will erroneously be added to the generated export symbols list file. +# Typeinfo and typeinfo-name symbols (__ZTI*, __ZTS*) are exempt from that +# filter: they have vague linkage and legitimately carry the same value, and +# exporting them is correct for cross-library catch/dynamic_cast on every +# platform that has a real version script. It does not, however, rescue the +# macOS/arm64 C++-UNO bridge's dlsym()-based exception lookup: clang emits the +# typeinfo of every keyless class (every UNO exception) as hidden on arm64 +# Darwin no matter what an export list says, so that bridge instead falls back +# to synthesising RTTI when dlsym() misses. See solenv/src/component.map and +# bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx. awk -v SYMBOLSREGEXP="`cat $2`" ' -match ($6,SYMBOLSREGEXP) > 0 && $6 !~ /_GLOBAL_/ { if (($2 != 1) && ( $2 != "1f" ) ) print $6 }' +match ($6,SYMBOLSREGEXP) > 0 && $6 !~ /_GLOBAL_/ { if (($2 != 1) && (($2 != "1f") || ($6 ~ /^__ZT[IS]/))) print $6 }' diff --git a/main/solenv/src/component.map b/main/solenv/src/component.map index ae0e47828f..f09f6bd704 100644 --- a/main/solenv/src/component.map +++ b/main/solenv/src/component.map @@ -18,10 +18,29 @@ # under the License. # ############################################################### +# NOTE: the _ZTI*/_ZTS* entries below are deliberately not platform-specific. +# A version script that hides typeinfo and typeinfo-name symbols breaks C++ +# across library boundaries: type_info objects have vague linkage, so hiding +# them gives each library its own copy and cross-library "catch" and +# "dynamic_cast" then fail to match. Exporting them restores the one-definition +# behaviour that libraries without a version script already have. +# +# Exporting is also the right intent on macOS/arm64, where the C++-UNO bridge +# resolves exception typeinfo with dlsym() first and falls back to synthesis +# only if that fails. In practice it always falls back there: clang emits the +# typeinfo of every keyless class (which is every UNO exception) as hidden on +# arm64 Darwin regardless of any version script or export list, so these _ZTI* +# entries do not reach dlsym() on that platform. Keep them anyway -- they are +# correct and load-bearing for every other target -- and see +# bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx (RTTI::getRTTI and +# RTTI::synthesiseRTTI) for how arm64 copes without them, and +# solenv/bin/addsym-macosx.sh. UDK_3_0_0 { global: component_getImplementationEnvironment; component_getFactory; + _ZTI*; + _ZTS*; local: *; }; diff --git a/main/testtools/com/sun/star/comp/bridge/TestComponent.java b/main/testtools/com/sun/star/comp/bridge/TestComponent.java index 0b54a6e650..e64969b8d0 100644 --- a/main/testtools/com/sun/star/comp/bridge/TestComponent.java +++ b/main/testtools/com/sun/star/comp/bridge/TestComponent.java @@ -44,6 +44,11 @@ import test.testtools.bridgetest.SmallStruct; import test.testtools.bridgetest.MediumStruct; import test.testtools.bridgetest.BigStruct; import test.testtools.bridgetest.AllFloats; +import test.testtools.bridgetest.TwoFloats; +import test.testtools.bridgetest.ThreeDoubles; +import test.testtools.bridgetest.MixedFloatLong; +import test.testtools.bridgetest.OneByte; +import test.testtools.bridgetest.ThreeLongs; import test.testtools.bridgetest.XBridgeTest; import test.testtools.bridgetest.XBridgeTest2; import test.testtools.bridgetest.XCurrentContextChecker; @@ -488,10 +493,48 @@ public class TestComponent { return i_Struct; } + public TwoFloats echoTwoFloats( TwoFloats i_Struct) throws com.sun.star.uno.RuntimeException { + return i_Struct; + } + + public ThreeDoubles echoThreeDoubles( ThreeDoubles i_Struct) throws com.sun.star.uno.RuntimeException { + return i_Struct; + } + + public MixedFloatLong echoMixedFloatLong( MixedFloatLong i_Struct) throws com.sun.star.uno.RuntimeException { + return i_Struct; + } + + public OneByte echoOneByte( OneByte i_Struct) throws com.sun.star.uno.RuntimeException { + return i_Struct; + } + + public ThreeLongs echoThreeLongs( ThreeLongs i_Struct) throws com.sun.star.uno.RuntimeException { + return i_Struct; + } + public int testPPCAlignment( long l1, long l2, int i1, long l3, int i2 ) throws com.sun.star.uno.RuntimeException { return i2; } + public long testPackedStack( long a0, long a1, long a2, long a3, long a4, long a5, long a6, + byte b, byte c, short s, int l, byte d, long h ) + throws com.sun.star.uno.RuntimeException + { + return ((((long)b) << 56) | + (((long)(c & 0xff)) << 48) | + (((long)(s & 0xffff)) << 32) | + (((long)l) & 0xffffffffL)) ^ + (((long)d) << 24) ^ h; + } + + public double testFpStack( double a0, double a1, double a2, double a3, double a4, + double a5, double a6, double a7, float f8, double d9 ) + throws com.sun.star.uno.RuntimeException + { + return (double)f8 + d9; + } + // Attributes public boolean getBool() throws com.sun.star.uno.RuntimeException { return _bool; diff --git a/main/testtools/source/bridgetest/bridgetest.cxx b/main/testtools/source/bridgetest/bridgetest.cxx index 70197b1443..e0a5265433 100644 --- a/main/testtools/source/bridgetest/bridgetest.cxx +++ b/main/testtools/source/bridgetest/bridgetest.cxx @@ -518,10 +518,64 @@ static sal_Bool performTest( memcmp(&aIn, &aOut, sizeof(AllFloats)) == 0, "all floats struct test"); } + { + TwoFloats aIn(1.25f, -2.5f); + TwoFloats aOut(xLBT->echoTwoFloats(aIn)); + bRet &= check(aOut.a == aIn.a && aOut.b == aIn.b, + "two-float HFA test"); + } + { + ThreeDoubles aIn(1.25, -2.5, 9.75); + ThreeDoubles aOut(xLBT->echoThreeDoubles(aIn)); + bRet &= check( + aOut.a == aIn.a && aOut.b == aIn.b && aOut.c == aIn.c, + "three-double HFA test"); + } + { + MixedFloatLong aIn(1.25f, -123456); + MixedFloatLong aOut(xLBT->echoMixedFloatLong(aIn)); + bRet &= check(aOut.a == aIn.a && aOut.b == aIn.b, + "mixed float-long struct test"); + } + { + OneByte aIn(-7); + OneByte aOut(xLBT->echoOneByte(aIn)); + bRet &= check(aOut.value == aIn.value, "one-byte struct test"); + } + { + ThreeLongs aIn(0x11223344, -7, 0x55667788); + ThreeLongs aOut(xLBT->echoThreeLongs(aIn)); + bRet &= check( + aOut.a == aIn.a && aOut.b == aIn.b && aOut.c == aIn.c, + "three-long struct test"); + } { sal_Int32 i2 = xLBT->testPPCAlignment(0, 0, 0, 0, 0xBEAF); bRet &= check(i2 == 0xBEAF, "ppc-style alignment test"); } + { + sal_Int8 b = -2; + sal_Int8 c = 0x35; + sal_Int16 s = -1234; + sal_Int32 l = 0x11223344; + sal_Int8 d = -7; + sal_Int64 h = SAL_CONST_INT64(0x0102030405060708); + sal_Int64 expected = ((static_cast<sal_Int64>(b) << 56) | + (static_cast<sal_uInt64>(static_cast<sal_uInt8>(c)) << 48) | + (static_cast<sal_uInt64>(static_cast<sal_uInt16>(s)) << 32) | + static_cast<sal_uInt32>(l)) ^ + (static_cast<sal_Int64>(d) << 24) ^ h; + bRet &= check( + xLBT->testPackedStack(0, 1, 2, 3, 4, 5, 6, b, c, s, l, d, h) + == expected, + "packed stack argument test"); + } + { + double result = xLBT->testFpStack( + 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, + -3.25f, 9.5); + bRet &= check(result == 6.25, "fp stack argument test"); + } // Test extended attributes that raise exceptions: try { xLBT->getRaiseAttr1(); diff --git a/main/testtools/source/bridgetest/cli/cli_cs_testobj.cs b/main/testtools/source/bridgetest/cli/cli_cs_testobj.cs index ad4caf22f4..dc1ed62517 100644 --- a/main/testtools/source/bridgetest/cli/cli_cs_testobj.cs +++ b/main/testtools/source/bridgetest/cli/cli_cs_testobj.cs @@ -240,11 +240,54 @@ public class BridgeTestObject : WeakBase, XRecursiveCall, XBridgeTest2 return arg; } + public TwoFloats echoTwoFloats(/*[in]*/TwoFloats arg) + { + return arg; + } + + public ThreeDoubles echoThreeDoubles(/*[in]*/ThreeDoubles arg) + { + return arg; + } + + public MixedFloatLong echoMixedFloatLong(/*[in]*/MixedFloatLong arg) + { + return arg; + } + + public OneByte echoOneByte(/*[in]*/OneByte arg) + { + return arg; + } + + public ThreeLongs echoThreeLongs(/*[in]*/ThreeLongs arg) + { + return arg; + } + public int testPPCAlignment( long l1, long l2, int i1, long l3, int i2 ) { return i2; } + // UNO byte maps to the unsigned System.Byte here, so the signed terms are + // recovered with an sbyte cast to match the other language bindings. + public long testPackedStack( long a0, long a1, long a2, long a3, long a4, long a5, long a6, + byte b, byte c, short s, int l, byte d, long h ) + { + return ((((long)(sbyte)b) << 56) | + (((long)c) << 48) | + (((long)(ushort)s) << 32) | + ((long)(uint)l)) ^ + (((long)(sbyte)d) << 24) ^ h; + } + + public double testFpStack( double a0, double a1, double a2, double a3, double a4, + double a5, double a6, double a7, float f8, double d9 ) + { + return (double)f8 + d9; + } + // Attributes public bool Bool { diff --git a/main/testtools/source/bridgetest/cppobj.cxx b/main/testtools/source/bridgetest/cppobj.cxx index b6dd183cd7..5d2cc941fd 100644 --- a/main/testtools/source/bridgetest/cppobj.cxx +++ b/main/testtools/source/bridgetest/cppobj.cxx @@ -217,8 +217,33 @@ public: { return rStruct; } virtual AllFloats SAL_CALL echoAllFloats(const AllFloats& rStruct) throw(com::sun::star::uno::RuntimeException) { return rStruct; } + virtual TwoFloats SAL_CALL echoTwoFloats(const TwoFloats& rStruct) throw(com::sun::star::uno::RuntimeException) + { return rStruct; } + virtual ThreeDoubles SAL_CALL echoThreeDoubles(const ThreeDoubles& rStruct) throw(com::sun::star::uno::RuntimeException) + { return rStruct; } + virtual MixedFloatLong SAL_CALL echoMixedFloatLong(const MixedFloatLong& rStruct) throw(com::sun::star::uno::RuntimeException) + { return rStruct; } + virtual OneByte SAL_CALL echoOneByte(const OneByte& rStruct) throw(com::sun::star::uno::RuntimeException) + { return rStruct; } + virtual ThreeLongs SAL_CALL echoThreeLongs(const ThreeLongs& rStruct) throw(com::sun::star::uno::RuntimeException) + { return rStruct; } virtual sal_Int32 SAL_CALL testPPCAlignment( sal_Int64, sal_Int64, sal_Int32, sal_Int64, sal_Int32 i2 ) throw(com::sun::star::uno::RuntimeException) { return i2; } + virtual sal_Int64 SAL_CALL testPackedStack( + sal_Int64, sal_Int64, sal_Int64, sal_Int64, sal_Int64, sal_Int64, + sal_Int64, sal_Int8 b, sal_Int8 c, sal_Int16 s, sal_Int32 l, + sal_Int8 d, sal_Int64 h) throw(com::sun::star::uno::RuntimeException) + { + return ((static_cast<sal_Int64>(b) << 56) | + (static_cast<sal_uInt64>(static_cast<sal_uInt8>(c)) << 48) | + (static_cast<sal_uInt64>(static_cast<sal_uInt16>(s)) << 32) | + static_cast<sal_uInt32>(l)) ^ + (static_cast<sal_Int64>(d) << 24) ^ h; + } + virtual double SAL_CALL testFpStack( + double, double, double, double, double, double, double, double, + float f8, double d9) throw(com::sun::star::uno::RuntimeException) + { return static_cast<double>(f8) + d9; } virtual sal_Bool SAL_CALL getBool() throw(com::sun::star::uno::RuntimeException) { return _aData.Bool; } diff --git a/main/testtools/source/bridgetest/idl/bridgetest.idl b/main/testtools/source/bridgetest/idl/bridgetest.idl index e4638aef1f..103bd6f9a6 100644 --- a/main/testtools/source/bridgetest/idl/bridgetest.idl +++ b/main/testtools/source/bridgetest/idl/bridgetest.idl @@ -106,6 +106,37 @@ struct AllFloats float c; float d; }; + +struct TwoFloats +{ + float a; + float b; +}; + +struct ThreeDoubles +{ + double a; + double b; + double c; +}; + +struct MixedFloatLong +{ + float a; + long b; +}; + +struct OneByte +{ + byte value; +}; + +struct ThreeLongs +{ + long a; + long b; + long c; +}; /** * complex types adding string, inteface, any */ @@ -261,12 +292,29 @@ interface XBridgeTestBase : com::sun::star::uno::XInterface * register return test 4 */ AllFloats echoAllFloats( [in] AllFloats aStruct ); + TwoFloats echoTwoFloats( [in] TwoFloats aStruct ); + ThreeDoubles echoThreeDoubles( [in] ThreeDoubles aStruct ); + MixedFloatLong echoMixedFloatLong( [in] MixedFloatLong aStruct ); + + OneByte echoOneByte( [in] OneByte aStruct ); + ThreeLongs echoThreeLongs( [in] ThreeLongs aStruct ); /** * register return test 4 (i107182) */ long testPPCAlignment( [in] hyper l1, [in] hyper l2, [in] long i1, [in] hyper l3, [in] long i2 ); + hyper testPackedStack( + [in] hyper a0, [in] hyper a1, [in] hyper a2, [in] hyper a3, + [in] hyper a4, [in] hyper a5, [in] hyper a6, + [in] byte b, [in] byte c, [in] short s, [in] long l, + [in] byte d, [in] hyper h ); + + double testFpStack( + [in] double a0, [in] double a1, [in] double a2, [in] double a3, + [in] double a4, [in] double a5, [in] double a6, [in] double a7, + [in] float f8, [in] double d9 ); + [attribute] boolean Bool; [attribute] byte Byte; [attribute] char Char;
