/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_bridges.hxx" #if defined OS2 #define INCL_DOS #define INCL_DOSMISC #endif #include "bridges/cpp_uno/shared/vtablefactory.hxx" #include "guardedarray.hxx" #include "bridges/cpp_uno/shared/vtables.hxx" #include "osl/thread.h" #include "osl/security.hxx" #include "osl/file.hxx" #include "osl/diagnose.h" #include "osl/mutex.hxx" #include "rtl/alloc.h" #include "rtl/ustring.hxx" #include "sal/types.h" #include "typelib/typedescription.hxx" #include #include #include #if defined SAL_UNX #include #include #include #elif defined SAL_W32 #define WIN32_LEAN_AND_MEAN #ifdef _MSC_VER #pragma warning(push,1) // disable warnings within system headers #endif #include #ifdef _MSC_VER #pragma warning(pop) #endif #elif defined SAL_OS2 #define INCL_DOS #define INCL_DOSMISC #include #else #error Unsupported platform #endif using bridges::cpp_uno::shared::VtableFactory; namespace { extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { sal_Size pagesize; #if defined SAL_UNX #if defined FREEBSD || defined NETBSD pagesize = getpagesize(); #else pagesize = sysconf(_SC_PAGESIZE); #endif #elif defined SAL_W32 SYSTEM_INFO info; GetSystemInfo(&info); pagesize = info.dwPageSize; #elif defined(SAL_OS2) ULONG ulPageSize; DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, &ulPageSize, sizeof(ULONG)); pagesize = (sal_Size)ulPageSize; #else #error Unsupported platform #endif sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1); void * p; #if defined SAL_UNX p = mmap( 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (p == MAP_FAILED) { p = 0; } else if (mprotect (static_cast(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { munmap (static_cast(p), n); p = 0; } #elif defined SAL_W32 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #elif defined(SAL_OS2) p = 0; DosAllocMem( &p, n, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY); #endif if (p != 0) { *size = n; } return p; } extern "C" void SAL_CALL freeExec( rtl_arena_type *, void * address, sal_Size size) { #if defined SAL_UNX munmap(static_cast< char * >(address), size); #elif defined SAL_W32 (void) size; // unused VirtualFree(address, 0, MEM_RELEASE); #elif defined(SAL_OS2) (void) DosFreeMem( address); #endif } } class VtableFactory::GuardedBlocks: public std::vector< Block > { public: GuardedBlocks(VtableFactory const & factory): m_factory(factory), m_guarded(true) {} ~GuardedBlocks(); void unguard() { m_guarded = false; } private: GuardedBlocks(GuardedBlocks &); // not implemented void operator =(GuardedBlocks); // not implemented VtableFactory const & m_factory; bool m_guarded; }; VtableFactory::GuardedBlocks::~GuardedBlocks() { if (m_guarded) { for (iterator i(begin()); i != end(); ++i) { m_factory.freeBlock(*i); } } } class VtableFactory::BaseOffset { public: BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); } sal_Int32 getFunctionOffset(rtl::OUString const & name) const { return m_map.find(name)->second; } private: sal_Int32 calculate( typelib_InterfaceTypeDescription * type, sal_Int32 offset); typedef std::hash_map< rtl::OUString, sal_Int32, rtl::OUStringHash > Map; Map m_map; }; sal_Int32 VtableFactory::BaseOffset::calculate( typelib_InterfaceTypeDescription * type, sal_Int32 offset) { rtl::OUString name(type->aBase.pTypeName); if (m_map.find(name) == m_map.end()) { for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { offset = calculate(type->ppBaseTypes[i], offset); } m_map.insert(Map::value_type(name, offset)); typelib_typedescription_complete( reinterpret_cast< typelib_TypeDescription ** >(&type)); offset += bridges::cpp_uno::shared::getLocalFunctions(type); } return offset; } VtableFactory::VtableFactory(): m_arena( rtl_arena_create( "bridges::cpp_uno::shared::VtableFactory", sizeof (void *), // to satisfy alignment requirements 0, reinterpret_cast< rtl_arena_type * >( 0 ), allocExec, freeExec, 0)) { if (m_arena == 0) { throw std::bad_alloc(); } } VtableFactory::~VtableFactory() { { osl::MutexGuard guard(m_mutex); for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) { for (sal_Int32 j = 0; j < i->second.count; ++j) { freeBlock(i->second.blocks[j]); } delete[] i->second.blocks; } } rtl_arena_destroy(m_arena); } VtableFactory::Vtables VtableFactory::getVtables( typelib_InterfaceTypeDescription * type) { rtl::OUString name(type->aBase.pTypeName); osl::MutexGuard guard(m_mutex); Map::iterator i(m_map.find(name)); if (i == m_map.end()) { GuardedBlocks blocks(*this); createVtables(blocks, BaseOffset(type), type, true); Vtables vtables; OSL_ASSERT(blocks.size() <= SAL_MAX_INT32); vtables.count = static_cast< sal_Int32 >(blocks.size()); bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks( new Block[vtables.count]); vtables.blocks = guardedBlocks.get(); for (sal_Int32 j = 0; j < vtables.count; ++j) { vtables.blocks[j] = blocks[j]; } i = m_map.insert(Map::value_type(name, vtables)).first; guardedBlocks.release(); blocks.unguard(); } return i->second; } #ifdef USE_DOUBLE_MMAP bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const { sal_Size size = getBlockSize(slotCount); sal_Size pagesize = sysconf(_SC_PAGESIZE); block.size = (size + (pagesize - 1)) & ~(pagesize - 1); block.start = block.exec = NULL; block.fd = -1; osl::Security aSecurity; rtl::OUString strDirectory; rtl::OUString strURLDirectory; if (aSecurity.getHomeDir(strURLDirectory)) osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory); for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i) { if (strDirectory.isEmpty()) strDirectory = rtl::OUString::createFromAscii("/tmp"); strDirectory += rtl::OUString::createFromAscii("/.execoooXXXXXX"); rtl::OString aTmpName = rtl::OUStringToOString(strDirectory, osl_getThreadTextEncoding()); char *tmpfname = new char[aTmpName.getLength()+1]; strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1); if ((block.fd = mkstemp(tmpfname)) == -1) perror("creation of executable memory area failed"); if (block.fd == -1) { delete[] tmpfname; break; } unlink(tmpfname); delete[] tmpfname; if (ftruncate(block.fd, block.size) == -1) { perror("truncation of executable memory area failed"); close(block.fd); block.fd = -1; break; } block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0); if (block.start== MAP_FAILED) { block.start = 0; } block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0); if (block.exec == MAP_FAILED) { block.exec = 0; } //All good if (block.start && block.exec && block.fd != -1) break; freeBlock(block); strDirectory = rtl::OUString(); } if (!block.start || !block.exec || block.fd == -1) { //Fall back to non-doublemmaped allocation block.fd = -1; block.start = block.exec = rtl_arena_alloc(m_arena, &block.size); } return (block.start != 0 && block.exec != 0); } void VtableFactory::freeBlock(Block const & block) const { //if the double-map failed we were allocated on the arena if (block.fd == -1 && block.start == block.exec && block.start != NULL) rtl_arena_free(m_arena, block.start, block.size); else { if (block.start) munmap(block.start, block.size); if (block.exec) munmap(block.exec, block.size); if (block.fd != -1) close(block.fd); } } #else bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const { block.size = getBlockSize(slotCount); block.start = rtl_arena_alloc(m_arena, &block.size); return block.start != 0; } void VtableFactory::freeBlock(Block const & block) const { rtl_arena_free(m_arena, block.start, block.size); } #endif void VtableFactory::createVtables( GuardedBlocks & blocks, BaseOffset const & baseOffset, typelib_InterfaceTypeDescription * type, bool includePrimary) const { if (includePrimary) { sal_Int32 slotCount = bridges::cpp_uno::shared::getPrimaryFunctions(type); Block block; if (!createBlock(block, slotCount)) { throw std::bad_alloc(); } try { Slot * slots = initializeBlock(block.start, slotCount); unsigned char * codeBegin = reinterpret_cast< unsigned char * >(slots); unsigned char * code = codeBegin; sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *); for (typelib_InterfaceTypeDescription const * type2 = type; type2 != 0; type2 = type2->pBaseTypeDescription) { code = addLocalFunctions( &slots, code, #ifdef USE_DOUBLE_MMAP sal_IntPtr(block.exec) - sal_IntPtr(block.start), #endif type2, baseOffset.getFunctionOffset(type2->aBase.pTypeName), bridges::cpp_uno::shared::getLocalFunctions(type2), vtableOffset); } flushCode(codeBegin, code); #ifdef USE_DOUBLE_MMAP //Finished generating block, swap writable pointer with executable //pointer ::std::swap(block.start, block.exec); #endif blocks.push_back(block); } catch (...) { freeBlock(block); throw; } } for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { createVtables(blocks, baseOffset, type->ppBaseTypes[i], i != 0); } }