From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- .../source/cpp_uno/msvc_win32_x86-64/except.cxx | 935 +++++++++++++++++++++ 1 file changed, 935 insertions(+) create mode 100644 bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx (limited to 'bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx') 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..daa0426a2 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/except.cxx @@ -0,0 +1,935 @@ +/* -*- 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 +#include "mscx.hxx" +#include + +#pragma pack(push, 8) + +using namespace ::com::sun::star::uno; +using namespace ::std; +using namespace ::osl; + +namespace CPPU_CURRENT_NAMESPACE +{ + static int mscx_getRTTI_len(OUString const & rUNOname); + + +static OUString toUNOname( + OUString const & rRTTIname ) + throw () +{ + OUStringBuffer aRet( 64 ); + OUString aStr( rRTTIname.copy( 4, rRTTIname.getLength()-4-2 ) ); // filter .?AUzzz@yyy@xxx@@ + sal_Int32 nPos = aStr.getLength(); + while (nPos > 0) + { + sal_Int32 n = aStr.lastIndexOf( '@', nPos ); + aRet.append( aStr.copy( n +1, nPos -n -1 ) ); + if (n >= 0) + { + aRet.append( '.' ); + } + nPos = n; + } + return aRet.makeStringAndClear(); +} + +static OUString toRTTIname( + OUString const & rUNOname ) + throw () +{ + OUStringBuffer aRet( 64 ); + aRet.append( ".?AV" ); // class ".?AV"; struct ".?AU" + sal_Int32 nPos = rUNOname.getLength(); + while (nPos > 0) + { + sal_Int32 n = rUNOname.lastIndexOf( '.', nPos ); + aRet.append( rUNOname.copy( n +1, nPos -n -1 ) ); + aRet.append( '@' ); + nPos = n; + } + aRet.append( '@' ); + return aRet.makeStringAndClear(); +} + +//RTTI simulation + +typedef std::unordered_map< OUString, void * > t_string2PtrMap; + +namespace { + +class type_info_descriptor; + +class RTTInfos +{ + Mutex _aMutex; + t_string2PtrMap _allRTTI; + +public: + type_info * getRTTI( OUString const & rUNOname ) throw (); + int getRTTI_len(OUString const & rUNOname) throw (); + type_info_descriptor * insert_new_type_info_descriptor(OUString const & rUNOname); + + RTTInfos() throw (); +}; +class type_info_ +{ + friend type_info * RTTInfos::getRTTI( OUString const & ) throw (); + friend int mscx::mscx_filterCppException( + EXCEPTION_POINTERS *, uno_Any *, uno_Mapping * ); + +public: + virtual ~type_info_() throw (); + + type_info_( void * m_data, const char * m_d_name ) throw () + : _m_data( m_data ) + { ::strcpy( _m_d_name, m_d_name ); } // #100211# - checked + +private: + void * _m_data; + char _m_d_name[1]; +}; + +} + +type_info_::~type_info_() throw () +{ + (void)_m_data; +} + +namespace { + +class type_info_descriptor +{ +private: + int type_info_size; + type_info_ info; + +public: + + type_info_descriptor(void * m_data, const char * m_d_name) throw () + : info(m_data, m_d_name) + { + type_info_size = sizeof(type_info_) + strlen(m_d_name); + } + + type_info * get_type_info() + { + return reinterpret_cast(&info); + } + int get_type_info_size() + { + return type_info_size; + } +}; + +} + +type_info_descriptor * RTTInfos::insert_new_type_info_descriptor(OUString const & rUNOname) { + + // insert new type_info + OString aRawName(OUStringToOString(toRTTIname(rUNOname), RTL_TEXTENCODING_ASCII_US)); + type_info_descriptor * pRTTI = new(std::malloc(sizeof(type_info_descriptor) + aRawName.getLength())) + type_info_descriptor(nullptr, aRawName.getStr()); + + // put into map + pair< t_string2PtrMap::iterator, bool > insertion( + _allRTTI.insert(t_string2PtrMap::value_type(rUNOname, pRTTI))); + assert(insertion.second && "### rtti insertion failed?!"); + + return pRTTI; +} +type_info * RTTInfos::getRTTI( OUString const & rUNOname ) throw () +{ + // a must be + static_assert(sizeof(type_info_) == sizeof(type_info), "### type info structure size differ!"); + + MutexGuard aGuard( _aMutex ); + t_string2PtrMap::const_iterator const iFind( _allRTTI.find( rUNOname ) ); + + // check if type is already available + if (iFind == _allRTTI.end()) + { + // Wrap new type_info_ in type_info_descriptor to preserve length info + type_info_descriptor * pRTTI = insert_new_type_info_descriptor(rUNOname); + return pRTTI->get_type_info(); + } + else + { + return static_cast(iFind->second)->get_type_info(); + } +} + +int RTTInfos::getRTTI_len(OUString const & rUNOname) throw () +{ + MutexGuard aGuard(_aMutex); + t_string2PtrMap::const_iterator const iFind(_allRTTI.find(rUNOname)); + + // Wrap new type_info_ in type_info_descriptor to preserve length info + // check if type is already available + if (iFind == _allRTTI.end()) + { + // Wrap new type_info_ in type_info_descriptor to preserve length info + type_info_descriptor * pRTTI = insert_new_type_info_descriptor(rUNOname); + return pRTTI->get_type_info_size(); + } + else + { + return static_cast(iFind->second)->get_type_info_size(); + } +} + +RTTInfos::RTTInfos() throw () +{ +} + +static void * __cdecl copyConstruct( + void * pExcThis, + void * pSource, + typelib_TypeDescription * pTD ) throw () +{ + ::uno_copyData( pExcThis, pSource, pTD, cpp_acquire ); + return pExcThis; +} + +static void * __cdecl destruct( + void * pExcThis, + typelib_TypeDescription * pTD ) throw () +{ + ::uno_destructData( pExcThis, pTD, cpp_release ); + return pExcThis; +} + +const int codeSnippetSize = 40; + +static void GenerateConstructorTrampoline( + unsigned char * code, + typelib_TypeDescription * pTD ) throw () +{ + 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 ) throw () +{ + 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 ); +} + +namespace { + +// This looks like it is the struct catchabletype above + +struct ExceptionType +{ + sal_Int32 _n0; // flags + sal_uInt32 _pTypeInfo; // typeinfo + sal_Int32 _n1, _n2, _n3; // thiscast + sal_Int32 _n4; // object_size + sal_uInt32 _pCopyCtor; // copyctor + type_info_ type_info; + + + ExceptionType( + unsigned char * pCode, + sal_uInt64 pCodeBase, + typelib_TypeDescription * pTD ) throw () + : _n0( 0 ) + , _n1( 0 ) + , _n2( -1 ) + , _n3( 0 ) + , _n4( pTD->nSize) + , 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. + memcpy(static_cast(&type_info), static_cast(mscx_getRTTI(pTD->pTypeName)), mscx_getRTTI_len(pTD->pTypeName)); + _pTypeInfo = static_cast( + reinterpret_cast(&type_info) - pCodeBase); + GenerateConstructorTrampoline( pCode, pTD ); + assert( + pCodeBase <= reinterpret_cast(pCode) + && (reinterpret_cast(pCode) - pCodeBase + < 0x100000000)); + _pCopyCtor = static_cast( + reinterpret_cast(pCode) - pCodeBase); + } +}; + +struct RaiseInfo; + +class ExceptionInfos +{ + Mutex _aMutex; + t_string2PtrMap _allRaiseInfos; + +public: + static RaiseInfo * getRaiseInfo( typelib_TypeDescription * pTD ) throw (); + + static DWORD allocationGranularity; + + ExceptionInfos() throw (); +}; + +} + +DWORD ExceptionInfos::allocationGranularity = 0; + +// This corresponds to the struct throwinfo described above. + +namespace { + +struct RaiseInfo +{ + sal_Int32 _n0; + sal_uInt32 _pDtor; + sal_Int32 _n2; + sal_uInt32 _types; + + // Additional fields + typelib_TypeDescription * _pTD; + unsigned char * _code; + sal_uInt64 _codeBase; + + explicit RaiseInfo(typelib_TypeDescription * pTD) throw (); +}; + +} + +/* 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)throw () + : _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 = mscx_getRTTI_len(pCompTD->aBase.pTypeName); + // 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); +} + +ExceptionInfos::ExceptionInfos() throw () +{ +} + +RaiseInfo * ExceptionInfos::getRaiseInfo( typelib_TypeDescription * pTD ) throw () +{ + static ExceptionInfos* s_pInfos = []() { + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + allocationGranularity = systemInfo.dwAllocationGranularity; + + return new ExceptionInfos(); + }(); + + assert( pTD && + (pTD->eTypeClass == typelib_TypeClass_STRUCT || + pTD->eTypeClass == typelib_TypeClass_EXCEPTION) ); + + RaiseInfo * pRaiseInfo; + + OUString const & rTypeName = OUString::unacquired( &pTD->pTypeName ); + MutexGuard aGuard( s_pInfos->_aMutex ); + t_string2PtrMap::const_iterator const iFind( + s_pInfos->_allRaiseInfos.find( rTypeName ) ); + if (iFind == s_pInfos->_allRaiseInfos.end()) + { + pRaiseInfo = new RaiseInfo( pTD ); + + // Put into map + pair< t_string2PtrMap::iterator, bool > insertion( + s_pInfos->_allRaiseInfos.insert( t_string2PtrMap::value_type( rTypeName, static_cast(pRaiseInfo) ) ) ); + assert(insertion.second && "### raise info insertion failed?!"); + } + else + { + // Reuse existing info + pRaiseInfo = static_cast(iFind->second); + } + + return pRaiseInfo; +} + +type_info * mscx_getRTTI( + OUString const & rUNOname ) +{ + static RTTInfos* s_pRTTIs = new RTTInfos(); + return s_pRTTIs->getRTTI( rUNOname ); +} +int mscx_getRTTI_len( + OUString const & rUNOname) +{ + static RTTInfos* s_pRTTIs = new RTTInfos(); + return s_pRTTIs->getRTTI_len(rUNOname); +} + + +void mscx_raiseException( + uno_Any * pUnoExc, + uno_Mapping * pUno2Cpp ) +{ + // no ctor/dtor in here: this leads to dtors called twice upon RaiseException()! + // thus this obj file will be compiled without opt, so no inlining of + // ExceptionInfos::getRaiseInfo() + + // construct cpp exception object + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, pUnoExc->pType ); + + void * pCppExc = alloca( pTD->nSize ); + ::uno_copyAndConvertData( pCppExc, pUnoExc->pData, pTD, pUno2Cpp ); + + ULONG_PTR arFilterArgs[4]; + arFilterArgs[0] = MSVC_magic_number; + arFilterArgs[1] = reinterpret_cast(pCppExc); + arFilterArgs[2] = reinterpret_cast(ExceptionInfos::getRaiseInfo( pTD )); + arFilterArgs[3] = reinterpret_cast(arFilterArgs[2])->_codeBase; + + // Destruct uno exception + ::uno_any_destruct( pUnoExc, nullptr ); + TYPELIB_DANGER_RELEASE( pTD ); + + // last point to release anything not affected by stack unwinding + RaiseException( MSVC_ExceptionCode, EXCEPTION_NONCONTINUABLE, 4, arFilterArgs); +} + +namespace +{ +// This function does the same check as __CxxDetectRethrow from msvcrt (see its +// crt/src/vcruntime/mgdframe.cpp). But it does not alter the global state, i.e. it does not +// increment __ProcessingThrow, and so does not break following exception handling. We rely on the +// definition of EHExceptionRecord, PER_IS_MSVC_EH and PER_PTHROW, that are current as of msvcrt +// 2017 (14.14.26428). +bool DetectRethrow(void* ppExcept) +{ + struct EHExceptionRecord + { + DWORD ExceptionCode; + DWORD ExceptionFlags; + struct _EXCEPTION_RECORD* ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + struct alignas(8) + { + DWORD magicNumber; + PVOID pExceptionObject; + PVOID pThrowInfo; + PVOID pThrowImageBase; + } params; + }; + + constexpr auto PER_IS_MSVC_EH = [](EHExceptionRecord* p) { + constexpr DWORD EH_EXCEPTION_NUMBER = 0xE06D7363; // The NT Exception # that msvcrt uses ('msc' | 0xE0000000) + constexpr DWORD EH_MAGIC_NUMBER1 = 0x19930520; // latest magic # in thrown object + constexpr DWORD EH_MAGIC_NUMBER2 = 0x19930521; // latest magic # in func info for exception specs + constexpr DWORD EH_MAGIC_NUMBER3 = 0x19930522; // latest magic # + constexpr DWORD EH_EXCEPTION_PARAMETERS = 4; // Number of parameters in exception record for AMD64 + + return p->ExceptionCode == EH_EXCEPTION_NUMBER + && p->NumberParameters == EH_EXCEPTION_PARAMETERS + && (p->params.magicNumber == EH_MAGIC_NUMBER1 + || p->params.magicNumber == EH_MAGIC_NUMBER2 + || p->params.magicNumber == EH_MAGIC_NUMBER3); + }; + + constexpr auto PER_PTHROW = [](EHExceptionRecord* p) { + return p->params.pThrowInfo; + }; + + EHExceptionRecord* pExcept; + if (!ppExcept) + return false; + pExcept = *static_cast(ppExcept); + if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == nullptr) + { + return true; + } + return false; +} +} + +int mscx_filterCppException( + EXCEPTION_POINTERS * pPointers, + uno_Any * pUnoExc, + uno_Mapping * pCpp2Uno ) +{ + if (pPointers == nullptr) + return EXCEPTION_CONTINUE_SEARCH; + + EXCEPTION_RECORD * pRecord = pPointers->ExceptionRecord; + + // Handle only C++ exceptions: + if (pRecord == nullptr || pRecord->ExceptionCode != MSVC_ExceptionCode) + return EXCEPTION_CONTINUE_SEARCH; + + const bool rethrow = DetectRethrow(&pRecord); + assert(pRecord == pPointers->ExceptionRecord); + + if (rethrow && pRecord == pPointers->ExceptionRecord) + { + pRecord = *reinterpret_cast< EXCEPTION_RECORD ** >(__current_exception()); + } + + // Rethrow: handle only C++ exceptions: + if (pRecord == nullptr || pRecord->ExceptionCode != MSVC_ExceptionCode) + return EXCEPTION_CONTINUE_SEARCH; + + if (pRecord->NumberParameters == 4 && + pRecord->ExceptionInformation[0] == MSVC_magic_number && + pRecord->ExceptionInformation[1] != 0 && + pRecord->ExceptionInformation[2] != 0 && + pRecord->ExceptionInformation[3] != 0) + { + // ExceptionInformation[1] is the address of the thrown object + // (or the address of a pointer to it, in most cases when it + // is a C++ class, obviously). + + // [2] is the throwinfo pointer + + // [3] is the image base address which is added the 32-bit + // rva_t fields in throwinfo to get actual 64-bit addresses + ULONG_PTR base = pRecord->ExceptionInformation[3]; + DWORD * types = reinterpret_cast( + base + + (reinterpret_cast(pRecord->ExceptionInformation[2]) + ->_types)); + if (types != nullptr && types[0] != 0) + { + DWORD pType = types[1]; + ExceptionType * et + = reinterpret_cast(base + pType); + if (pType != 0 && et->_pTypeInfo != 0) + { + OUString aRTTIname( + OStringToOUString( + (reinterpret_cast(base + et->_pTypeInfo) + ->_m_d_name), + RTL_TEXTENCODING_ASCII_US)); + OUString aUNOname( toUNOname( aRTTIname ) ); + + typelib_TypeDescription * pExcTD = nullptr; + typelib_typedescription_getByName( + &pExcTD, aUNOname.pData ); + if (pExcTD == nullptr) + { + OUString sMsg = "[mscx_uno bridge error] UNO type of " + "C++ exception unknown: \"" + + aUNOname + "\", RTTI-name=\"" + + aRTTIname + "\"!"; + + RuntimeException exc( sMsg ); + uno_type_any_constructAndConvert( + pUnoExc, &exc, + cppu::UnoType::get().getTypeLibType(), pCpp2Uno ); + } + else + { + // construct uno exception any + uno_any_constructAndConvert( + pUnoExc, reinterpret_cast(pRecord->ExceptionInformation[1]), + pExcTD, pCpp2Uno ); + typelib_typedescription_release( pExcTD ); + } + + return EXCEPTION_EXECUTE_HANDLER; + } + } + } + // though this unknown exception leaks now, no user-defined exception + // is ever thrown through the binary C-UNO dispatcher call stack. + RuntimeException exc( "[mscx_uno bridge error] unexpected " + "C++ exception occurred!" ); + uno_type_any_constructAndConvert( + pUnoExc, &exc, cppu::UnoType::get().getTypeLibType(), pCpp2Uno ); + return EXCEPTION_EXECUTE_HANDLER; +} + +} + +#pragma pack(pop) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3