diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /bridges/source/cpp_uno/gcc3_macosx_x86-64 | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.cxx | 305 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.hxx | 67 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/call.cxx | 81 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/call.hxx | 33 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.cxx | 178 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.hxx | 37 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/cpp2uno.cxx | 536 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/except.cxx | 407 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/share.hxx | 156 | ||||
-rw-r--r-- | bridges/source/cpp_uno/gcc3_macosx_x86-64/uno2cpp.cxx | 442 |
10 files changed, 2242 insertions, 0 deletions
diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.cxx new file mode 100644 index 000000000..8f6ca3095 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.cxx @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + + +// This is an implementation of the x86-64 ABI as described in 'System V +// Application Binary Interface, AMD64 Architecture Processor Supplement' +// (http://www.x86-64.org/documentation/abi-0.95.pdf) +// +// The code in this file is a modification of src/x86/ffi64.c from libffi +// (http://sources.redhat.com/libffi/) which is under the following license: + +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 2002 Bo Thorsen <bo@suse.de> + + x86-64 Foreign Function Interface + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#include <sal/config.h> + +#include "abi.hxx" + +#include <o3tl/unreachable.hxx> +#include <sal/log.hxx> + +using namespace x86_64; + +namespace { + +/* Register class used for passing given 64bit part of the argument. + These represent classes as documented by the PS ABI, with the exception + of SSESF, SSEDF classes, that are basically SSE class, just gcc will + use SF or DFmode move instead of DImode to avoid reformatting penalties. + + Similarly we play games with INTEGERSI_CLASS to use cheaper SImode moves + whenever possible (upper half does contain padding). + */ +enum x86_64_reg_class +{ + X86_64_NO_CLASS, + X86_64_INTEGER_CLASS, + X86_64_INTEGERSI_CLASS, + X86_64_SSE_CLASS, + X86_64_SSESF_CLASS, + X86_64_MEMORY_CLASS +}; + +} + +#define MAX_CLASSES 4 + +/* x86-64 register passing implementation. See x86-64 ABI for details. Goal + of this code is to classify each 8bytes of incoming argument by the register + class and assign registers accordingly. */ + +/* Return the union class of CLASS1 and CLASS2. + See the x86-64 PS ABI for details. */ + +static enum x86_64_reg_class +merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2) + noexcept +{ + /* Rule #1: If both classes are equal, this is the resulting class. */ + if (class1 == class2) + return class1; + + /* Rule #2: If one of the classes is NO_CLASS, the resulting class is + the other class. */ + if (class1 == X86_64_NO_CLASS) + return class2; + if (class2 == X86_64_NO_CLASS) + return class1; + + /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */ + if (class1 == X86_64_MEMORY_CLASS || class2 == X86_64_MEMORY_CLASS) + return X86_64_MEMORY_CLASS; + + /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */ + if ((class1 == X86_64_INTEGERSI_CLASS && class2 == X86_64_SSESF_CLASS) + || (class2 == X86_64_INTEGERSI_CLASS && class1 == X86_64_SSESF_CLASS)) + return X86_64_INTEGERSI_CLASS; + if (class1 == X86_64_INTEGER_CLASS || class1 == X86_64_INTEGERSI_CLASS + || class2 == X86_64_INTEGER_CLASS || class2 == X86_64_INTEGERSI_CLASS) + return X86_64_INTEGER_CLASS; + + /* Rule #6: Otherwise class SSE is used. */ + return X86_64_SSE_CLASS; +} + +/* Classify a parameter/return type. + CLASSES will be filled by the register class used to pass each word + of the operand. The number of words is returned. In case the operand + should be passed in memory, 0 is returned. + + See the x86-64 PS ABI for details. +*/ +static int +classify_argument( typelib_TypeDescriptionReference *pTypeRef, enum x86_64_reg_class classes[], int byteOffset ) noexcept +{ + switch ( pTypeRef->eTypeClass ) + { + case typelib_TypeClass_CHAR: + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_ENUM: + if ( ( byteOffset % 8 + pTypeRef->pType->nSize ) <= 4 ) + classes[0] = X86_64_INTEGERSI_CLASS; + else + classes[0] = X86_64_INTEGER_CLASS; + return 1; + case typelib_TypeClass_FLOAT: + if ( ( byteOffset % 8 ) == 0 ) + classes[0] = X86_64_SSESF_CLASS; + else + classes[0] = X86_64_SSE_CLASS; + return 1; + case typelib_TypeClass_DOUBLE: + classes[0] = X86_64_SSE_CLASS; + return 1; + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + return 0; + case typelib_TypeClass_STRUCT: + { + typelib_TypeDescription * pTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + + const int UNITS_PER_WORD = 8; + int words = ( pTypeDescr->nSize + UNITS_PER_WORD - 1 ) / UNITS_PER_WORD; + enum x86_64_reg_class subclasses[MAX_CLASSES]; + + /* If the struct is larger than 16 bytes, pass it on the stack. */ + if ( pTypeDescr->nSize > 16 ) + { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return 0; + } + + for ( int i = 0; i < words; i++ ) + classes[i] = X86_64_NO_CLASS; + + const typelib_CompoundTypeDescription *pStruct = reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr ); + + /* Merge the fields of structure. */ + for ( sal_Int32 nMember = 0; nMember < pStruct->nMembers; ++nMember ) + { + typelib_TypeDescriptionReference *pTypeInStruct = pStruct->ppTypeRefs[ nMember ]; + int offset = byteOffset + pStruct->pMemberOffsets[ nMember ]; + + int num = classify_argument( pTypeInStruct, subclasses, offset ); + + if ( num == 0 ) + { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return 0; + } + + for ( int i = 0; i < num; i++ ) + { + int pos = offset / 8; + classes[i + pos] = merge_classes( subclasses[i], classes[i + pos] ); + if (classes[i + pos] == X86_64_MEMORY_CLASS) { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return 0; + } + } + } + + TYPELIB_DANGER_RELEASE( pTypeDescr ); + + return words; + } + + default: + O3TL_UNREACHABLE; + } +} + +/* Examine the argument and return set number of register required in each + class. Return 0 iff parameter should be passed in memory. */ +bool x86_64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedSSE ) noexcept +{ + enum x86_64_reg_class classes[MAX_CLASSES]; + int n; + + n = classify_argument( pTypeRef, classes, 0 ); + + if ( n == 0 ) + return false; + + nUsedGPR = 0; + nUsedSSE = 0; + for ( n--; n >= 0; n-- ) + switch ( classes[n] ) + { + case X86_64_INTEGER_CLASS: + case X86_64_INTEGERSI_CLASS: + nUsedGPR++; + break; + case X86_64_SSE_CLASS: + case X86_64_SSESF_CLASS: + nUsedSSE++; + break; + default: + O3TL_UNREACHABLE; + } + return true; +} + +bool x86_64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) noexcept +{ + if (pTypeRef->eTypeClass == typelib_TypeClass_VOID) { + return false; + } + x86_64_reg_class classes[MAX_CLASSES]; + return classify_argument(pTypeRef, classes, 0) == 0; +} + +x86_64::ReturnKind x86_64::getReturnKind(typelib_TypeDescriptionReference * type) noexcept { + x86_64_reg_class classes[MAX_CLASSES]; + auto const n = classify_argument(type, classes, 0); + if (n == 0) { + return ReturnKind::Memory; + } + if (n == 2 && (classes[0] == X86_64_SSE_CLASS || classes[0] == X86_64_SSESF_CLASS) + && (classes[1] == X86_64_INTEGER_CLASS || classes[1] == X86_64_INTEGERSI_CLASS)) + { + return ReturnKind::RegistersFpInt; + } + if (n == 2 && (classes[0] == X86_64_INTEGER_CLASS || classes[0] == X86_64_INTEGERSI_CLASS) + && (classes[1] == X86_64_SSE_CLASS || classes[1] == X86_64_SSESF_CLASS)) + { + return ReturnKind::RegistersIntFp; + } + return ReturnKind::RegistersGeneral; +} + +void x86_64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pSSE, void *pStruct ) noexcept +{ + enum x86_64_reg_class classes[MAX_CLASSES]; + int n; + + n = classify_argument( pTypeRef, classes, 0 ); + + sal_uInt64 *pStructAlign = static_cast<sal_uInt64 *>( pStruct ); + for ( int i = 0; i != n; ++i ) + switch ( classes[i] ) + { + case X86_64_INTEGER_CLASS: + case X86_64_INTEGERSI_CLASS: + *pStructAlign++ = *pGPR++; + break; + case X86_64_SSE_CLASS: + case X86_64_SSESF_CLASS: + *pStructAlign++ = *reinterpret_cast<const sal_uInt64 *>( pSSE++ ); + break; + default: + O3TL_UNREACHABLE; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.hxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.hxx new file mode 100644 index 000000000..3ef80543a --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/abi.hxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#pragma once + +// This is an implementation of the x86-64 ABI as described in 'System V +// Application Binary Interface, AMD64 Architecture Processor Supplement' +// (http://www.x86-64.org/documentation/abi-0.95.pdf) + +#include <typelib/typedescription.hxx> + +namespace x86_64 +{ + +/* 6 general purpose registers are used for parameter passing */ +const sal_uInt32 MAX_GPR_REGS = 6; + +/* 8 SSE registers are used for parameter passing */ +const sal_uInt32 MAX_SSE_REGS = 8; + +/* Count number of required registers. + + Examine the argument and return set number of register required in each + class. + + Return false iff parameter should be passed in memory. +*/ +bool examine_argument( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedSSE ) noexcept; + +/** Does function that returns this type use a hidden parameter, or registers? + + The value can be returned either in a hidden 1st parameter (which is a + pointer to a structure allocated by the caller), or in registers (rax, rdx + for the integers, xmm0, xmm1 for the floating point numbers). +*/ +bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) noexcept; + +enum class ReturnKind { + Memory, + RegistersGeneral, + RegistersFpInt, + RegistersIntFp +}; + +ReturnKind getReturnKind(typelib_TypeDescriptionReference * type) noexcept; + +void fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64* pGPR, const double* pSSE, void *pStruct ) noexcept; + +} // namespace x86_64 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.cxx new file mode 100644 index 000000000..2c7332a16 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#include <sal/config.h> + +#include "call.hxx" + +void privateSnippetExecutor() +{ + asm volatile + ( + " subq $160, %rsp\n" + + " movq %r10, -152(%rbp) # Save (nVtableOffset << 32) + nFunctionIndex\n" + + " movq %rdi, -112(%rbp) # Save GP registers\n" + " movq %rsi, -104(%rbp)\n" + " movq %rdx, -96(%rbp)\n" + " movq %rcx, -88(%rbp)\n" + " movq %r8 , -80(%rbp)\n" + " movq %r9 , -72(%rbp)\n" + + " movsd %xmm0, -64(%rbp) # Save FP registers\n" + " movsd %xmm1, -56(%rbp)\n" + " movsd %xmm2, -48(%rbp)\n" + " movsd %xmm3, -40(%rbp)\n" + " movsd %xmm4, -32(%rbp)\n" + " movsd %xmm5, -24(%rbp)\n" + " movsd %xmm6, -16(%rbp)\n" + " movsd %xmm7, -8(%rbp)\n" + + " leaq -144(%rbp), %r9 # 6th param: sal_uInt64 * pRegisterReturn\n" + " leaq 16(%rbp), %r8 # 5rd param: void ** ovrflw\n" + " leaq -64(%rbp), %rcx # 4th param: void ** fpreg\n" + " leaq -112(%rbp), %rdx # 3rd param: void ** gpreg\n" + " movl -148(%rbp), %esi # 2nd param: sal_int32 nVtableOffset\n" + " movl -152(%rbp), %edi # 1st param: sal_int32 nFunctionIndex\n" + + " call _cpp_vtable_call\n" + + " testl %eax, %eax\n" + " je .Lfpint\n" + " jg .Lintfp\n" + + " movq -144(%rbp), %rax # Potential return value (general case)\n" + " movq -136(%rbp), %rdx # Potential return value (general case)\n" + " movq -144(%rbp), %xmm0 # Potential return value (general case)\n" + " movq -136(%rbp), %xmm1 # Potential return value (general case)\n" + " jmp .Lfinish\n" + + ".Lfpint:\n" + " movq -144(%rbp), %xmm0 # Return value (special fp and integer case)\n" + " movq -136(%rbp), %rax # Return value (special fp and integer case)\n" + " jmp .Lfinish\n" + + ".Lintfp:\n" + " movq -144(%rbp), %rax # Return value (special integer and fp case)\n" + " movq -136(%rbp), %xmm0 # Return value (special integer and fp case)\n" + + ".Lfinish:\n" + " addq $160, %rsp\n" + ); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.hxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.hxx new file mode 100644 index 000000000..efb5b9518 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/call.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <sal/types.h> + +extern "C" int cpp_vtable_call( + sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, + void ** gpreg, void ** fpreg, void ** ovrflw, + sal_uInt64 * pRegisterReturn /* space for register return */ ); + +extern "C" void privateSnippetExecutor(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.cxx new file mode 100644 index 000000000..83a9ca81d --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.cxx @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#include <sal/config.h> + +#include <cstring> + +#include <cppu/macros.hxx> +#include <sal/types.h> +#include <typelib/typeclass.h> +#include <typelib/typedescription.h> + +#include "abi.hxx" +#include "callvirtualmethod.hxx" + +// The call instruction within the asm block of callVirtualMethod may throw +// exceptions. At least GCC 4.7.0 with -O0 would create (unnecessary) +// .gcc_exception_table call-site table entries around all other calls in this +// function that can throw, leading to std::terminate if the asm call throws an +// exception and the unwinding C++ personality routine finds the unexpected hole +// in the .gcc_exception_table. Therefore, make sure this function explicitly +// only calls nothrow-functions (so GCC 4.7.0 with -O0 happens to not create a +// .gcc_exception_table section at all for this function). For some reason, +// this also needs to be in a source file of its own. +// +// Also, this file should be compiled with -fnon-call-exceptions, and ideally +// there would be a way to tell the compiler that the asm block contains calls +// to functions that can potentially throw; see the mail thread starting at +// <http://gcc.gnu.org/ml/gcc/2012-03/msg00454.html> "C++: Letting compiler know +// asm block can call function that can throw?" + +void CPPU_CURRENT_NAMESPACE::callVirtualMethod( + void * pThis, sal_uInt32 nVtableIndex, void * pRegisterReturn, + typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, + sal_uInt64 *pStack, sal_uInt32 nStack, sal_uInt64 *pGPR, double * pFPR) +{ + // Work around -fsanitize=address "inline assembly requires more registers + // than available" error: + struct Data { + sal_uInt64 pMethod; + sal_uInt64 * pStack; + sal_uInt32 nStack; + sal_uInt64 * pGPR; + double * pFPR; + // Return values: + sal_uInt64 rax; + sal_uInt64 rdx; + double xmm0; + double xmm1; + } data; + data.pStack = pStack; + data.nStack = nStack; + data.pGPR = pGPR; + data.pFPR = pFPR; + + // Get pointer to method + sal_uInt64 pMethod = *static_cast<sal_uInt64 *>(pThis); + pMethod += 8 * nVtableIndex; + data.pMethod = *reinterpret_cast<sal_uInt64 *>(pMethod); + + asm volatile ( + // Push arguments to stack + "movq %%rsp, %%r12\n\t" + "movl 16%0, %%ecx\n\t" + "jrcxz Lpushed\n\t" + "xor %%rax, %%rax\n\t" + "leaq (%%rax, %%rcx, 8), %%rax\n\t" + "subq %%rax, %%rsp\n\t" + "andq $-9, %%rsp\n\t" // 16-bytes aligned + "movq 8%0, %%rsi\n\t" + "\nLpush:\n\t" + "decq %%rcx\n\t" + "movq (%%rsi, %%rcx, 8), %%rax\n\t" + "movq %%rax, (%%rsp, %%rcx, 8)\n\t" + "jnz Lpush\n\t" + "\nLpushed:\n\t" + + // Fill the xmm registers + "movq 32%0, %%rax\n\t" + + "movsd (%%rax), %%xmm0\n\t" + "movsd 8(%%rax), %%xmm1\n\t" + "movsd 16(%%rax), %%xmm2\n\t" + "movsd 24(%%rax), %%xmm3\n\t" + "movsd 32(%%rax), %%xmm4\n\t" + "movsd 40(%%rax), %%xmm5\n\t" + "movsd 48(%%rax), %%xmm6\n\t" + "movsd 56(%%rax), %%xmm7\n\t" + + // Fill the general purpose registers + "movq 24%0, %%rax\n\t" + + "movq (%%rax), %%rdi\n\t" + "movq 8(%%rax), %%rsi\n\t" + "movq 16(%%rax), %%rdx\n\t" + "movq 24(%%rax), %%rcx\n\t" + "movq 32(%%rax), %%r8\n\t" + "movq 40(%%rax), %%r9\n\t" + + // Perform the call + "movq 0%0, %%r11\n\t" + "call *%%r11\n\t" + + // Fill the return values + "movq %%rax, 40%0\n\t" + "movq %%rdx, 48%0\n\t" + "movsd %%xmm0, 56%0\n\t" + "movsd %%xmm1, 64%0\n\t" + + // Reset %rsp + "movq %%r12, %%rsp\n\t" + :: "o" (data) + : "rax", "rdi", "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "r12", + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", + "memory" + ); + + switch (pReturnTypeRef->eTypeClass) + { + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + *static_cast<sal_uInt64 *>( pRegisterReturn ) = data.rax; + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_ENUM: + *static_cast<sal_uInt32 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt32 *>( &data.rax ); + break; + case typelib_TypeClass_CHAR: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + *static_cast<sal_uInt16 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt16 *>( &data.rax ); + break; + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + *static_cast<sal_uInt8 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt8 *>( &data.rax ); + break; + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + *static_cast<double *>( pRegisterReturn ) = data.xmm0; + break; + default: + { + sal_Int32 const nRetSize = pReturnTypeRef->pType->nSize; + if (bSimpleReturn && nRetSize <= 16 && nRetSize > 0) + { + sal_uInt64 longs[2]; + longs[0] = data.rax; + longs[1] = data.rdx; + + double doubles[2]; + doubles[0] = data.xmm0; + doubles[1] = data.xmm1; + x86_64::fill_struct( pReturnTypeRef, &longs[0], &doubles[0], pRegisterReturn); + } + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.hxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.hxx new file mode 100644 index 000000000..ac87c1c85 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/callvirtualmethod.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <cppu/macros.hxx> +#include <sal/types.h> +#include <typelib/typedescription.h> + +namespace CPPU_CURRENT_NAMESPACE { + +void callVirtualMethod( + void * pThis, sal_uInt32 nVtableIndex, void * pRegisterReturn, + typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, + sal_uInt64 *pStack, sal_uInt32 nStack, sal_uInt64 *pGPR, double * pFPR); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/cpp2uno.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/cpp2uno.cxx new file mode 100644 index 000000000..efdf211d8 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/cpp2uno.cxx @@ -0,0 +1,536 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <rtl/alloc.h> +#include <sal/log.hxx> + +#include <com/sun/star/uno/genfunc.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <uno/data.h> +#include <typelib/typedescription.hxx> + +#include <bridge.hxx> +#include <cppinterfaceproxy.hxx> +#include <types.hxx> +#include <vtablefactory.hxx> + +#include "abi.hxx" +#include "call.hxx" +#include "share.hxx" + +using namespace ::osl; +using namespace ::com::sun::star::uno; + + +// Perform the UNO call +// +// We must convert the parameters stored in gpreg, fpreg and ovrflw to UNO +// arguments and call pThis->getUnoI()->pDispatcher. +// +// gpreg: [ret *], this, [gpr params] +// fpreg: [fpr params] +// ovrflw: [gpr or fpr params (properly aligned)] +// +// [ret *] is present when we are returning a structure bigger than 16 bytes +// Simple types are returned in rax, rdx (int), or xmm0, xmm1 (fp). +// Similarly structures <= 16 bytes are in rax, rdx, xmm0, xmm1 as necessary. +// +// The return value is the same as for cpp_vtable_call. +static int cpp2uno_call( + bridges::cpp_uno::shared::CppInterfaceProxy * pThis, + const typelib_TypeDescription * pMemberTypeDescr, + typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return + sal_Int32 nParams, typelib_MethodParameter * pParams, + void ** gpreg, void ** fpreg, void ** ovrflw, + 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 + + // return + typelib_TypeDescription * pReturnTypeDescr = nullptr; + if (pReturnTypeRef) + TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); + x86_64::ReturnKind returnKind + = (pReturnTypeRef == nullptr || pReturnTypeRef->eTypeClass == typelib_TypeClass_VOID) + ? x86_64::ReturnKind::RegistersGeneral : x86_64::getReturnKind(pReturnTypeRef); + + void * pUnoReturn = nullptr; + void * pCppReturn = nullptr; // complex return ptr: if != 0 && != pUnoReturn, reconversion need + + if ( pReturnTypeDescr ) + { + if ( returnKind == x86_64::ReturnKind::Memory ) + { + pCppReturn = *gpreg++; + nr_gpr++; + + pUnoReturn = ( bridges::cpp_uno::shared::relatesToInterfaceType( pReturnTypeDescr ) + ? alloca( pReturnTypeDescr->nSize ) + : pCppReturn ); // direct way + } + else + pUnoReturn = pRegisterReturn; // direct way for simple types + } + + // pop this + gpreg++; + nr_gpr++; + + // stack space + // parameters + void ** pUnoArgs = static_cast<void **>(alloca( 4 * sizeof(void *) * nParams )); + void ** pCppArgs = pUnoArgs + nParams; + // indices of values this have to be converted (interface conversion cpp<=>uno) + sal_Int32 * pTempIndices = reinterpret_cast<sal_Int32 *>(pUnoArgs + (2 * nParams)); + // type descriptions for reconversions + typelib_TypeDescription ** ppTempParamTypeDescr = reinterpret_cast<typelib_TypeDescription **>(pUnoArgs + (3 * nParams)); + + sal_Int32 nTempIndices = 0; + + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + const typelib_MethodParameter & rParam = pParams[nPos]; + + if ( !rParam.bOut && bridges::cpp_uno::shared::isSimpleType( rParam.pTypeRef ) ) // value + { + int nUsedGPR = 0; + int nUsedSSE = 0; +#ifndef NDEBUG + bool bFitsRegisters = +#endif + x86_64::examine_argument( rParam.pTypeRef, nUsedGPR, nUsedSSE ); + + // Simple types must fit exactly one register on x86_64 + assert(bFitsRegisters && ( ( nUsedSSE == 1 && nUsedGPR == 0 ) || ( nUsedSSE == 0 && nUsedGPR == 1 ) )); + + if ( nUsedSSE == 1 ) + { + if ( nr_fpr < x86_64::MAX_SSE_REGS ) + { + pCppArgs[nPos] = pUnoArgs[nPos] = fpreg++; + nr_fpr++; + } + else + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++; + } + else if ( nUsedGPR == 1 ) + { + if ( nr_gpr < x86_64::MAX_GPR_REGS ) + { + pCppArgs[nPos] = pUnoArgs[nPos] = gpreg++; + nr_gpr++; + } + else + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++; + } + } + else // ref + { + typelib_TypeDescription * pParamTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); + + void *pCppStack; + if ( nr_gpr < x86_64::MAX_GPR_REGS ) + { + pCppArgs[nPos] = pCppStack = *gpreg++; + nr_gpr++; + } + else + pCppArgs[nPos] = pCppStack = *ovrflw++; + + if (! rParam.bIn) // is pure out + { + // uno out is unconstructed mem! + pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ); + pTempIndices[nTempIndices] = nPos; + // will be released at reconversion + ppTempParamTypeDescr[nTempIndices++] = pParamTypeDescr; + } + else if ( bridges::cpp_uno::shared::relatesToInterfaceType( pParamTypeDescr ) ) // is in/inout + { + uno_copyAndConvertData( pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ), + pCppStack, pParamTypeDescr, + pThis->getBridge()->getCpp2Uno() ); + pTempIndices[nTempIndices] = nPos; // has to be reconverted + // will be released at reconversion + ppTempParamTypeDescr[nTempIndices++] = pParamTypeDescr; + } + else // direct way + { + pUnoArgs[nPos] = pCppStack; + // no longer needed + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + } + } + + // ExceptionHolder + uno_Any aUnoExc; // Any will be constructed by callee + uno_Any * pUnoExc = &aUnoExc; + + // invoke uno dispatch call + (*pThis->getUnoI()->pDispatcher)( pThis->getUnoI(), pMemberTypeDescr, pUnoReturn, pUnoArgs, &pUnoExc ); + + // in case an exception occurred... + if ( pUnoExc ) + { + // destruct temporary in/inout params + for ( ; nTempIndices--; ) + { + sal_Int32 nIndex = pTempIndices[nTempIndices]; + + if (pParams[nIndex].bIn) // is in/inout => was constructed + uno_destructData( pUnoArgs[nIndex], ppTempParamTypeDescr[nTempIndices], nullptr ); + TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndices] ); + } + if (pReturnTypeDescr) + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + + CPPU_CURRENT_NAMESPACE::raiseException( &aUnoExc, pThis->getBridge()->getUno2Cpp() ); // has to destruct the any + // is here for dummy + return 0; + } + else // else no exception occurred... + { + // temporary params + for ( ; nTempIndices--; ) + { + sal_Int32 nIndex = pTempIndices[nTempIndices]; + typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndices]; + + if ( pParams[nIndex].bOut ) // inout/out + { + // convert and assign + uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); + uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr, + pThis->getBridge()->getUno2Cpp() ); + } + // destroy temp uno param + uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, nullptr ); + + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + // return + if ( pCppReturn ) // has complex return + { + if ( pUnoReturn != pCppReturn ) // needs reconversion + { + uno_copyAndConvertData( pCppReturn, pUnoReturn, pReturnTypeDescr, + pThis->getBridge()->getUno2Cpp() ); + // destroy temp uno return + uno_destructData( pUnoReturn, pReturnTypeDescr, nullptr ); + } + // complex return ptr is set to return reg + *reinterpret_cast<void **>(pRegisterReturn) = pCppReturn; + } + if ( pReturnTypeDescr ) + { + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + } + switch (returnKind) { + case x86_64::ReturnKind::RegistersFpInt: + return 0; + case x86_64::ReturnKind::RegistersIntFp: + return 1; + default: + return -1; + } + } +} + +// Returns -1 for the general case where potential return values from privateSnippetExecutor can be +// copied from pRegisterReturn to both %rax and %rdx (in that order) and to %xmm0 and %xmm1 (in that +// order)---each specific return type will only require a subset of that copy operations, but the +// other copies to those non--callee-saved registers will be redundant and harmless. Returns 0 for +// the special case where return values from privateSnippetExecutor must be copied from +// pRegisterReturn to %xmm0 and %rax (in that order). Returns 1 for the special case where return +// privateSnippetExecutor must be copied from pRegisterReturn to %rax and %xmm0 (in that order). +int cpp_vtable_call( + sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, + void ** gpreg, void ** fpreg, void ** ovrflw, + sal_uInt64 * pRegisterReturn /* space for register return */ ) +{ + // gpreg: [ret *], this, [other gpr params] + // fpreg: [fpr params] + // ovrflw: [gpr or fpr params (properly aligned)] + void * pThis; + if ( nFunctionIndex & 0x80000000 ) + { + nFunctionIndex &= 0x7fffffff; + pThis = gpreg[1]; + } + else + { + pThis = gpreg[0]; + } + pThis = static_cast<char *>( pThis ) - nVtableOffset; + + bridges::cpp_uno::shared::CppInterfaceProxy * pCppI = + bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( pThis ); + + typelib_InterfaceTypeDescription * pTypeDescr = pCppI->getTypeDescr(); + + if ( nFunctionIndex >= pTypeDescr->nMapFunctionIndexToMemberIndex ) + { + SAL_WARN( + "bridges", + "illegal " << OUString::unacquired(&pTypeDescr->aBase.pTypeName) + << " vtable index " << nFunctionIndex << "/" + << pTypeDescr->nMapFunctionIndexToMemberIndex); + throw RuntimeException( + ("illegal " + OUString::unacquired(&pTypeDescr->aBase.pTypeName) + + " vtable index " + OUString::number(nFunctionIndex) + "/" + + OUString::number(pTypeDescr->nMapFunctionIndexToMemberIndex)), + reinterpret_cast<XInterface *>( pCppI ) ); + } + + // determine called method + sal_Int32 nMemberPos = pTypeDescr->pMapFunctionIndexToMemberIndex[nFunctionIndex]; + assert(nMemberPos < pTypeDescr->nAllMembers); + + TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] ); + + int eRet; + switch ( aMemberDescr.get()->eTypeClass ) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + typelib_TypeDescriptionReference *pAttrTypeRef = + reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( aMemberDescr.get() )->pAttributeTypeRef; + + if ( pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex ) + { + // is GET method + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef, + 0, nullptr, // no params + gpreg, fpreg, ovrflw, pRegisterReturn ); + } + else + { + // is SET method + typelib_MethodParameter aParam; + aParam.pTypeRef = pAttrTypeRef; + aParam.bIn = true; + aParam.bOut = false; + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + nullptr, // indicates void return + 1, &aParam, + gpreg, fpreg, ovrflw, pRegisterReturn ); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + // is METHOD + switch ( nFunctionIndex ) + { + case 1: // acquire() + pCppI->acquireProxy(); // non virtual call! + eRet = 0; + break; + case 2: // release() + pCppI->releaseProxy(); // non virtual call! + eRet = 0; + break; + case 0: // queryInterface() opt + { + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, static_cast<Type *>( gpreg[2] )->getTypeLibType() ); + if ( pTD ) + { + XInterface * pInterface = nullptr; + (*pCppI->getBridge()->getCppEnv()->getRegisteredInterface) + ( pCppI->getBridge()->getCppEnv(), + reinterpret_cast<void **>(&pInterface), + pCppI->getOid().pData, + reinterpret_cast<typelib_InterfaceTypeDescription *>( pTD ) ); + + if ( pInterface ) + { + ::uno_any_construct( static_cast<uno_Any *>( gpreg[0] ), + &pInterface, pTD, cpp_acquire ); + + pInterface->release(); + TYPELIB_DANGER_RELEASE( pTD ); + + reinterpret_cast<void **>( pRegisterReturn )[0] = gpreg[0]; + eRet = 0; + break; + } + TYPELIB_DANGER_RELEASE( pTD ); + } + [[fallthrough]]; // else perform queryInterface() + } + default: + { + typelib_InterfaceMethodTypeDescription *pMethodTD = + reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( aMemberDescr.get() ); + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + pMethodTD->pReturnTypeRef, + pMethodTD->nParams, + pMethodTD->pParams, + gpreg, fpreg, ovrflw, pRegisterReturn ); + } + } + break; + } + default: + { + throw RuntimeException("no member description found!", + reinterpret_cast<XInterface *>( pCppI ) ); + } + } + + return eRet; +} + +const int codeSnippetSize = 24; + +// Generate a trampoline that redirects method calls to +// privateSnippetExecutor(). +// +// privateSnippetExecutor() saves all the registers that are used for +// parameter passing on x86_64, and calls the cpp_vtable_call(). +// When it returns, privateSnippetExecutor() sets the return value. +// +// Note: The code snippet we build here must not create a stack frame, +// otherwise the UNO exceptions stop working thanks to non-existing +// unwinding info. +static unsigned char * codeSnippet( unsigned char * code, + sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, + bool bHasHiddenParam ) +{ + sal_uInt64 nOffsetAndIndex = ( static_cast<sal_uInt64>(nVtableOffset) << 32 ) | static_cast<sal_uInt64>(nFunctionIndex); + + if ( bHasHiddenParam ) + nOffsetAndIndex |= 0x80000000; + + // movq $<nOffsetAndIndex>, %r10 + *reinterpret_cast<sal_uInt16 *>( code ) = 0xba49; + *reinterpret_cast<sal_uInt16 *>( code + 2 ) = nOffsetAndIndex & 0xFFFF; + *reinterpret_cast<sal_uInt32 *>( code + 4 ) = nOffsetAndIndex >> 16; + *reinterpret_cast<sal_uInt16 *>( code + 8 ) = nOffsetAndIndex >> 48; + + // movq $<address of the privateSnippetExecutor>, %r11 + *reinterpret_cast<sal_uInt16 *>( code + 10 ) = 0xbb49; + *reinterpret_cast<sal_uInt32 *>( code + 12 ) + = reinterpret_cast<sal_uInt64>(privateSnippetExecutor); + *reinterpret_cast<sal_uInt32 *>( code + 16 ) + = reinterpret_cast<sal_uInt64>(privateSnippetExecutor) >> 32; + + // jmpq *%r11 + *reinterpret_cast<sal_uInt32 *>( code + 20 ) = 0x00e3ff49; + +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, + "==> codeSnippet, functionIndex=%d%s, vtableOffset=%d\n", + nFunctionIndex, (bHasHiddenParam ? "|0x80000000":""), nVtableOffset); +#endif + + return code + codeSnippetSize; +} + +struct bridges::cpp_uno::shared::VtableFactory::Slot { void * fn; }; + +bridges::cpp_uno::shared::VtableFactory::Slot * +bridges::cpp_uno::shared::VtableFactory::mapBlockToVtable(void * block) +{ + return static_cast< Slot * >(block) + 2; +} + +std::size_t bridges::cpp_uno::shared::VtableFactory::getBlockSize( + sal_Int32 slotCount) +{ + return (slotCount + 2) * sizeof (Slot) + slotCount * codeSnippetSize; +} + +bridges::cpp_uno::shared::VtableFactory::Slot * +bridges::cpp_uno::shared::VtableFactory::initializeBlock( + void * block, sal_Int32 slotCount, sal_Int32, + typelib_InterfaceTypeDescription *) +{ + Slot * slots = mapBlockToVtable(block); + slots[-2].fn = nullptr; + slots[-1].fn = nullptr; + return slots + slotCount; +} + + +unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( + Slot ** slots, unsigned char * code, + typelib_InterfaceTypeDescription const * type, sal_Int32 nFunctionOffset, + sal_Int32 functionCount, sal_Int32 nVtableOffset ) +{ + const sal_PtrDiff writetoexecdiff = 0; + (*slots) -= functionCount; + Slot * s = *slots; + for ( sal_Int32 nPos = 0; nPos < type->nMembers; ++nPos ) + { + typelib_TypeDescription * pTD = nullptr; + + TYPELIB_DANGER_GET( &pTD, type->ppMembers[ nPos ] ); + assert(pTD); + + if ( typelib_TypeClass_INTERFACE_ATTRIBUTE == pTD->eTypeClass ) + { + typelib_InterfaceAttributeTypeDescription *pAttrTD = + reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( pTD ); + + // get method + (s++)->fn = code + writetoexecdiff; + code = codeSnippet( code, nFunctionOffset++, nVtableOffset, + x86_64::return_in_hidden_param( pAttrTD->pAttributeTypeRef ) ); + + if ( ! pAttrTD->bReadOnly ) + { + // set method + (s++)->fn = code + writetoexecdiff; + code = codeSnippet( code, nFunctionOffset++, nVtableOffset, false ); + } + } + else if ( typelib_TypeClass_INTERFACE_METHOD == pTD->eTypeClass ) + { + typelib_InterfaceMethodTypeDescription *pMethodTD = + reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( pTD ); + + (s++)->fn = code + writetoexecdiff; + code = codeSnippet( code, nFunctionOffset++, nVtableOffset, + x86_64::return_in_hidden_param( pMethodTD->pReturnTypeRef ) ); + } + else + assert(false); + + TYPELIB_DANGER_RELEASE( pTD ); + } + return code; +} + +void bridges::cpp_uno::shared::VtableFactory::flushCode( + SAL_UNUSED_PARAMETER unsigned char const *, + SAL_UNUSED_PARAMETER unsigned char const * ) +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/except.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/except.cxx new file mode 100644 index 000000000..60f5f6e40 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/except.cxx @@ -0,0 +1,407 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <new> +#include <stdio.h> +#include <string.h> +#include <typeinfo> + +#include <cxxabi.h> +#include <dlfcn.h> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/genfunc.hxx> +#include <sal/log.hxx> +#include <osl/mutex.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <typelib/typedescription.h> +#include <uno/any2.h> +#include <unordered_map> +#include "share.hxx" + +using namespace ::osl; +using namespace ::com::sun::star::uno; + +namespace CPPU_CURRENT_NAMESPACE { + +namespace { + +struct Fake_type_info { + virtual ~Fake_type_info() = delete; + char const * name; +}; + +struct Fake_class_type_info: Fake_type_info { + virtual ~Fake_class_type_info() override = delete; +}; + +struct Fake_si_class_type_info: Fake_class_type_info { + virtual ~Fake_si_class_type_info() override = delete; + void const * base; +}; + +struct Base {}; +struct Derived: Base {}; + +std::type_info * createFake_class_type_info(char const * name) { + char * buf = new char[sizeof (Fake_class_type_info)]; + + *reinterpret_cast<void **>(buf) = *reinterpret_cast<void * const *>( + &typeid(Base)); + // copy __cxxabiv1::__class_type_info vtable into place + Fake_class_type_info * fake = reinterpret_cast<Fake_class_type_info *>(buf); + fake->name = name; + return reinterpret_cast<std::type_info *>( + static_cast<Fake_type_info *>(fake)); +} + +std::type_info * createFake_si_class_type_info( + char const * name, std::type_info const * base) +{ + char * buf = new char[sizeof (Fake_si_class_type_info)]; + + *reinterpret_cast<void **>(buf) = *reinterpret_cast<void * const *>( + &typeid(Derived)); + // copy __cxxabiv1::__si_class_type_info vtable into place + Fake_si_class_type_info * fake + = reinterpret_cast<Fake_si_class_type_info *>(buf); + fake->name = name; + fake->base = base; + return reinterpret_cast<std::type_info *>( + static_cast<Fake_type_info *>(fake)); +} + +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +void dummy_can_throw_anything( char const * ) +{ +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static OUString toUNOname( char const * p ) +{ +#if OSL_DEBUG_LEVEL > 1 + char const * start = p; +#endif + + // example: N3com3sun4star4lang24IllegalArgumentExceptionE + + OUStringBuffer buf( 64 ); + assert( 'N' == *p ); + ++p; // skip N + + while ('E' != *p) + { + // read chars count + long n = *p++ - '0'; + while ('0' <= *p && '9' >= *p) + { + n *= 10; + n += (*p++ - '0'); + } + buf.appendAscii( p, n ); + p += n; + if ('E' != *p) + buf.append( '.' ); + } + +#if OSL_DEBUG_LEVEL > 1 + OUString ret( buf.makeStringAndClear() ); + OString c_ret( OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US ) ); + fprintf( stderr, "> toUNOname(): %s => %s\n", start, c_ret.getStr() ); + return ret; +#else + return buf.makeStringAndClear(); +#endif +} + +namespace { + +class RTTI +{ + typedef std::unordered_map< OUString, std::type_info * > t_rtti_map; + + Mutex m_mutex; + t_rtti_map m_rttis; + t_rtti_map m_generatedRttis; + + void * m_hApp; + +public: + RTTI(); + ~RTTI(); + + std::type_info * getRTTI( typelib_CompoundTypeDescription * ); +}; + +} + +RTTI::RTTI() + : m_hApp( dlopen( nullptr, RTLD_LAZY ) ) +{ +} + +RTTI::~RTTI() +{ + dlclose( m_hApp ); +} + + +std::type_info * RTTI::getRTTI( typelib_CompoundTypeDescription *pTypeDescr ) +{ + std::type_info * rtti; + + OUString const & unoName = OUString::unacquired(&pTypeDescr->aBase.pTypeName); + + MutexGuard guard( m_mutex ); + t_rtti_map::const_iterator iFind( m_rttis.find( unoName ) ); + if (iFind == m_rttis.end()) + { + // RTTI symbol + OStringBuffer buf( 64 ); + buf.append( "_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' ); + + OString symName( buf.makeStringAndClear() ); + rtti = static_cast<std::type_info *>(dlsym( m_hApp, symName.getStr() )); + + if (rtti) + { + std::pair< t_rtti_map::iterator, bool > insertion( + m_rttis.insert( t_rtti_map::value_type( unoName, rtti ) ) ); + SAL_WARN_IF( !insertion.second, + "bridges", + "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 * rttiName = strdup(symName.getStr() + 4); + if (rttiName == nullptr) { + throw std::bad_alloc(); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr,"generated rtti for %s\n", rttiName ); +#endif + if (pTypeDescr->pBaseTypeDescription) + { + // ensure availability of base + std::type_info * base_rtti = getRTTI( + pTypeDescr->pBaseTypeDescription ); + rtti = createFake_si_class_type_info(rttiName, base_rtti); + } + else + { + rtti = createFake_class_type_info(rttiName); + } + + std::pair< t_rtti_map::iterator, bool > insertion( + m_generatedRttis.insert( t_rtti_map::value_type( unoName, rtti ) ) ); + SAL_WARN_IF( !insertion.second, + "bridges", + "inserting new generated rtti failed" ); + } + else // taking already generated rtti + { + rtti = iFind2->second; + } + } + } + else + { + rtti = iFind->second; + } + + return rtti; +} + + +static void deleteException( void * pExc ) +{ + __cxa_exception const * header = static_cast<__cxa_exception const *>(pExc) - 1; + // The libcxxabi commit + // <http://llvm.org/viewvc/llvm-project?view=revision&revision=303175> + // "[libcxxabi] Align unwindHeader on a double-word boundary" towards + // LLVM 5.0 changed the size of __cxa_exception by adding + // + // __attribute__((aligned)) + // + // to the final member unwindHeader, on x86-64 effectively adding a hole of + // size 8 in front of that member (changing its offset from 88 to 96, + // sizeof(__cxa_exception) from 120 to 128, and alignof(__cxa_exception) + // from 8 to 16); a hack to dynamically determine whether we run against a + // LLVM 5 libcxxabi is to look at the exceptionDestructor member, which must + // point to this function (the use of __cxa_exception in fillUnoException is + // unaffected, as it only accesses members towards the start of the struct, + // through a pointer known to actually point at the start). The libcxxabi commit + // <https://github.com/llvm/llvm-project/commit/9ef1daa46edb80c47d0486148c0afc4e0d83ddcf> + // "Insert padding before the __cxa_exception header to ensure the thrown" in LLVM 6 + // removes the need for this hack, so it can be removed again once we can be sure that we only + // run against libcxxabi from LLVM >= 6: + if (header->exceptionDestructor != &deleteException) { + header = reinterpret_cast<__cxa_exception const *>( + reinterpret_cast<char const *>(header) - 8); + assert(header->exceptionDestructor == &deleteException); + } + typelib_TypeDescription * pTD = nullptr; + OUString unoName( toUNOname( header->exceptionType->name() ) ); + ::typelib_typedescription_getByName( &pTD, unoName.pData ); + assert(pTD && "### unknown exception type! leaving out destruction => leaking!!!"); + if (pTD) + { + ::uno_destructData( pExc, pTD, cpp_release ); + ::typelib_typedescription_release( pTD ); + } +} + +void raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ) +{ +#if OSL_DEBUG_LEVEL > 1 + OString cstr( + OUStringToOString( + OUString::unacquired( &pUnoExc->pType->pTypeName ), + RTL_TEXTENCODING_ASCII_US ) ); + fprintf( stderr, "> uno exception occurred: %s\n", cstr.getStr() ); +#endif + void * pCppExc; + std::type_info * rtti; + + { + // construct cpp exception object + typelib_TypeDescription * pTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pTypeDescr, pUnoExc->pType ); + assert(pTypeDescr); + if (! pTypeDescr) + { + throw RuntimeException( + "cannot get typedescription for type " + + OUString::unacquired( &pUnoExc->pType->pTypeName ) ); + } + + pCppExc = __cxxabiv1::__cxa_allocate_exception( pTypeDescr->nSize ); + ::uno_copyAndConvertData( pCppExc, pUnoExc->pData, pTypeDescr, pUno2Cpp ); + + // destruct uno exception + ::uno_any_destruct( pUnoExc, nullptr ); + // avoiding locked counts + static RTTI rtti_data; + rtti = rtti_data.getRTTI(reinterpret_cast<typelib_CompoundTypeDescription*>(pTypeDescr)); + TYPELIB_DANGER_RELEASE( pTypeDescr ); + assert(rtti && "### no rtti for throwing exception!"); + if (! rtti) + { + throw RuntimeException( + "no rtti for type " + + OUString::unacquired( &pUnoExc->pType->pTypeName ) ); + } + } + + __cxxabiv1::__cxa_throw( pCppExc, rtti, deleteException ); +} + +void fillUnoException(uno_Any * pUnoExc, uno_Mapping * pCpp2Uno) +{ + __cxa_exception * header = __cxa_get_globals()->caughtExceptions; + if (! header) + { + RuntimeException aRE( "no exception header!" ); + Type const & rType = cppu::UnoType<decltype(aRE)>::get(); + uno_type_any_constructAndConvert( pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno ); + SAL_WARN("bridges", aRE.Message); + return; + } + + // Very bad HACK to find out whether we run against a libcxxabi that has a new + // __cxa_exception::reserved member at the start, introduced with LLVM 10 + // <https://github.com/llvm/llvm-project/commit/674ec1eb16678b8addc02a4b0534ab383d22fa77> + // "[libcxxabi] Insert padding in __cxa_exception struct for compatibility". The layout of the + // start of __cxa_exception is + // + // [8 byte void *reserve] + // 8 byte size_t referenceCount + // + // where the (bad, hacky) assumption is that reserve (if present) is null + // (__cxa_allocate_exception in at least LLVM 11 zero-fills the object, and nothing actively + // sets reserve) while referenceCount is non-null (__cxa_throw sets it to 1, and + // __cxa_decrement_exception_refcount destroys the exception as soon as it drops to 0; for a + // __cxa_dependent_exception, the referenceCount member is rather + // + // 8 byte void* primaryException + // + // but which also will always be set to a non-null value in __cxa_rethrow_primary_exception). + // As described in the definition of __cxa_exception + // (bridges/source/cpp_uno/gcc3_macosx_x86-64/share.hxx), this hack (together with the "#if 0" + // there) can be dropped once we can be sure that we only run against new libcxxabi that has the + // reserve member: + if (*reinterpret_cast<void **>(header) == nullptr) { + header = reinterpret_cast<__cxa_exception *>(reinterpret_cast<void **>(header) + 1); + } + + std::type_info *exceptionType = __cxxabiv1::__cxa_current_exception_type(); + + typelib_TypeDescription * pExcTypeDescr = nullptr; + OUString unoName( toUNOname( 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 (nullptr == pExcTypeDescr) + { + RuntimeException aRE( "exception type not found: " + unoName ); + Type const & rType = cppu::UnoType<decltype(aRE)>::get(); + uno_type_any_constructAndConvert( pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno ); + SAL_WARN("bridges", aRE.Message); + } + else + { + // construct uno exception any + uno_any_constructAndConvert( pUnoExc, header->adjustedPtr, pExcTypeDescr, pCpp2Uno ); + typelib_typedescription_release( pExcTypeDescr ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/share.hxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/share.hxx new file mode 100644 index 000000000..6b09d99a5 --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/share.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ +#pragma once + +#include <uno/mapping.h> + +#include <typeinfo> +#include <exception> +#include <cstddef> + +namespace CPPU_CURRENT_NAMESPACE +{ + +// From opensource.apple.com: libunwind-35.1/include/unwind.h + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +struct _Unwind_Exception +{ + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, struct _Unwind_Exception* exc); + uintptr_t private_1; // non-zero means forced unwind + uintptr_t private_2; // holds sp that phase1 found for phase2 to use +#if !__LP64__ + // The gcc implementation of _Unwind_Exception used attribute mode on the above fields + // which had the side effect of causing this whole struct to round up to 32 bytes in size. + // To be more explicit, we add pad fields added for binary compatibility. + uint32_t reserved[3]; +#endif +}; + + +// From libcppabi-24.2/include/unwind-cxx.h + +typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); + +// A C++ exception object consists of a header, which is a wrapper around +// an unwind object header with additional C++ specific information, +// followed by the exception object itself. + +struct __cxa_exception +{ +#if __LP64__ +#if 0 + // This is a new field added with LLVM 10 + // <https://github.com/llvm/llvm-project/commit/674ec1eb16678b8addc02a4b0534ab383d22fa77> + // "[libcxxabi] Insert padding in __cxa_exception struct for compatibility". The HACK in + // fillUnoException (bridges/source/cpp_uno/gcc3_macosx_x86-64/except.cxx) tries to find out at + // runtime whether a __cxa_exception has this member. Once we can be sure that we only run + // against new libcxxabi that has this member, we can drop the "#if 0" here and drop the hack + // in fillUnoException. + + // Now _Unwind_Exception is marked with __attribute__((aligned)), + // which implies __cxa_exception is also aligned. Insert padding + // in the beginning of the struct, rather than before unwindHeader. + void *reserve; +#endif + + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is at the start of this + // struct which is prepended to the object thrown in + // __cxa_allocate_exception. + size_t referenceCount; +#endif + // Manage the exception object itself. + std::type_info *exceptionType; + void (*exceptionDestructor)(void *); + + // The C++ standard has entertaining rules wrt calling set_terminate + // and set_unexpected in the middle of the exception cleanup process. + void (*unexpectedHandler)(); // std::unexpected_handler dropped from C++17 + std::terminate_handler terminateHandler; + + // The caught exception stack threads through here. + __cxa_exception *nextException; + + // How many nested handlers have caught this exception. A negated + // value is a signal that this object has been rethrown. + int handlerCount; + +#ifdef __ARM_EABI_UNWINDER__ + // Stack of exceptions in cleanups. + __cxa_exception* nextPropagatingException; + + // The number of active cleanup handlers for this exception. + int propagationCount; +#else + // Cache parsed handler data from the personality routine Phase 1 + // for Phase 2 and __cxa_call_unexpected. + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + _Unwind_Ptr catchTemp; + void *adjustedPtr; +#endif +#if !__LP64__ + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is placed where the compiler + // previously adding padded to 64-bit align unwindHeader. + size_t referenceCount; +#endif + + // The generic exception header. Must be last. + _Unwind_Exception unwindHeader; +}; + +// Each thread in a C++ program has access to a __cxa_eh_globals object. +struct __cxa_eh_globals +{ + __cxa_exception *caughtExceptions; + unsigned int uncaughtExceptions; +#ifdef __ARM_EABI_UNWINDER__ + __cxa_exception* propagatingExceptions; +#endif +}; + +} + +extern "C" CPPU_CURRENT_NAMESPACE::__cxa_eh_globals *__cxa_get_globals () noexcept; + +namespace CPPU_CURRENT_NAMESPACE +{ + +void raiseException( + uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ); + +void fillUnoException(uno_Any *, uno_Mapping * pCpp2Uno); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/gcc3_macosx_x86-64/uno2cpp.cxx b/bridges/source/cpp_uno/gcc3_macosx_x86-64/uno2cpp.cxx new file mode 100644 index 000000000..0b320a00c --- /dev/null +++ b/bridges/source/cpp_uno/gcc3_macosx_x86-64/uno2cpp.cxx @@ -0,0 +1,442 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 regarding copyright + * ownership. The ASF licenses this file 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 . + */ + +#include <sal/alloca.h> + +#include <exception> +#include <typeinfo> + +#include <rtl/alloc.h> + +#include <com/sun/star/uno/genfunc.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <o3tl/runtimetooustring.hxx> +#include <uno/data.h> + +#include <bridge.hxx> +#include <types.hxx> +#include <unointerfaceproxy.hxx> +#include <vtables.hxx> + +#include "abi.hxx" +#include "callvirtualmethod.hxx" +#include "share.hxx" + +using namespace ::com::sun::star::uno; + +namespace { + +// Functions for easier insertion of values to registers or stack +// 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 value in %xmm register is already prepared to be retrieved as a float, +// thus we treat float and double the same +void INSERT_FLOAT_DOUBLE( + void const * pSV, sal_uInt32 & nr, double * pFPR, sal_uInt64 *& pDS) +{ + if ( nr < x86_64::MAX_SSE_REGS ) + pFPR[nr++] = *static_cast<double const *>( pSV ); + else + *pDS++ = *static_cast<sal_uInt64 const *>( pSV ); // verbatim! +} + +void INSERT_INT64( + void const * pSV, sal_uInt32 & nr, sal_uInt64 * pGPR, sal_uInt64 *& pDS) +{ + if ( nr < x86_64::MAX_GPR_REGS ) + pGPR[nr++] = *static_cast<sal_uInt64 const *>( pSV ); + else + *pDS++ = *static_cast<sal_uInt64 const *>( pSV ); +} + +void INSERT_INT32( + void const * pSV, sal_uInt32 & nr, sal_uInt64 * pGPR, sal_uInt64 *& pDS) +{ + if ( nr < x86_64::MAX_GPR_REGS ) + pGPR[nr++] = *static_cast<sal_uInt32 const *>( pSV ); + else + *pDS++ = *static_cast<sal_uInt32 const *>( pSV ); +} + +void INSERT_INT16( + void const * pSV, sal_uInt32 & nr, sal_uInt64 * pGPR, sal_uInt64 *& pDS) +{ + if ( nr < x86_64::MAX_GPR_REGS ) + pGPR[nr++] = *static_cast<sal_uInt16 const *>( pSV ); + else + *pDS++ = *static_cast<sal_uInt16 const *>( pSV ); +} + +void INSERT_INT8( + void const * pSV, sal_uInt32 & nr, sal_uInt64 * pGPR, sal_uInt64 *& pDS) +{ + if ( nr < x86_64::MAX_GPR_REGS ) + pGPR[nr++] = *static_cast<sal_uInt8 const *>( pSV ); + else + *pDS++ = *static_cast<sal_uInt8 const *>( pSV ); +} + +} + +static void cpp_call( + bridges::cpp_uno::shared::UnoInterfaceProxy * pThis, + bridges::cpp_uno::shared::VtableSlot aVtableSlot, + typelib_TypeDescriptionReference * pReturnTypeRef, + sal_Int32 nParams, typelib_MethodParameter * pParams, + void * pUnoReturn, void * pUnoArgs[], uno_Any ** ppUnoExc ) +{ + // Maximum 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 = static_cast<sal_uInt64 *>(__builtin_alloca( (nParams + 3) * sizeof(sal_uInt64) )); + sal_uInt64 *pStackStart = pStack; + + sal_uInt64 pGPR[x86_64::MAX_GPR_REGS]; + sal_uInt32 nGPR = 0; + + double pFPR[x86_64::MAX_SSE_REGS]; + sal_uInt32 nFPR = 0; + + // Return + typelib_TypeDescription * pReturnTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); + assert(pReturnTypeDescr); + + void * pCppReturn = nullptr; // if != 0 && != pUnoReturn, needs reconversion (see below) + + bool bSimpleReturn = true; + if ( pReturnTypeDescr ) + { + if ( x86_64::return_in_hidden_param( pReturnTypeRef ) ) + bSimpleReturn = false; + + if ( bSimpleReturn ) + pCppReturn = pUnoReturn; // direct way for simple types + else + { + // complex return via ptr + pCppReturn = bridges::cpp_uno::shared::relatesToInterfaceType( pReturnTypeDescr )? + __builtin_alloca( pReturnTypeDescr->nSize ) : pUnoReturn; + INSERT_INT64( &pCppReturn, nGPR, pGPR, pStack ); + } + } + + // Push "this" pointer + void * pAdjustedThisPtr = reinterpret_cast< void ** >( pThis->getCppI() ) + aVtableSlot.offset; + INSERT_INT64( &pAdjustedThisPtr, nGPR, pGPR, pStack ); + + // Args + void ** pCppArgs = static_cast<void **>(alloca( 3 * sizeof(void *) * nParams )); + // Indices of values this have to be converted (interface conversion cpp<=>uno) + sal_Int32 * pTempIndices = reinterpret_cast<sal_Int32 *>(pCppArgs + nParams); + // Type descriptions for reconversions + typelib_TypeDescription ** ppTempParamTypeDescr = reinterpret_cast<typelib_TypeDescription **>(pCppArgs + (2 * nParams)); + + sal_Int32 nTempIndices = 0; + + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + const typelib_MethodParameter & rParam = pParams[nPos]; + typelib_TypeDescription * pParamTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); + + if (!rParam.bOut && bridges::cpp_uno::shared::isSimpleType( pParamTypeDescr )) + { + uno_copyAndConvertData( pCppArgs[nPos] = alloca( 8 ), pUnoArgs[nPos], pParamTypeDescr, + pThis->getBridge()->getUno2Cpp() ); + + switch (pParamTypeDescr->eTypeClass) + { + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + INSERT_INT64( pCppArgs[nPos], nGPR, pGPR, pStack ); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_ENUM: + INSERT_INT32( pCppArgs[nPos], nGPR, pGPR, pStack ); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_UNSIGNED_SHORT: + INSERT_INT16( pCppArgs[nPos], nGPR, pGPR, pStack ); + break; + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + INSERT_INT8( pCppArgs[nPos], nGPR, pGPR, pStack ); + break; + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + INSERT_FLOAT_DOUBLE( pCppArgs[nPos], nFPR, pFPR, pStack ); + break; + default: + break; + } + + // no longer needed + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + else // ptr to complex value | ref + { + if (! rParam.bIn) // is pure out + { + // cpp out is constructed mem, uno out is not! + uno_constructData( + pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ), + pParamTypeDescr ); + pTempIndices[nTempIndices] = nPos; // default constructed for cpp call + // will be released at reconversion + ppTempParamTypeDescr[nTempIndices++] = pParamTypeDescr; + } + // is in/inout + else if (bridges::cpp_uno::shared::relatesToInterfaceType( pParamTypeDescr )) + { + uno_copyAndConvertData( + pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ), + pUnoArgs[nPos], pParamTypeDescr, pThis->getBridge()->getUno2Cpp() ); + + pTempIndices[nTempIndices] = nPos; // has to be reconverted + // will be released at reconversion + ppTempParamTypeDescr[nTempIndices++] = pParamTypeDescr; + } + else // direct way + { + pCppArgs[nPos] = pUnoArgs[nPos]; + // no longer needed + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + INSERT_INT64( &(pCppArgs[nPos]), nGPR, pGPR, pStack ); + } + } + + try + { + try { + CPPU_CURRENT_NAMESPACE::callVirtualMethod( + pAdjustedThisPtr, aVtableSlot.index, + pCppReturn, pReturnTypeRef, bSimpleReturn, + pStackStart, ( pStack - pStackStart ), + pGPR, pFPR ); + } catch (const Exception &) { + throw; + } catch (const std::exception & e) { + throw RuntimeException( + "C++ code threw " + o3tl::runtimeToOUString(typeid(e).name()) + + ": " + o3tl::runtimeToOUString(e.what())); + } catch (...) { + throw RuntimeException("C++ code threw unknown exception"); + } + + *ppUnoExc = nullptr; + + // reconvert temporary params + for ( ; nTempIndices--; ) + { + sal_Int32 nIndex = pTempIndices[nTempIndices]; + typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndices]; + + if (pParams[nIndex].bIn) + { + if (pParams[nIndex].bOut) // inout + { + uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, nullptr ); // destroy uno value + uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr, + pThis->getBridge()->getCpp2Uno() ); + } + } + else // pure out + { + uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr, + pThis->getBridge()->getCpp2Uno() ); + } + // destroy temp cpp param => cpp: every param was constructed + uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); + + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + // return value + if (pCppReturn && pUnoReturn != pCppReturn) + { + uno_copyAndConvertData( pUnoReturn, pCppReturn, pReturnTypeDescr, + pThis->getBridge()->getCpp2Uno() ); + uno_destructData( pCppReturn, pReturnTypeDescr, cpp_release ); + } + } + catch (...) + { + // fill uno exception + CPPU_CURRENT_NAMESPACE::fillUnoException(*ppUnoExc, pThis->getBridge()->getCpp2Uno()); + + // temporary params + for ( ; nTempIndices--; ) + { + sal_Int32 nIndex = pTempIndices[nTempIndices]; + // destroy temp cpp param => cpp: every param was constructed + uno_destructData( pCppArgs[nIndex], ppTempParamTypeDescr[nTempIndices], cpp_release ); + TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndices] ); + } + // return type + if (pReturnTypeDescr) + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + } +} + + +namespace bridges::cpp_uno::shared { + +void unoInterfaceProxyDispatch( + uno_Interface * pUnoI, const typelib_TypeDescription * pMemberDescr, + void * pReturn, void * pArgs[], uno_Any ** ppException ) +{ + // is my surrogate + bridges::cpp_uno::shared::UnoInterfaceProxy * pThis + = static_cast< bridges::cpp_uno::shared::UnoInterfaceProxy * >(pUnoI); +#if OSL_DEBUG_LEVEL > 0 + typelib_InterfaceTypeDescription * pTypeDescr = pThis->pTypeDescr; +#endif + + switch (pMemberDescr->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { +#if OSL_DEBUG_LEVEL > 0 + // determine vtable call index + sal_Int32 nMemberPos = reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberDescr)->nPosition; + assert(nMemberPos < pTypeDescr->nAllMembers); +#endif + VtableSlot aVtableSlot( + getVtableSlot( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription const * >( + pMemberDescr))); + + if (pReturn) + { + // dependent dispatch + cpp_call( + pThis, aVtableSlot, + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>(pMemberDescr)->pAttributeTypeRef, + 0, nullptr, // no params + pReturn, pArgs, ppException ); + } + else + { + // is SET + typelib_MethodParameter aParam; + aParam.pTypeRef = + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>(pMemberDescr)->pAttributeTypeRef; + aParam.bIn = true; + aParam.bOut = false; + + typelib_TypeDescriptionReference * pReturnTypeRef = nullptr; + OUString aVoidName("void"); + typelib_typedescriptionreference_new( + &pReturnTypeRef, typelib_TypeClass_VOID, aVoidName.pData ); + + // dependent dispatch + aVtableSlot.index += 1; // get, then set method + cpp_call( + pThis, aVtableSlot, // get, then set method + pReturnTypeRef, + 1, &aParam, + pReturn, pArgs, ppException ); + + typelib_typedescriptionreference_release( pReturnTypeRef ); + } + + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { +#if OSL_DEBUG_LEVEL > 0 + // determine vtable call index + sal_Int32 nMemberPos = reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberDescr)->nPosition; + assert(nMemberPos < pTypeDescr->nAllMembers); +#endif + VtableSlot aVtableSlot( + getVtableSlot( + reinterpret_cast< + typelib_InterfaceMethodTypeDescription const * >( + pMemberDescr))); + + switch (aVtableSlot.index) + { + // standard calls + case 1: // acquire uno interface + (*pUnoI->acquire)( pUnoI ); + *ppException = nullptr; + break; + case 2: // release uno interface + (*pUnoI->release)( pUnoI ); + *ppException = nullptr; + break; + case 0: // queryInterface() opt + { + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, static_cast< Type * >( pArgs[0] )->getTypeLibType() ); + if (pTD) + { + uno_Interface * pInterface = nullptr; + (*pThis->getBridge()->getUnoEnv()->getRegisteredInterface)( + pThis->getBridge()->getUnoEnv(), + reinterpret_cast<void **>(&pInterface), pThis->oid.pData, reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD) ); + + if (pInterface) + { + ::uno_any_construct( + static_cast< uno_Any * >( pReturn ), + &pInterface, pTD, nullptr ); + (*pInterface->release)( pInterface ); + TYPELIB_DANGER_RELEASE( pTD ); + *ppException = nullptr; + break; + } + TYPELIB_DANGER_RELEASE( pTD ); + } + [[fallthrough]]; // else perform queryInterface() + } + default: + // dependent dispatch + cpp_call( + pThis, aVtableSlot, + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberDescr)->pReturnTypeRef, + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberDescr)->nParams, + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberDescr)->pParams, + pReturn, pArgs, ppException ); + } + break; + } + default: + { + ::com::sun::star::uno::RuntimeException aExc( + "illegal member type description!", + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >() ); + + Type const & rExcType = cppu::UnoType<decltype(aExc)>::get(); + // binary identical null reference + ::uno_type_any_construct( *ppException, &aExc, rExcType.getTypeLibType(), nullptr ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |