From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- bridges/source/cpp_uno/msvc_win32_x86-64/call.asm | 112 +++++ bridges/source/cpp_uno/msvc_win32_x86-64/call.hxx | 29 ++ .../source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx | 264 ++++++++++++ .../source/cpp_uno/msvc_win32_x86-64/except.cxx | 465 +++++++++++++++++++++ .../source/cpp_uno/msvc_win32_x86-64/uno2cpp.cxx | 454 ++++++++++++++++++++ 5 files changed, 1324 insertions(+) create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/call.asm create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/call.hxx create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/uno2cpp.cxx (limited to 'bridges/source/cpp_uno/msvc_win32_x86-64') diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/call.asm b/bridges/source/cpp_uno/msvc_win32_x86-64/call.asm new file mode 100644 index 000000000..fc12d6873 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/call.asm @@ -0,0 +1,112 @@ +; -*- Mode: text; tab-width: 8; indent-tabs-mode: nil comment-column: 44; comment-start: ";; " comment-start-skip: ";; *" -*- + +;; +;; 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 is the function jumped to from the trampoline generated by +;; codeSnippet() in cpp2uno.cxx. Here we call cpp_vtable_call() which +;; then calls the actual UNO function. + +;; The code snippet generated is called from "normal" C++ code which +;; has no idea that it is calling dynamically generated code. + +;; The generated short code snippet is not covered by any function +;; table and unwind info, but that doesn't matter, as the instructions +;; in it are not really going to cause any exception. Once it jumps +;; here it is covered by a function table, and the calls further down +;; through cpp_vtable_call() can be unwound cleanly. + +;; This is in a separate file for x86-64 as MSVC doesn't have in-line +;; assembly for x64. + +;; Random web links and other documentation about low-level +;; implementation details for the C++/UNO bridge on x64 Windows kept +;; here: + +;; Caolan's "Lazy Hackers Guide To Porting" is useful: +;; http://wiki.openoffice.org/wiki/Lazy_Hackers_Guide_To_Porting + +;; As for details about the x64 Windows calling convention, register +;; usage, stack usage, exception handling etc, the official +;; documentation (?) on MSDN is a bit fragmented and split up into a +;; needlessly large number of short pages. But still: +;; http://msdn.microsoft.com/en-us/library/7kcdt6fy%28v=VS.90%29.aspx + +;; Also see Raymond Chen's blog post: +;; http://blogs.msdn.com/b/oldnewthing/archive/2004/01/14/58579.aspx + +;; This one is actually more readable: "Improving Automated Analysis +;; of Windows x64 Binaries": http://www.uninformed.org/?v=4&a=1 + +;; This one has a mass of information about different architectures +;; and compilers, and contains some details about the x64 Windows +;; calling convention in particular that Microsoft doesn't mention +;; above: +;; http://www.agner.org/optimize/calling_conventions.pdf + +;; Random interesting discussion threads: +;; http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/300bd6d3-9381-4d2d-8129-e48b392c05d8 + +;; Ken Johnson's blog http://www.nynaeve.net/ has much interesting +;; information, for instance: +;; http://www.nynaeve.net/?p=11 + +typelib_TypeClass_FLOAT equ 10 +typelib_TypeClass_DOUBLE equ 11 + +extern cpp_vtable_call: proc + +.code + +privateSnippetExecutor proc frame + + ;; Make stack frame. Re-align RSP at 16 bytes. We need just one + ;; qword of stack for our own purposes: Where cpp_vtable_call() + ;; will store the return value of the UNO callee. But we of course + ;; must also allocate space for the functions we call (i.e., just + ;; cpp_vtable_call()) to spill their register parameters. + + sub rsp, 40 + .allocstack (40) + .endprolog + + ;; Call cpp_vtable_call() with 2 parameters: + + ;; 1 (rcx): nOffsetAndIndex (already put there in code generated by codeSnippet) + ;; 2 (rdx): pointer to where to store return value, followed by our + ;; return address (uninteresting to cpp_vtable_call()), followed + ;; by our spilled register parameters, as stored above, followed + ;; by the rest of our parameters, if any. + + lea rdx, 32[rsp] + + call cpp_vtable_call + + ;; cpp_vtable_call() returns the typelib_TypeClass type of the + ;; return value of the called UNO function + + cmp rax, typelib_TypeClass_FLOAT + je Lfloat + + cmp rax, typelib_TypeClass_DOUBLE + je Lfloat + + mov rax, qword ptr 32[rsp] + jmp Lepilogue + +Lfloat: + movsd xmm0, qword ptr 32[rsp] + +Lepilogue: + add rsp, 40 + ret +privateSnippetExecutor endp + +end + +; vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/call.hxx b/bridges/source/cpp_uno/msvc_win32_x86-64/call.hxx new file mode 100644 index 000000000..393f8fcb4 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/call.hxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 + +#include +#include + +extern "C" typelib_TypeClass cpp_vtable_call(sal_Int64 nOffsetAndIndex, void** pStack); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx b/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx new file mode 100644 index 000000000..159eb99a4 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx @@ -0,0 +1,264 @@ +/* -*- 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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "call.hxx" +#include +#include + +using namespace ::com::sun::star; + +extern "C" typelib_TypeClass cpp_vtable_call(sal_Int64 nOffsetAndIndex, void ** pCallStack) +{ + sal_Int32 nFunctionIndex = (nOffsetAndIndex & 0xFFFFFFFF); + sal_Int32 nVtableOffset = ((nOffsetAndIndex >> 32) & 0xFFFFFFFF); + return cpp_mediate(pCallStack, nFunctionIndex, nVtableOffset, nullptr); +} + +int const codeSnippetSize = 48; + +namespace { + +typedef enum { REGPARAM_INT, REGPARAM_FLT } RegParamKind; + +} + +extern "C" char privateSnippetExecutor; + +// This function generates the code that acts as a proxy for the UNO function to be called. +// The generated code does the following: +// - Spills register parameters on stack +// - Loads functionIndex and vtableOffset into scratch registers +// - Jumps to privateSnippetExecutor + +static unsigned char * codeSnippet( + unsigned char * code, + RegParamKind param_kind[4], + sal_Int32 nFunctionIndex, + sal_Int32 nVtableOffset ) +{ + sal_uInt64 nOffsetAndIndex = ( static_cast(nVtableOffset) << 32 ) | static_cast(nFunctionIndex); + unsigned char *p = code; + + // Spill parameters + if (param_kind[0] == REGPARAM_INT) + { + // mov qword ptr 8[rsp], rcx + *p++ = 0x48; *p++ = 0x89; *p++ = 0x4C; *p++ = 0x24; *p++ = 0x08; + } + else + { + // movsd qword ptr 8[rsp], xmm0 + *p++ = 0xF2; *p++ = 0x0F; *p++ = 0x11; *p++ = 0x44; *p++ = 0x24; *p++ = 0x08; + } + if ( param_kind[1] == REGPARAM_INT ) + { + // mov qword ptr 16[rsp], rdx + *p++ = 0x48; *p++ = 0x89; *p++ = 0x54; *p++ = 0x24; *p++ = 0x10; + } + else + { + // movsd qword ptr 16[rsp], xmm1 + *p++ = 0xF2; *p++ = 0x0F; *p++ = 0x11; *p++ = 0x4C; *p++ = 0x24; *p++ = 0x10; + } + if ( param_kind[2] == REGPARAM_INT ) + { + // mov qword ptr 24[rsp], r8 + *p++ = 0x4C; *p++ = 0x89; *p++ = 0x44; *p++ = 0x24; *p++ = 0x18; + } + else + { + // movsd qword ptr 24[rsp], xmm2 + *p++ = 0xF2; *p++ = 0x0F; *p++ = 0x11; *p++ = 0x54; *p++ = 0x24; *p++ = 0x18; + } + if ( param_kind[3] == REGPARAM_INT ) + { + // mov qword ptr 32[rsp], r9 + *p++ = 0x4C;*p++ = 0x89; *p++ = 0x4C; *p++ = 0x24; *p++ = 0x20; + } + else + { + // movsd qword ptr 32[rsp], xmm3 + *p++ = 0xF2; *p++ = 0x0F; *p++ = 0x11; *p++ = 0x5C; *p++ = 0x24; *p++ = 0x20; + } + + // mov rcx, nOffsetAndIndex + *p++ = 0x48; *p++ = 0xB9; + *reinterpret_cast(p) = nOffsetAndIndex; p += 8; + + // mov r11, privateSnippetExecutor + *p++ = 0x49; *p++ = 0xBB; + *reinterpret_cast(p) = &privateSnippetExecutor; p += 8; + + // jmp r11 + *p++ = 0x41; *p++ = 0xFF; *p++ = 0xE3; + + assert(p < code + codeSnippetSize); + + 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) + 1; +} + +std::size_t bridges::cpp_uno::shared::VtableFactory::getBlockSize( + sal_Int32 slotCount) +{ + return (slotCount + 1) * 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 *) +{ + struct Rtti { + sal_Int32 n0, n1, n2; + type_info * rtti; + Rtti(): + n0(0), n1(0), n2(0), + rtti(RTTInfos::get("com.sun.star.uno.XInterface")) + {} + }; + static Rtti rtti; + + Slot * slots = mapBlockToVtable(block); + slots[-1].fn = &rtti; + 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 ) +{ + (*slots) -= functionCount; + Slot * s = *slots; + + for (int member = 0; member < type->nMembers; ++member) { + typelib_TypeDescription * pTD = nullptr; + + TYPELIB_DANGER_GET( &pTD, type->ppMembers[ member ] ); + assert(pTD); + + RegParamKind param_kind[4]; + int nr = 0; + + for (int i = 0; i < 4; ++i) + param_kind[i] = REGPARAM_INT; + + // 'this' + ++nr; + + if ( pTD->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE ) + { + typelib_InterfaceAttributeTypeDescription * pIfaceAttrTD = + reinterpret_cast( pTD ); + + // Getter + + (s++)->fn = code; + code = codeSnippet( code, param_kind, nFunctionOffset++, nVtableOffset ); + if ( ! pIfaceAttrTD->bReadOnly ) + { + typelib_TypeDescription * pAttrTD = nullptr; + TYPELIB_DANGER_GET( &pAttrTD, pIfaceAttrTD->pAttributeTypeRef ); + assert(pAttrTD); + + // Setter + if ( pAttrTD->eTypeClass == typelib_TypeClass_FLOAT || + pAttrTD->eTypeClass == typelib_TypeClass_DOUBLE ) + param_kind[nr++] = REGPARAM_FLT; + + TYPELIB_DANGER_RELEASE( pAttrTD ); + + (s++)->fn = code; + code = codeSnippet( code, param_kind, nFunctionOffset++, nVtableOffset ); + } + } + else if ( pTD->eTypeClass == typelib_TypeClass_INTERFACE_METHOD ) + { + typelib_InterfaceMethodTypeDescription * pMethodTD = + reinterpret_cast( pTD ); + + typelib_TypeDescription * pReturnTD = nullptr; + TYPELIB_DANGER_GET( &pReturnTD, pMethodTD->pReturnTypeRef ); + assert(pReturnTD); + + if ( !bridges::cpp_uno::shared::isSimpleType( pReturnTD ) ) + { + // Return value + ++nr; + } + + for (int param = 0; nr < 4 && param < pMethodTD->nParams; ++param, ++nr) + { + typelib_TypeDescription * pParamTD = nullptr; + + TYPELIB_DANGER_GET( &pParamTD, pMethodTD->pParams[param].pTypeRef ); + assert(pParamTD); + + if ( pParamTD->eTypeClass == typelib_TypeClass_FLOAT || + pParamTD->eTypeClass == typelib_TypeClass_DOUBLE ) + param_kind[nr] = REGPARAM_FLT; + + TYPELIB_DANGER_RELEASE( pParamTD ); + } + (s++)->fn = code; + code = codeSnippet( code, param_kind, nFunctionOffset++, nVtableOffset ); + + TYPELIB_DANGER_RELEASE( pReturnTD ); + } + else + assert(false); + + TYPELIB_DANGER_RELEASE( pTD ); + } + return code; +} + +void bridges::cpp_uno::shared::VtableFactory::flushCode( + unsigned char const *, + unsigned char const *) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx b/bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx new file mode 100644 index 000000000..e5836dd11 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx @@ -0,0 +1,465 @@ +/* -*- 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 . + */ + +// Interesting info can be found in: + +// MSDN, obviously + +// http://www.osronline.com/article.cfm?article=469 + +// ONTL, "Open NT Native Template Library", a C++ STL-like library +// that can be used even when writing Windows drivers. This is some +// quite badass code. The author has done impressive heavy spelunking +// of MSVCR structures. http://code.google.com/p/ontl/ + +// Geoff Chappell's pages: +// http://www.geoffchappell.com/studies/msvc/language/index.htm + +// The below is from ONTL's ntl/nt/exception.hxx, cleaned up to keep just the _M_X64 parts: + +#if 0 + +/* This information until the corresponding '#endif // 0' is covered + * by ONTL's license, which is said to be the "zlib/libpng license" + * below, which as far as I see is permissive enough to allow this + * information to be included here in this source file. Note that no + * actual code from ONTL below gets compiled into the object file. + */ + +/* + * Copyright (c) 2011 (The ONTL main + * developer(s) don't tell their real name(s) on the ONTL site.) + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + */ + +typedef uint32_t rva_t; + +///\note the calling convention should not matter since this has no arguments +typedef void generic_function_t(); + +struct ptrtomember // _PMD +{ + typedef __w64 int32_t mdiff_t; + mdiff_t member_offset; + mdiff_t vbtable_offset; // -1 if not a virtual base + mdiff_t vdisp_offset; // offset to the displacement value inside the vbtable + + template + T * operator()(T * const thisptr) const + { + uintptr_t tp = reinterpret_cast(thisptr); + uintptr_t ptr = tp + member_offset; + if ( vbtable_offset != -1 ) // !(vbtable_offset < 0) + { + ptr += *reinterpret_cast( static_cast(vdisp_offset + *reinterpret_cast(tp + vbtable_offset)) ) + + vbtable_offset; + } + return reinterpret_cast(ptr); + } +}; + +struct eobject +{ + typedef void (* dtor_ptr )(eobject*); + typedef void (* ctor_ptr )(eobject*, eobject*); + typedef void (* ctor_ptr2)(eobject*, eobject*, int); +}; + +struct catchabletype +{ + /** is simple type */ + uint32_t memmoveable : 1; + /** catchable as reference only */ + uint32_t refonly : 1; + /** class with virtual base */ + uint32_t hasvirtbase : 1; + /** offset to the type descriptor */ + rva_t typeinfo; + + /** catch type instance location within a thrown object */ + ptrtomember thiscast; + /** size of the simple type or offset into buffer of \c this pointer for catch object */ + uint32_t object_size; + + union + { + rva_t copyctor; + rva_t copyctor2; + }; +}; + +#pragma pack(push, 4) +struct catchabletypearray +{ + uint32_t size; + rva_t type[1]; +}; +#pragma pack(pop) + +#pragma pack(push, 4) +struct throwinfo +{ + typedef exception_disposition __cdecl forwardcompathandler_t(...); + + /* 0x00 */ uint32_t econst : 1; + /* 0x00 */ uint32_t evolatile : 1; + /* 0x00 */ uint32_t : 1; + /* 0x00 */ uint32_t e8 : 1; + /* 0x04 */ rva_t exception_dtor; + /* 0x08 */ rva_t forwardcompathandler; + /* 0x0C */ rva_t catchabletypearray; ///< types able to catch the exception. +}; +#pragma pack(pop) + +/// This type represents the catch clause +struct ehandler +{ + // union { uint32_t adjectives; void * ptr; }; + uint32_t isconst : 1; + uint32_t isvolatile : 1; + uint32_t isunaligned : 1;// guess it is not used on x86 + uint32_t isreference : 1; + + uint32_t :27; + uint32_t ishz : 1; + + /** offset to the type descriptor of this catch object */ + /*0x04*/ rva_t typeinfo; // dispType + /*0x08*/ int eobject_bpoffset; // dispCatchObj + /** offset to the catch clause funclet */ + /*0x0C*/ rva_t handler; // dispOfHandler + /*0x10*/ uint32_t frame; // dispFrame +} + +// ___BuildCatchObject +/// 15.3/16 When the exception-declaration specifies a class type, a copy +/// constructor is used to initialize either the object declared +/// in the exception-declaration or, +/// if the exception-declaration does not specify a name, +/// a temporary object of that type. +///\note This is the question may we optimize out the last case. +///\warning If the copy constructor throws an exception, std::unexpected would be called +void + constructcatchobject( + cxxregistration * cxxreg, + const ehandler * const catchblock, + catchabletype * const convertible, + const dispatcher_context* const dispatch + ) + const +{ + _EH_TRACE_ENTER(); + // build helper + __try { + struct typeinfo_t { void* vtbl; void* spare; char name[1]; }; + enum catchable_info { cidefault, cicomplex, civirtual } cinfo = cidefault; + + const typeinfo_t* ti = catchblock->typeinfo ? dispatch->va(catchblock->typeinfo) : NULL; + if(ti && *ti->name && (catchblock->eobject_bpoffset || catchblock->ishz)){ + eobject** objplace = catchblock->ishz + ? reinterpret_cast(cxxreg) + : reinterpret_cast(catchblock->eobject_bpoffset + cxxreg->fp.FramePointers); + if(catchblock->isreference){ + // just ref/pointer + *objplace = adjust_pointer(get_object(), convertible); + }else if(convertible->memmoveable){ + // POD + std::memcpy(objplace, get_object(), convertible->object_size); + if(convertible->object_size == sizeof(void*) && *objplace) + *objplace = adjust_pointer((void*)*objplace, convertible); + }else{ + // if copy ctor exists, call it; binary copy otherwise + if(convertible->copyctor){ + cinfo = convertible->hasvirtbase ? civirtual : cicomplex; + }else{ + std::memcpy(objplace, (const void*)adjust_pointer(get_object(), convertible), convertible->object_size); + } + } + } + // end of build helper + if(cinfo != cidefault){ + eobject* objthis = catchblock->ishz + ? reinterpret_cast(cxxreg) + : reinterpret_cast(catchblock->eobject_bpoffset + cxxreg->fp.FramePointers); + void* copyctor = thrown_va(convertible->copyctor); + eobject* copyarg = adjust_pointer(get_object(), convertible); + if(cinfo == cicomplex) + (eobject::ctor_ptr (copyctor))(objthis, copyarg); + else + (eobject::ctor_ptr2(copyctor))(objthis, copyarg, 1); + } + } + __except(cxxregistration::unwindfilter(static_cast(_exception_code()))) + { + nt::exception::inconsistency(); + } + _EH_TRACE_LEAVE(); +} + +#endif // 0 + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#pragma pack(push, 8) + +using namespace ::com::sun::star; + +static void* __cdecl copyConstruct(void* pExcThis, void* pSource, + typelib_TypeDescription* pTD) noexcept +{ + ::uno_copyData(pExcThis, pSource, pTD, uno::cpp_acquire); + return pExcThis; +} + +static void* __cdecl destruct(void* pExcThis, typelib_TypeDescription* pTD) noexcept +{ + ::uno_destructData(pExcThis, pTD, uno::cpp_release); + return pExcThis; +} + +const int codeSnippetSize = 40; + +static void GenerateConstructorTrampoline(unsigned char* code, + typelib_TypeDescription* pTD) noexcept +{ + unsigned char* p = code; + + // mov r8, pTD + *p++ = 0x49; + *p++ = 0xB8; + *reinterpret_cast(p) = pTD; + p += 8; + + // mov r11, copyConstruct + *p++ = 0x49; + *p++ = 0xBB; + *reinterpret_cast(p) = reinterpret_cast(©Construct); + p += 8; + + // jmp r11 + *p++ = 0x41; + *p++ = 0xFF; + *p++ = 0xE3; + + assert(p < code + codeSnippetSize); +} + +static void GenerateDestructorTrampoline(unsigned char* code, typelib_TypeDescription* pTD) noexcept +{ + unsigned char* p = code; + + // mov rdx, pTD + *p++ = 0x48; + *p++ = 0xBA; + *reinterpret_cast(p) = pTD; + p += 8; + + // mov r11, destruct + *p++ = 0x49; + *p++ = 0xBB; + *reinterpret_cast(p) = reinterpret_cast(&destruct); + p += 8; + + // jmp r11 + *p++ = 0x41; + *p++ = 0xFF; + *p++ = 0xE3; + + assert(p < code + codeSnippetSize); +} + +ExceptionType::ExceptionType(unsigned char* pCode, sal_uInt64 pCodeBase, + typelib_TypeDescription* pTD) noexcept + : _n0(0) + , _n1(0) + , _n2(-1) + , _n3(0) + , _n4(pTD->nSize) + , exc_type_info(nullptr, "") +{ + // As _n0 is always initialized to zero, that means the + // hasvirtbase flag (see the ONTL catchabletype struct) is + // off, and thus the copyctor is of the ctor_ptr kind. + + int len; + type_info* pRTTI = RTTInfos::get(pTD->pTypeName, &len); + + memcpy(static_cast(&exc_type_info), static_cast(pRTTI), len); + _pTypeInfo = static_cast(reinterpret_cast(&exc_type_info) - pCodeBase); + GenerateConstructorTrampoline(pCode, pTD); + + assert(pCodeBase <= reinterpret_cast(pCode) + && (reinterpret_cast(pCode) - pCodeBase < 0x100000000)); + _pCopyCtor = static_cast(reinterpret_cast(pCode) - pCodeBase); +} + +/* Rewrite of 32-Bit-Code to work under 64 Bit: +* To use the 32 Bit offset values in the ExceptionType we have to +* allocate a single allocation block and use it for all code and date +* all offsets inside this area are guaranteed to be in 32 bit address range. +* So we have to calc total memory allocation size for D-tor, C-Tors, +* ExceptionType and type_info. ExceptionType is allocated via placement new +* to locate everything inside our mem block. +* There is one caveat: Struct type_info is kept in +* a map and was referenced from class ExceptionType. Therefore type_info now +* is also member of ExceptionType and can be referenced via 32 bit offset. +*/ + +RaiseInfo::RaiseInfo(typelib_TypeDescription* pTD) noexcept + : _n0(0) + , _n2(0) + , _pTD(pTD) +{ + typelib_CompoundTypeDescription* pCompTD; + + // Count how many trampolines we need + int codeSize = codeSnippetSize; + + // Info count + int nLen = 0; + for (pCompTD = reinterpret_cast(pTD); pCompTD; + pCompTD = pCompTD->pBaseTypeDescription) + { + ++nLen; + codeSize += codeSnippetSize; + } + + // Array with size (4) and all _pTypeInfo (4*nLen) + int typeInfoArraySize = 4 + 4 * nLen; + + // 2.Pass: Get the total needed memory for class ExceptionType + // (with embedded type_info) and keep the sizes for each instance + // is stored in allocated int array + auto exceptionTypeSizeArray = std::make_unique(nLen); + + nLen = 0; + for (pCompTD = reinterpret_cast(pTD); pCompTD; + pCompTD = pCompTD->pBaseTypeDescription) + { + int typeInfoLen; + RTTInfos::get(pCompTD->aBase.pTypeName, &typeInfoLen); + // Mem has to be on 4-byte Boundary + if (typeInfoLen % 4 != 0) + { + int n = typeInfoLen / 4; + n++; + typeInfoLen = n * 4; + } + exceptionTypeSizeArray[nLen++] = typeInfoLen + sizeof(ExceptionType); + } + + // Total ExceptionType related mem + int excTypeAddLen = 0; + for (int i = 0; i < nLen; i++) + { + excTypeAddLen += exceptionTypeSizeArray[i]; + } + + // Allocate mem for code and all dynamic data in one chunk to guarantee + // 32 bit offsets + const int totalSize = codeSize + typeInfoArraySize + excTypeAddLen; + unsigned char* pCode = _code = static_cast(std::malloc(totalSize)); + int pCodeOffset = 0; + + // New base of types array, starts after Trampoline D-Tor / C-Tors + DWORD* types = reinterpret_cast(pCode + codeSize); + + // New base of ExceptionType array, starts after types array + unsigned char* etMem = pCode + codeSize + typeInfoArraySize; + int etMemOffset = 0; + + _codeBase = reinterpret_cast(pCode) + & ~static_cast(ExceptionInfos::allocationGranularity - 1); + + DWORD old_protect; + bool success = VirtualProtect(pCode, codeSize, PAGE_EXECUTE_READWRITE, &old_protect); + (void)success; + assert(success && "VirtualProtect() failed!"); + + ::typelib_typedescription_acquire(pTD); + + // Fill pCode with D-Tor code + GenerateDestructorTrampoline(pCode, pTD); + _pDtor = static_cast(reinterpret_cast(pCode) - _codeBase); + pCodeOffset += codeSnippetSize; + + // Info count accompanied by type info ptrs: type, base type, base base type, ... + // Keep offset of types_array + _types = static_cast(reinterpret_cast(types) - _codeBase); + // Fill types: (nLen, _offset to ExceptionType1, ...ExceptionType2, ...) + types[0] = nLen; + + int nPos = 1; + for (pCompTD = reinterpret_cast(pTD); pCompTD; + pCompTD = pCompTD->pBaseTypeDescription) + { + // Create instance in mem block with placement new + ExceptionType* et = new (etMem + etMemOffset) ExceptionType( + pCode + pCodeOffset, _codeBase, reinterpret_cast(pCompTD)); + + // Next trampoline entry offset + pCodeOffset += codeSnippetSize; + // Next ExceptionType placement offset + etMemOffset += exceptionTypeSizeArray[nPos - 1]; + + // Keep offset of addresses of ET for D-Tor call in ~RaiseInfo + types[nPos++] = static_cast(reinterpret_cast(et) - _codeBase); + } + // Final check: end of address calculation must be end of mem + assert(etMem + etMemOffset == pCode + totalSize); +} + +#pragma pack(pop) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/uno2cpp.cxx b/bridges/source/cpp_uno/msvc_win32_x86-64/uno2cpp.cxx new file mode 100644 index 000000000..d5df4a161 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/uno2cpp.cxx @@ -0,0 +1,454 @@ +/* -*- 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 + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#if OSL_DEBUG_LEVEL > 1 +#include +#endif + +using namespace ::com::sun::star; + +namespace +{ + +bool 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 ) noexcept +{ + const int MAXPARAMS = 32; + + if ( nParams > MAXPARAMS ) + { + // We have a hard limit on the number of parameters so that we + // don't need any assembler code here but can call the + // function using normal C++. + + return false; + } + + // Table with this pointer, optional complex return value ptr, and the parameters + union { + sal_Int64 i; + void *p; + double d; + } aCppArgs[MAXPARAMS+2], uRetVal; + int nCppParamIndex = 0; + + // return type + typelib_TypeDescription * pReturnTD = nullptr; + TYPELIB_DANGER_GET( &pReturnTD, pReturnTypeRef ); + assert(pReturnTD); + + // 'this' + void * pAdjustedThisPtr = reinterpret_cast( pThis->getCppI() ) + aVtableSlot.offset; + aCppArgs[nCppParamIndex++].p = pAdjustedThisPtr; + + enum class ReturnKind { Void, Simple, Complex, ComplexConvert }; + ReturnKind retKind; + if (pUnoReturn == nullptr) { + retKind = ReturnKind::Void; + } else { + assert(pReturnTD != nullptr); + if (bridges::cpp_uno::shared::isSimpleType(pReturnTD)) { + retKind = ReturnKind::Simple; + } else if (bridges::cpp_uno::shared::relatesToInterfaceType(pReturnTD)) + { + retKind = ReturnKind::ComplexConvert; + aCppArgs[nCppParamIndex++].p = alloca(pReturnTD->nSize); + } else { + retKind = ReturnKind::Complex; + aCppArgs[nCppParamIndex++].p = pUnoReturn; + } + } + + // indexes of values this have to be converted (interface conversion C++<=>UNO) + int pTempCppIndexes[MAXPARAMS]; + int pTempIndexes[MAXPARAMS]; + int nTempIndexes = 0; + + // type descriptions for reconversions + typelib_TypeDescription *pTempParamTD[MAXPARAMS]; + + for (int nPos = 0; nPos < nParams; ++nPos, ++nCppParamIndex) + { + const typelib_MethodParameter & rParam = pParams[nPos]; + typelib_TypeDescription * pParamTD = nullptr; + TYPELIB_DANGER_GET( &pParamTD, rParam.pTypeRef ); + + if (!rParam.bOut && bridges::cpp_uno::shared::isSimpleType(pParamTD)) + { + ::uno_copyAndConvertData( + &aCppArgs[nCppParamIndex], pUnoArgs[nPos], pParamTD, + pThis->getBridge()->getUno2Cpp() ); + + // no longer needed + TYPELIB_DANGER_RELEASE( pParamTD ); + } + else // ptr to complex value | ref + { + if (!rParam.bIn) // Is pure out + { + // C++ out is constructed mem, UNO out is not! + ::uno_constructData( + aCppArgs[nCppParamIndex].p = alloca( pParamTD->nSize ), + pParamTD ); + + pTempCppIndexes[nTempIndexes] = nCppParamIndex; + + // default constructed for C++ call + pTempIndexes[nTempIndexes] = nPos; + + // will be released at reconversion + pTempParamTD[nTempIndexes++] = pParamTD; + } + // is in/inout + else if (bridges::cpp_uno::shared::relatesToInterfaceType(pParamTD)) + { + ::uno_copyAndConvertData( + aCppArgs[nCppParamIndex].p = alloca( pParamTD->nSize ), + pUnoArgs[nPos], pParamTD, + pThis->getBridge()->getUno2Cpp() ); + + pTempCppIndexes[nTempIndexes] = nCppParamIndex; + + // has to be reconverted + pTempIndexes[nTempIndexes] = nPos; + + // will be released at reconversion + pTempParamTD[nTempIndexes++] = pParamTD; + } + else // direct way + { + aCppArgs[nCppParamIndex].p = pUnoArgs[nPos]; + // no longer needed + TYPELIB_DANGER_RELEASE( pParamTD ); + } + } + } + + __try + { + // The first real parameter is always 'this'. + + // The Windows x64 calling convention is very regular and + // elegant (even if perhaps then slightly slower than the + // Linux x64 one): The first four parameters, never more, are + // passed in registers, as long as they are a qword in size + // or less. (If larger, a pointer to a temp copy is passed, so + // it's equivalent anyway.) Floating point values are passed + // in XMM0..3 registers, others in RCX, RDX, R8, R9. + + // Now, the nice thing for us is that when calling varargs + // functions, floating-point parameters among the four first + // ones are always passed *both* in an XMM and integer + // register. So we don't need to bother here calling the + // method different ways depending on what types of parameters + // it actually expects. We just pretend parameters 3..4 are + // doubles, and they will be passed both in XMM and integer + // registers, and the callee will find them where it + // expects. (The callee is not actually varargs, of course.) + + sal_Int64 (*pIMethod)(sal_Int64, ...) = + reinterpret_cast( + (*static_cast(pAdjustedThisPtr))[aVtableSlot.index]); + + double (*pFMethod)(sal_Int64, ...) = + reinterpret_cast( + (*static_cast(pAdjustedThisPtr))[aVtableSlot.index]); + + // Pass parameters 2..4 as if it was a floating-point value so + // that it gets put in both XMM and integer registers per the + // calling convention. It doesn't matter if it actually is a + // fp or not. + + if ( pReturnTD && + (pReturnTD->eTypeClass == typelib_TypeClass_FLOAT || + pReturnTD->eTypeClass == typelib_TypeClass_DOUBLE) ) + uRetVal.d = + pFMethod (aCppArgs[0].i, aCppArgs[1].d, aCppArgs[2].d, aCppArgs[3].d, + aCppArgs[4].i, aCppArgs[5].i, aCppArgs[6].i, aCppArgs[7].i, + aCppArgs[8].i, aCppArgs[9].i, aCppArgs[10].i, aCppArgs[11].i, + aCppArgs[12].i, aCppArgs[13].i, aCppArgs[14].i, aCppArgs[15].i, + aCppArgs[16].i, aCppArgs[17].i, aCppArgs[18].i, aCppArgs[19].i, + aCppArgs[20].i, aCppArgs[21].i, aCppArgs[22].i, aCppArgs[23].i, + aCppArgs[24].i, aCppArgs[25].i, aCppArgs[26].i, aCppArgs[27].i, + aCppArgs[28].i, aCppArgs[29].i, aCppArgs[30].i, aCppArgs[31].i ); + else + uRetVal.i = + pIMethod (aCppArgs[0].i, aCppArgs[1].d, aCppArgs[2].d, aCppArgs[3].d, + aCppArgs[4].i, aCppArgs[5].i, aCppArgs[6].i, aCppArgs[7].i, + aCppArgs[8].i, aCppArgs[9].i, aCppArgs[10].i, aCppArgs[11].i, + aCppArgs[12].i, aCppArgs[13].i, aCppArgs[14].i, aCppArgs[15].i, + aCppArgs[16].i, aCppArgs[17].i, aCppArgs[18].i, aCppArgs[19].i, + aCppArgs[20].i, aCppArgs[21].i, aCppArgs[22].i, aCppArgs[23].i, + aCppArgs[24].i, aCppArgs[25].i, aCppArgs[26].i, aCppArgs[27].i, + aCppArgs[28].i, aCppArgs[29].i, aCppArgs[30].i, aCppArgs[31].i ); + } + __except (msvc_filterCppException( + GetExceptionInformation(), + *ppUnoExc, pThis->getBridge()->getCpp2Uno() )) + { + // *ppUnoExc was constructed by filter function + // temporary params + while (nTempIndexes--) + { + int nCppIndex = pTempCppIndexes[nTempIndexes]; + // destroy temp C++ param => C++: every param was constructed + ::uno_destructData( + aCppArgs[nCppIndex].p, pTempParamTD[nTempIndexes], + uno::cpp_release ); + TYPELIB_DANGER_RELEASE( pTempParamTD[nTempIndexes] ); + } + + // return type + if (pReturnTD) + TYPELIB_DANGER_RELEASE( pReturnTD ); + + return true; + } + + // NO exception occurred + *ppUnoExc = nullptr; + + // reconvert temporary params + while (nTempIndexes--) + { + int nCppIndex = pTempCppIndexes[nTempIndexes]; + int nIndex = pTempIndexes[nTempIndexes]; + typelib_TypeDescription * pParamTD = + pTempParamTD[nTempIndexes]; + + if (pParams[nIndex].bIn) + { + if (pParams[nIndex].bOut) // inout + { + ::uno_destructData( + pUnoArgs[nIndex], pParamTD, nullptr ); // destroy UNO value + ::uno_copyAndConvertData( + pUnoArgs[nIndex], aCppArgs[nCppIndex].p, pParamTD, + pThis->getBridge()->getCpp2Uno() ); + } + } + else // pure out + { + ::uno_copyAndConvertData( + pUnoArgs[nIndex], aCppArgs[nCppIndex].p, pParamTD, + pThis->getBridge()->getCpp2Uno() ); + } + + // destroy temp C++ param => C++: every param was constructed + ::uno_destructData( + aCppArgs[nCppIndex].p, pParamTD, uno::cpp_release ); + + TYPELIB_DANGER_RELEASE( pParamTD ); + } + + // return value + switch (retKind) { + case ReturnKind::Void: + break; + case ReturnKind::Simple: + *static_cast(pUnoReturn) = uRetVal.i; + break; + case ReturnKind::Complex: + assert(uRetVal.p == pUnoReturn); + break; + case ReturnKind::ComplexConvert: + assert(uRetVal.p == aCppArgs[1].p); + ::uno_copyAndConvertData( + pUnoReturn, uRetVal.p, pReturnTD, + pThis->getBridge()->getCpp2Uno() ); + ::uno_destructData( + uRetVal.p, pReturnTD, uno::cpp_release ); + break; + } + + // return type + if ( pReturnTD ) + TYPELIB_DANGER_RELEASE( pReturnTD ); + + return true; +} + +} // namespace + +namespace bridges::cpp_uno::shared { + +void unoInterfaceProxyDispatch( + uno_Interface * pUnoI, + const typelib_TypeDescription * pMemberTD, + 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 (pMemberTD->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { +#if OSL_DEBUG_LEVEL > 0 + // determine vtable call index + sal_Int32 nMemberPos = reinterpret_cast(pMemberTD)->nPosition; + assert(nMemberPos < pTypeDescr->nAllMembers); +#endif + VtableSlot aVtableSlot( + getVtableSlot( + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription const * >( + pMemberTD))); + if ( pReturn ) + { + // is GET + cpp_call( + pThis, aVtableSlot, + reinterpret_cast(pMemberTD)->pAttributeTypeRef, + 0, nullptr, // no params + pReturn, pArgs, ppException ); + } + else + { + // is SET + typelib_MethodParameter aParam; + aParam.pTypeRef = + reinterpret_cast(pMemberTD)->pAttributeTypeRef; + aParam.bIn = true; + aParam.bOut = false; + + typelib_TypeDescriptionReference * pReturnTypeRef = nullptr; + OUString aVoidName("void"); + typelib_typedescriptionreference_new( + &pReturnTypeRef, typelib_TypeClass_VOID, aVoidName.pData ); + + aVtableSlot.index += 1; // get, then set method + cpp_call( + pThis, aVtableSlot, + 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(pMemberTD)->nPosition; + assert(nMemberPos < pTypeDescr->nAllMembers); +#endif + VtableSlot aVtableSlot( + getVtableSlot( + reinterpret_cast< + typelib_InterfaceMethodTypeDescription const * >( + pMemberTD))); + + switch (aVtableSlot.index) + { + 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< uno::Type * >( pArgs[0] )->getTypeLibType() ); + + if ( pTD ) + { + uno_Interface * pInterface = nullptr; + (*pThis->getBridge()->getUnoEnv()->getRegisteredInterface)( + pThis->getBridge()->getUnoEnv(), + reinterpret_cast(&pInterface), pThis->oid.pData, reinterpret_cast(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: + typelib_InterfaceMethodTypeDescription const* pMethodTD + = reinterpret_cast(pMemberTD); + + if (!cpp_call(pThis, aVtableSlot, pMethodTD->pReturnTypeRef, pMethodTD->nParams, + pMethodTD->pParams, pReturn, pArgs, ppException)) + { + uno::RuntimeException aExc( "Too many parameters!" ); + + uno::Type const & rExcType = cppu::UnoType::get(); + ::uno_type_any_construct(*ppException, &aExc, rExcType.getTypeLibType(), nullptr); + } + } + break; + } + default: + { + uno::RuntimeException aExc("Illegal member type description!", uno::Reference()); + + uno::Type const & rExcType = cppu::UnoType::get(); + // binary identical null reference + ::uno_type_any_construct(*ppException, &aExc, rExcType.getTypeLibType(), nullptr); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3