1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_bridges.hxx" 26 27 #if defined OS2 28 #define INCL_DOS 29 #define INCL_DOSMISC 30 #endif 31 32 #include "bridges/cpp_uno/shared/vtablefactory.hxx" 33 34 #include "guardedarray.hxx" 35 36 #include "bridges/cpp_uno/shared/vtables.hxx" 37 38 #include "osl/thread.h" 39 #include "osl/security.hxx" 40 #include "osl/file.hxx" 41 #include "osl/diagnose.h" 42 #include "osl/mutex.hxx" 43 #include "rtl/alloc.h" 44 #include "rtl/ustring.hxx" 45 #include "sal/types.h" 46 #include "typelib/typedescription.hxx" 47 48 #include <hash_map> 49 #include <new> 50 #include <vector> 51 52 #if defined SAL_UNX 53 #include <unistd.h> 54 #include <string.h> 55 #include <sys/mman.h> 56 #elif defined SAL_W32 57 #define WIN32_LEAN_AND_MEAN 58 #ifdef _MSC_VER 59 #pragma warning(push,1) // disable warnings within system headers 60 #endif 61 #include <windows.h> 62 #ifdef _MSC_VER 63 #pragma warning(pop) 64 #endif 65 #elif defined SAL_OS2 66 #define INCL_DOS 67 #define INCL_DOSMISC 68 #include <os2.h> 69 #else 70 #error Unsupported platform 71 #endif 72 73 using bridges::cpp_uno::shared::VtableFactory; 74 75 namespace { 76 77 extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { 78 sal_Size pagesize; 79 #if defined SAL_UNX 80 #if defined FREEBSD || defined NETBSD 81 pagesize = getpagesize(); 82 #else 83 pagesize = sysconf(_SC_PAGESIZE); 84 #endif 85 #elif defined SAL_W32 86 SYSTEM_INFO info; 87 GetSystemInfo(&info); 88 pagesize = info.dwPageSize; 89 #elif defined(SAL_OS2) 90 ULONG ulPageSize; 91 DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, &ulPageSize, sizeof(ULONG)); 92 pagesize = (sal_Size)ulPageSize; 93 #else 94 #error Unsupported platform 95 #endif 96 sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1); 97 void * p; 98 #if defined SAL_UNX 99 #if defined MACOSX 100 p = mmap( 101 0, n, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 102 0); 103 #else 104 p = mmap( 105 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 106 0); 107 if (p == MAP_FAILED) { 108 p = 0; 109 } 110 else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) 111 { 112 munmap (static_cast<char*>(p), n); 113 p = 0; 114 } 115 #endif 116 #elif defined SAL_W32 117 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 118 #elif defined(SAL_OS2) 119 p = 0; 120 DosAllocMem( &p, n, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY); 121 #endif 122 if (p != 0) { 123 *size = n; 124 } 125 return p; 126 } 127 128 extern "C" void SAL_CALL freeExec( 129 rtl_arena_type *, void * address, sal_Size size) 130 { 131 #if defined SAL_UNX 132 munmap(static_cast< char * >(address), size); 133 #elif defined SAL_W32 134 (void) size; // unused 135 VirtualFree(address, 0, MEM_RELEASE); 136 #elif defined(SAL_OS2) 137 (void) DosFreeMem( address); 138 #endif 139 } 140 141 } 142 143 class VtableFactory::GuardedBlocks: public std::vector< Block > { 144 public: 145 GuardedBlocks(VtableFactory const & factory): 146 m_factory(factory), m_guarded(true) {} 147 148 ~GuardedBlocks(); 149 150 void unguard() { m_guarded = false; } 151 152 private: 153 GuardedBlocks(GuardedBlocks &); // not implemented 154 void operator =(GuardedBlocks); // not implemented 155 156 VtableFactory const & m_factory; 157 bool m_guarded; 158 }; 159 160 VtableFactory::GuardedBlocks::~GuardedBlocks() { 161 if (m_guarded) { 162 for (iterator i(begin()); i != end(); ++i) { 163 m_factory.freeBlock(*i); 164 } 165 } 166 } 167 168 class VtableFactory::BaseOffset { 169 public: 170 BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); } 171 172 sal_Int32 getFunctionOffset(rtl::OUString const & name) const 173 { return m_map.find(name)->second; } 174 175 private: 176 sal_Int32 calculate( 177 typelib_InterfaceTypeDescription * type, sal_Int32 offset); 178 179 typedef std::hash_map< rtl::OUString, sal_Int32, rtl::OUStringHash > Map; 180 181 Map m_map; 182 }; 183 184 sal_Int32 VtableFactory::BaseOffset::calculate( 185 typelib_InterfaceTypeDescription * type, sal_Int32 offset) 186 { 187 rtl::OUString name(type->aBase.pTypeName); 188 if (m_map.find(name) == m_map.end()) { 189 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { 190 offset = calculate(type->ppBaseTypes[i], offset); 191 } 192 m_map.insert(Map::value_type(name, offset)); 193 typelib_typedescription_complete( 194 reinterpret_cast< typelib_TypeDescription ** >(&type)); 195 offset += bridges::cpp_uno::shared::getLocalFunctions(type); 196 } 197 return offset; 198 } 199 200 VtableFactory::VtableFactory(): m_arena( 201 rtl_arena_create( 202 "bridges::cpp_uno::shared::VtableFactory", 203 sizeof (void *), // to satisfy alignment requirements 204 0, reinterpret_cast< rtl_arena_type * >( 0 ), allocExec, freeExec, 0)) 205 { 206 if (m_arena == 0) { 207 throw std::bad_alloc(); 208 } 209 } 210 211 VtableFactory::~VtableFactory() { 212 { 213 osl::MutexGuard guard(m_mutex); 214 for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) { 215 for (sal_Int32 j = 0; j < i->second.count; ++j) { 216 freeBlock(i->second.blocks[j]); 217 } 218 delete[] i->second.blocks; 219 } 220 } 221 rtl_arena_destroy(m_arena); 222 } 223 224 VtableFactory::Vtables VtableFactory::getVtables( 225 typelib_InterfaceTypeDescription * type) 226 { 227 rtl::OUString name(type->aBase.pTypeName); 228 osl::MutexGuard guard(m_mutex); 229 Map::iterator i(m_map.find(name)); 230 if (i == m_map.end()) { 231 GuardedBlocks blocks(*this); 232 createVtables(blocks, BaseOffset(type), type, true); 233 Vtables vtables; 234 OSL_ASSERT(blocks.size() <= SAL_MAX_INT32); 235 vtables.count = static_cast< sal_Int32 >(blocks.size()); 236 bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks( 237 new Block[vtables.count]); 238 vtables.blocks = guardedBlocks.get(); 239 for (sal_Int32 j = 0; j < vtables.count; ++j) { 240 vtables.blocks[j] = blocks[j]; 241 } 242 i = m_map.insert(Map::value_type(name, vtables)).first; 243 guardedBlocks.release(); 244 blocks.unguard(); 245 } 246 return i->second; 247 } 248 249 #ifdef USE_DOUBLE_MMAP 250 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const 251 { 252 sal_Size size = getBlockSize(slotCount); 253 sal_Size pagesize = sysconf(_SC_PAGESIZE); 254 block.size = (size + (pagesize - 1)) & ~(pagesize - 1); 255 block.start = block.exec = NULL; 256 block.fd = -1; 257 258 osl::Security aSecurity; 259 rtl::OUString strDirectory; 260 rtl::OUString strURLDirectory; 261 if (aSecurity.getHomeDir(strURLDirectory)) 262 osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory); 263 264 for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i) 265 { 266 if (strDirectory.isEmpty()) 267 strDirectory = rtl::OUString::createFromAscii("/tmp"); 268 269 strDirectory += rtl::OUString::createFromAscii("/.execoooXXXXXX"); 270 rtl::OString aTmpName = rtl::OUStringToOString(strDirectory, osl_getThreadTextEncoding()); 271 char *tmpfname = new char[aTmpName.getLength()+1]; 272 strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1); 273 if ((block.fd = mkstemp(tmpfname)) == -1) 274 perror("creation of executable memory area failed"); 275 if (block.fd == -1) 276 { 277 delete[] tmpfname; 278 break; 279 } 280 unlink(tmpfname); 281 delete[] tmpfname; 282 if (ftruncate(block.fd, block.size) == -1) 283 { 284 perror("truncation of executable memory area failed"); 285 close(block.fd); 286 block.fd = -1; 287 break; 288 } 289 block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0); 290 if (block.start== MAP_FAILED) { 291 block.start = 0; 292 } 293 block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0); 294 if (block.exec == MAP_FAILED) { 295 block.exec = 0; 296 } 297 298 //All good 299 if (block.start && block.exec && block.fd != -1) 300 break; 301 302 freeBlock(block); 303 304 strDirectory = rtl::OUString(); 305 } 306 if (!block.start || !block.exec || block.fd == -1) 307 { 308 //Fall back to non-doublemmaped allocation 309 block.fd = -1; 310 block.start = block.exec = rtl_arena_alloc(m_arena, &block.size); 311 } 312 return (block.start != 0 && block.exec != 0); 313 } 314 315 void VtableFactory::freeBlock(Block const & block) const { 316 //if the double-map failed we were allocated on the arena 317 if (block.fd == -1 && block.start == block.exec && block.start != NULL) 318 rtl_arena_free(m_arena, block.start, block.size); 319 else 320 { 321 if (block.start) munmap(block.start, block.size); 322 if (block.exec) munmap(block.exec, block.size); 323 if (block.fd != -1) close(block.fd); 324 } 325 } 326 #else 327 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const 328 { 329 block.size = getBlockSize(slotCount); 330 block.start = rtl_arena_alloc(m_arena, &block.size); 331 return block.start != 0; 332 } 333 334 void VtableFactory::freeBlock(Block const & block) const { 335 rtl_arena_free(m_arena, block.start, block.size); 336 } 337 #endif 338 339 void VtableFactory::createVtables( 340 GuardedBlocks & blocks, BaseOffset const & baseOffset, 341 typelib_InterfaceTypeDescription * type, bool includePrimary) const 342 { 343 if (includePrimary) { 344 sal_Int32 slotCount 345 = bridges::cpp_uno::shared::getPrimaryFunctions(type); 346 Block block; 347 if (!createBlock(block, slotCount)) { 348 throw std::bad_alloc(); 349 } 350 try { 351 Slot * slots = initializeBlock(block.start, slotCount); 352 unsigned char * codeBegin = 353 reinterpret_cast< unsigned char * >(slots); 354 unsigned char * code = codeBegin; 355 sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *); 356 for (typelib_InterfaceTypeDescription const * type2 = type; 357 type2 != 0; type2 = type2->pBaseTypeDescription) 358 { 359 code = addLocalFunctions( 360 &slots, code, 361 #ifdef USE_DOUBLE_MMAP 362 sal_IntPtr(block.exec) - sal_IntPtr(block.start), 363 #endif 364 type2, 365 baseOffset.getFunctionOffset(type2->aBase.pTypeName), 366 bridges::cpp_uno::shared::getLocalFunctions(type2), 367 vtableOffset); 368 } 369 flushCode(codeBegin, code); 370 #ifdef USE_DOUBLE_MMAP 371 //Finished generating block, swap writable pointer with executable 372 //pointer 373 ::std::swap(block.start, block.exec); 374 #endif 375 blocks.push_back(block); 376 } catch (...) { 377 freeBlock(block); 378 throw; 379 } 380 } 381 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { 382 createVtables(blocks, baseOffset, type->ppBaseTypes[i], i != 0); 383 } 384 } 385