/* -*- 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 #include #include #include #include #include #include #include #include #include "abi.hxx" #include "vtablecall.hxx" namespace { void call( bridges::cpp_uno::shared::CppInterfaceProxy * proxy, css::uno::TypeDescription const & description, typelib_TypeDescriptionReference * returnType, sal_Int32 count, typelib_MethodParameter * parameters, unsigned long * gpr, unsigned long * fpr, unsigned long * stack, void * indirectRet) { typelib_TypeDescription * rtd = nullptr; if (returnType != nullptr) { TYPELIB_DANGER_GET(&rtd, returnType); } abi_aarch64::ReturnKind retKind = rtd == nullptr ? abi_aarch64::RETURN_KIND_REG : abi_aarch64::getReturnKind(rtd); bool retConv = rtd != nullptr && bridges::cpp_uno::shared::relatesToInterfaceType(rtd); void * retin = retKind == abi_aarch64::RETURN_KIND_INDIRECT && !retConv ? indirectRet : rtd == nullptr ? nullptr : alloca(rtd->nSize); void ** args = static_cast< void ** >(alloca(count * sizeof (void *))); void ** cppArgs = static_cast< void ** >(alloca(count * sizeof (void *))); typelib_TypeDescription ** argtds = static_cast( alloca(count * sizeof (typelib_TypeDescription *))); sal_Int32 ngpr = 1; sal_Int32 nfpr = 0; sal_Int32 sp = 0; #ifdef MACOSX sal_Int32 subsp = 0; #endif for (sal_Int32 i = 0; i != count; ++i) { if (!parameters[i].bOut && bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef)) { switch (parameters[i].pTypeRef->eTypeClass) { #ifdef MACOSX case typelib_TypeClass_BOOLEAN: case typelib_TypeClass_BYTE: if (ngpr < 8) { args[i] = gpr + ngpr; ngpr++; } else { args[i] = reinterpret_cast(reinterpret_cast(stack + sp) + subsp); subsp += 1; if (subsp == 8) { sp++; subsp = 0; } } break; case typelib_TypeClass_SHORT: case typelib_TypeClass_UNSIGNED_SHORT: case typelib_TypeClass_CHAR: if (ngpr < 8) { args[i] = gpr + ngpr; ngpr++; } else { subsp = (subsp + 1) & ~0x1; if (subsp == 8) { sp++; subsp = 0; } args[i] = reinterpret_cast(reinterpret_cast(stack + sp) + subsp); subsp += 2; if (subsp == 8) { sp++; subsp = 0; } } break; case typelib_TypeClass_LONG: case typelib_TypeClass_UNSIGNED_LONG: case typelib_TypeClass_ENUM: if (ngpr < 8) { args[i] = gpr + ngpr; ngpr++; } else { subsp = (subsp + 3) & ~0x3; if (subsp == 8) { sp++; subsp = 0; } args[i] = reinterpret_cast(reinterpret_cast(stack + sp) + subsp); subsp += 4; if (subsp == 8) { sp++; subsp = 0; } } break; case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: if (ngpr < 8) { args[i] = gpr + ngpr; ngpr++; } else { if (subsp > 0) { sp++; subsp = 0; } args[i] = stack + sp; sp++; } break; case typelib_TypeClass_FLOAT: if (nfpr < 8) { args[i] = fpr + nfpr; nfpr++; } else { subsp = (subsp + 3) & ~0x3; if (subsp == 8) { sp++; subsp = 0; } args[i] = reinterpret_cast(reinterpret_cast(stack + sp) + subsp); subsp += 4; if (subsp == 8) { sp++; subsp = 0; } } break; case typelib_TypeClass_DOUBLE: if (nfpr < 8) { args[i] = fpr + nfpr; nfpr++; } else { if (subsp > 0) { sp++; subsp = 0; } args[i] = stack + sp; sp++; } break; #else 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_CHAR: case typelib_TypeClass_ENUM: args[i] = ngpr == 8 ? stack + sp++ : gpr + ngpr++; break; case typelib_TypeClass_FLOAT: case typelib_TypeClass_DOUBLE: args[i] = nfpr == 8 ? stack + sp++ : fpr + nfpr++; break; #endif default: assert(false); } argtds[i] = nullptr; } else { #ifdef MACOSX if (subsp > 0) { sp++; subsp = 0; } #endif cppArgs[i] = reinterpret_cast( ngpr == 8 ? stack[sp++] : gpr[ngpr++]); typelib_TypeDescription * ptd = nullptr; TYPELIB_DANGER_GET(&ptd, parameters[i].pTypeRef); if (!parameters[i].bIn) { args[i] = alloca(ptd->nSize); argtds[i] = ptd; } else if (bridges::cpp_uno::shared::relatesToInterfaceType(ptd)) { args[i] = alloca(ptd->nSize); uno_copyAndConvertData( args[i], cppArgs[i], ptd, proxy->getBridge()->getCpp2Uno()); argtds[i] = ptd; } else { args[i] = cppArgs[i]; argtds[i] = nullptr; TYPELIB_DANGER_RELEASE(ptd); } } } uno_Any exc; uno_Any * pexc = &exc; proxy->getUnoI()->pDispatcher( proxy->getUnoI(), description.get(), retin, args, &pexc); if (pexc != nullptr) { for (sal_Int32 i = 0; i != count; ++i) { if (argtds[i] != nullptr) { if (parameters[i].bIn) { uno_destructData(args[i], argtds[i], nullptr); } TYPELIB_DANGER_RELEASE(argtds[i]); } } if (rtd != nullptr) { TYPELIB_DANGER_RELEASE(rtd); } abi_aarch64::raiseException(&exc, proxy->getBridge()->getUno2Cpp()); } for (sal_Int32 i = 0; i != count; ++i) { if (argtds[i] != nullptr) { if (parameters[i].bOut) { uno_destructData( cppArgs[i], argtds[i], reinterpret_cast(css::uno::cpp_release)); uno_copyAndConvertData( cppArgs[i], args[i], argtds[i], proxy->getBridge()->getUno2Cpp()); } uno_destructData(args[i], argtds[i], nullptr); TYPELIB_DANGER_RELEASE(argtds[i]); } } void * retout = nullptr; // avoid false -Werror=maybe-uninitialized switch (retKind) { case abi_aarch64::RETURN_KIND_REG: switch (rtd == nullptr ? typelib_TypeClass_VOID : rtd->eTypeClass) { case typelib_TypeClass_VOID: break; #if defined MACOSX case typelib_TypeClass_BOOLEAN: assert(rtd->nSize == sizeof (bool)); *gpr = static_cast(*static_cast(retin)); assert(!retConv); break; case typelib_TypeClass_BYTE: assert(rtd->nSize == sizeof (sal_Int8)); *gpr = *static_cast(retin); assert(!retConv); break; case typelib_TypeClass_SHORT: assert(rtd->nSize == sizeof (sal_Int16)); *gpr = *static_cast(retin); assert(!retConv); break; case typelib_TypeClass_UNSIGNED_SHORT: assert(rtd->nSize == sizeof (sal_uInt16)); *gpr = *static_cast(retin); assert(!retConv); break; case typelib_TypeClass_CHAR: assert(rtd->nSize == sizeof (sal_Unicode)); *gpr = *static_cast(retin); assert(!retConv); break; #else case typelib_TypeClass_BOOLEAN: case typelib_TypeClass_BYTE: case typelib_TypeClass_SHORT: case typelib_TypeClass_UNSIGNED_SHORT: case typelib_TypeClass_CHAR: #endif case typelib_TypeClass_LONG: case typelib_TypeClass_UNSIGNED_LONG: case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: case typelib_TypeClass_ENUM: std::memcpy(gpr, retin, rtd->nSize); assert(!retConv); break; case typelib_TypeClass_FLOAT: case typelib_TypeClass_DOUBLE: std::memcpy(fpr, retin, rtd->nSize); assert(!retConv); break; case typelib_TypeClass_STRUCT: if (retConv) { retout = gpr; } else { std::memcpy(gpr, retin, rtd->nSize); } break; default: assert(false); } break; case abi_aarch64::RETURN_KIND_HFA_FLOAT: assert(rtd != nullptr); switch (rtd->nSize) { case 16: std::memcpy(fpr + 3, static_cast(retin) + 12, 4); [[fallthrough]]; case 12: std::memcpy(fpr + 2, static_cast(retin) + 8, 4); [[fallthrough]]; case 8: std::memcpy(fpr + 1, static_cast(retin) + 4, 4); [[fallthrough]]; case 4: std::memcpy(fpr, retin, 4); break; default: assert(false); } assert(!retConv); break; case abi_aarch64::RETURN_KIND_HFA_DOUBLE: assert(rtd != nullptr); std::memcpy(fpr, retin, rtd->nSize); assert(!retConv); break; case abi_aarch64::RETURN_KIND_INDIRECT: retout = indirectRet; break; } if (retConv) { uno_copyAndConvertData( retout, retin, rtd, proxy->getBridge()->getUno2Cpp()); uno_destructData(retin, rtd, nullptr); } if (rtd != nullptr) { TYPELIB_DANGER_RELEASE(rtd); } } } void vtableCall( sal_Int32 functionIndex, sal_Int32 vtableOffset, unsigned long * gpr, unsigned long * fpr, unsigned long * stack, void * indirectRet) { bridges::cpp_uno::shared::CppInterfaceProxy * proxy = bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( reinterpret_cast(gpr[0]) - vtableOffset); typelib_InterfaceTypeDescription * type = proxy->getTypeDescr(); assert(functionIndex < type->nMapFunctionIndexToMemberIndex); sal_Int32 pos = type->pMapFunctionIndexToMemberIndex[functionIndex]; css::uno::TypeDescription desc(type->ppAllMembers[pos]); switch (desc.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: if (type->pMapMemberIndexToFunctionIndex[pos] == functionIndex) { // Getter: call( proxy, desc, reinterpret_cast( desc.get())->pAttributeTypeRef, 0, nullptr, gpr, fpr, stack, indirectRet); } else { // Setter: typelib_MethodParameter param = { nullptr, reinterpret_cast( desc.get())->pAttributeTypeRef, true, false }; call(proxy, desc, nullptr, 1, ¶m, gpr, fpr, stack, indirectRet); } break; case typelib_TypeClass_INTERFACE_METHOD: switch (functionIndex) { case 1: proxy->acquireProxy(); break; case 2: proxy->releaseProxy(); break; case 0: { typelib_TypeDescription * td = nullptr; TYPELIB_DANGER_GET( &td, (reinterpret_cast(gpr[1]) ->getTypeLibType())); if (td != nullptr && td->eTypeClass == typelib_TypeClass_INTERFACE) { css::uno::XInterface * ifc = nullptr; proxy->getBridge()->getCppEnv()->getRegisteredInterface( proxy->getBridge()->getCppEnv(), reinterpret_cast(&ifc), proxy->getOid().pData, reinterpret_cast( td)); if (ifc != nullptr) { uno_any_construct( static_cast(indirectRet), &ifc, td, reinterpret_cast( css::uno::cpp_acquire)); ifc->release(); TYPELIB_DANGER_RELEASE(td); break; } TYPELIB_DANGER_RELEASE(td); } } [[fallthrough]]; default: call( proxy, desc, reinterpret_cast( desc.get())->pReturnTypeRef, reinterpret_cast( desc.get())->nParams, reinterpret_cast( desc.get())->pParams, gpr, fpr, stack, indirectRet); } break; default: assert(false); } } namespace { std::size_t const codeSnippetSize = 8 * 4; unsigned char * generateCodeSnippet( unsigned char * code, sal_Int32 functionIndex, sal_Int32 vtableOffset) { // movz x9, reinterpret_cast(code)[0] = 0xD2800009 | ((functionIndex & 0xFFFF) << 5); // movk x9, , LSL #16 reinterpret_cast(code)[1] = 0xF2A00009 | ((functionIndex >> 16) << 5); // movz x10, reinterpret_cast(code)[2] = 0xD280000A | ((vtableOffset & 0xFFFF) << 5); // movk x10, , LSL #16 reinterpret_cast(code)[3] = 0xF2A0000A | ((vtableOffset >> 16) << 5); // ldr x11, +2*4 reinterpret_cast(code)[4] = 0x5800004B; // br x11 reinterpret_cast(code)[5] = 0xD61F0160; reinterpret_cast(code)[3] = reinterpret_cast(&vtableSlotCall); 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(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, #ifdef USE_DOUBLE_MMAP sal_PtrDiff writetoexecdiff, #endif typelib_InterfaceTypeDescription const * type, sal_Int32 functionOffset, sal_Int32 functionCount, sal_Int32 vtableOffset) { #ifndef USE_DOUBLE_MMAP constexpr sal_PtrDiff writetoexecdiff = 0; #endif (*slots) -= functionCount; Slot * s = *slots; for (sal_Int32 i = 0; i != type->nMembers; ++i) { typelib_TypeDescription * td = nullptr; TYPELIB_DANGER_GET(&td, type->ppMembers[i]); assert(td != nullptr); switch (td->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { typelib_InterfaceAttributeTypeDescription * atd = reinterpret_cast< typelib_InterfaceAttributeTypeDescription *>(td); // Getter: (s++)->fn = code + writetoexecdiff; code = generateCodeSnippet( code, functionOffset++, vtableOffset); // Setter: if (!atd->bReadOnly) { (s++)->fn = code + writetoexecdiff; code = generateCodeSnippet( code, functionOffset++, vtableOffset); } break; } case typelib_TypeClass_INTERFACE_METHOD: (s++)->fn = code + writetoexecdiff; code = generateCodeSnippet(code, functionOffset++, vtableOffset); break; default: assert(false); } TYPELIB_DANGER_RELEASE(td); } return code; } void bridges::cpp_uno::shared::VtableFactory::flushCode( unsigned char const * begin, unsigned char const * end) { #if !defined ANDROID && !defined MACOSX static void (*clear_cache)(unsigned char const *, unsigned char const *) = (void (*)(unsigned char const *, unsigned char const *)) dlsym( RTLD_DEFAULT, "__clear_cache"); (*clear_cache)(begin, end); #else // GCC clarified with // // "extend.texi (__clear_cache): Correct signature" that __builtin___clear_cache takes void* // parameters, while Clang uses char* ever since // "Add // support for __builtin___clear_cache in Clang" (TODO: see // "__builtin___clear_cache() has a different // prototype than GCC"; once fixed for our Clang baseline, we can drop the reinterpret_casts): __builtin___clear_cache( reinterpret_cast(const_cast(begin)), reinterpret_cast(const_cast(end))); #endif } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */