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 #if defined MACOSX && defined AARCH64 57 #include <pthread.h> 58 #endif 59 #elif defined SAL_W32 60 #define WIN32_LEAN_AND_MEAN 61 #ifdef _MSC_VER 62 #pragma warning(push,1) // disable warnings within system headers 63 #endif 64 #include <windows.h> 65 #ifdef _MSC_VER 66 #pragma warning(pop) 67 #endif 68 #elif defined SAL_OS2 69 #define INCL_DOS 70 #define INCL_DOSMISC 71 #include <os2.h> 72 #else 73 #error Unsupported platform 74 #endif 75 76 using bridges::cpp_uno::shared::VtableFactory; 77 78 namespace { 79 80 #if defined MACOSX && defined AARCH64 81 class JitWriteGuard 82 { 83 public: 84 JitWriteGuard(): m_active(pthread_jit_write_protect_supported_np() != 0) 85 { 86 if (m_active) 87 pthread_jit_write_protect_np(0); 88 } 89 90 ~JitWriteGuard() 91 { 92 if (m_active) 93 pthread_jit_write_protect_np(1); 94 } 95 96 private: 97 bool m_active; 98 }; 99 #endif 100 101 extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { 102 sal_Size pagesize; 103 #if defined SAL_UNX 104 #if defined FREEBSD || defined NETBSD 105 pagesize = getpagesize(); 106 #else 107 pagesize = sysconf(_SC_PAGESIZE); 108 #endif 109 #elif defined SAL_W32 110 SYSTEM_INFO info; 111 GetSystemInfo(&info); 112 pagesize = info.dwPageSize; 113 #elif defined(SAL_OS2) 114 ULONG ulPageSize; 115 DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, &ulPageSize, sizeof(ULONG)); 116 pagesize = (sal_Size)ulPageSize; 117 #else 118 #error Unsupported platform 119 #endif 120 sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1); 121 void * p; 122 #if defined SAL_UNX 123 #if defined MACOSX && defined AARCH64 124 p = mmap( 125 0, n, PROT_READ | PROT_WRITE | PROT_EXEC, 126 MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); 127 #else 128 p = mmap( 129 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 130 0); 131 #endif 132 if (p == MAP_FAILED) { 133 p = 0; 134 } 135 #if !defined MACOSX || !defined AARCH64 136 else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) 137 { 138 munmap (static_cast<char*>(p), n); 139 p = 0; 140 } 141 #endif 142 #elif defined SAL_W32 143 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 144 #elif defined(SAL_OS2) 145 p = 0; 146 DosAllocMem( &p, n, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY); 147 #endif 148 if (p != 0) { 149 *size = n; 150 } 151 return p; 152 } 153 154 extern "C" void SAL_CALL freeExec( 155 rtl_arena_type *, void * address, sal_Size size) 156 { 157 #if defined SAL_UNX 158 munmap(static_cast< char * >(address), size); 159 #elif defined SAL_W32 160 (void) size; // unused 161 VirtualFree(address, 0, MEM_RELEASE); 162 #elif defined(SAL_OS2) 163 (void) DosFreeMem( address); 164 #endif 165 } 166 167 } 168 169 class VtableFactory::GuardedBlocks: public std::vector< Block > { 170 public: 171 GuardedBlocks(VtableFactory const & factory): 172 m_factory(factory), m_guarded(true) {} 173 174 ~GuardedBlocks(); 175 176 void unguard() { m_guarded = false; } 177 178 private: 179 GuardedBlocks(GuardedBlocks &); // not implemented 180 void operator =(GuardedBlocks); // not implemented 181 182 VtableFactory const & m_factory; 183 bool m_guarded; 184 }; 185 186 VtableFactory::GuardedBlocks::~GuardedBlocks() { 187 if (m_guarded) { 188 for (iterator i(begin()); i != end(); ++i) { 189 m_factory.freeBlock(*i); 190 } 191 } 192 } 193 194 class VtableFactory::BaseOffset { 195 public: 196 BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); } 197 198 sal_Int32 getFunctionOffset(rtl::OUString const & name) const 199 { return m_map.find(name)->second; } 200 201 private: 202 sal_Int32 calculate( 203 typelib_InterfaceTypeDescription * type, sal_Int32 offset); 204 205 typedef std::hash_map< rtl::OUString, sal_Int32, rtl::OUStringHash > Map; 206 207 Map m_map; 208 }; 209 210 sal_Int32 VtableFactory::BaseOffset::calculate( 211 typelib_InterfaceTypeDescription * type, sal_Int32 offset) 212 { 213 rtl::OUString name(type->aBase.pTypeName); 214 if (m_map.find(name) == m_map.end()) { 215 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { 216 offset = calculate(type->ppBaseTypes[i], offset); 217 } 218 m_map.insert(Map::value_type(name, offset)); 219 typelib_typedescription_complete( 220 reinterpret_cast< typelib_TypeDescription ** >(&type)); 221 offset += bridges::cpp_uno::shared::getLocalFunctions(type); 222 } 223 return offset; 224 } 225 226 VtableFactory::VtableFactory(): m_arena( 227 rtl_arena_create( 228 "bridges::cpp_uno::shared::VtableFactory", 229 sizeof (void *), // to satisfy alignment requirements 230 0, reinterpret_cast< rtl_arena_type * >( 0 ), allocExec, freeExec, 0)) 231 { 232 if (m_arena == 0) { 233 throw std::bad_alloc(); 234 } 235 } 236 237 VtableFactory::~VtableFactory() { 238 { 239 osl::MutexGuard guard(m_mutex); 240 for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) { 241 for (sal_Int32 j = 0; j < i->second.count; ++j) { 242 freeBlock(i->second.blocks[j]); 243 } 244 delete[] i->second.blocks; 245 } 246 } 247 rtl_arena_destroy(m_arena); 248 } 249 250 VtableFactory::Vtables VtableFactory::getVtables( 251 typelib_InterfaceTypeDescription * type) 252 { 253 rtl::OUString name(type->aBase.pTypeName); 254 osl::MutexGuard guard(m_mutex); 255 Map::iterator i(m_map.find(name)); 256 if (i == m_map.end()) { 257 GuardedBlocks blocks(*this); 258 createVtables(blocks, BaseOffset(type), type, true); 259 Vtables vtables; 260 OSL_ASSERT(blocks.size() <= SAL_MAX_INT32); 261 vtables.count = static_cast< sal_Int32 >(blocks.size()); 262 bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks( 263 new Block[vtables.count]); 264 vtables.blocks = guardedBlocks.get(); 265 for (sal_Int32 j = 0; j < vtables.count; ++j) { 266 vtables.blocks[j] = blocks[j]; 267 } 268 i = m_map.insert(Map::value_type(name, vtables)).first; 269 guardedBlocks.release(); 270 blocks.unguard(); 271 } 272 return i->second; 273 } 274 275 #ifdef USE_DOUBLE_MMAP 276 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const 277 { 278 sal_Size size = getBlockSize(slotCount); 279 sal_Size pagesize = sysconf(_SC_PAGESIZE); 280 block.size = (size + (pagesize - 1)) & ~(pagesize - 1); 281 block.start = block.exec = NULL; 282 block.fd = -1; 283 284 osl::Security aSecurity; 285 rtl::OUString strDirectory; 286 rtl::OUString strURLDirectory; 287 if (aSecurity.getHomeDir(strURLDirectory)) 288 osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory); 289 290 for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i) 291 { 292 if (strDirectory.isEmpty()) 293 strDirectory = rtl::OUString::createFromAscii("/tmp"); 294 295 strDirectory += rtl::OUString::createFromAscii("/.execoooXXXXXX"); 296 rtl::OString aTmpName = rtl::OUStringToOString(strDirectory, osl_getThreadTextEncoding()); 297 char *tmpfname = new char[aTmpName.getLength()+1]; 298 strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1); 299 if ((block.fd = mkstemp(tmpfname)) == -1) 300 perror("creation of executable memory area failed"); 301 if (block.fd == -1) 302 { 303 delete[] tmpfname; 304 break; 305 } 306 unlink(tmpfname); 307 delete[] tmpfname; 308 if (ftruncate(block.fd, block.size) == -1) 309 { 310 perror("truncation of executable memory area failed"); 311 close(block.fd); 312 block.fd = -1; 313 break; 314 } 315 block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0); 316 if (block.start== MAP_FAILED) { 317 block.start = 0; 318 } 319 block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0); 320 if (block.exec == MAP_FAILED) { 321 block.exec = 0; 322 } 323 324 //All good 325 if (block.start && block.exec && block.fd != -1) 326 break; 327 328 freeBlock(block); 329 330 strDirectory = rtl::OUString(); 331 } 332 if (!block.start || !block.exec || block.fd == -1) 333 { 334 //Fall back to non-doublemmaped allocation 335 block.fd = -1; 336 block.start = block.exec = rtl_arena_alloc(m_arena, &block.size); 337 } 338 return (block.start != 0 && block.exec != 0); 339 } 340 341 void VtableFactory::freeBlock(Block const & block) const { 342 //if the double-map failed we were allocated on the arena 343 if (block.fd == -1 && block.start == block.exec && block.start != NULL) 344 rtl_arena_free(m_arena, block.start, block.size); 345 else 346 { 347 if (block.start) munmap(block.start, block.size); 348 if (block.exec) munmap(block.exec, block.size); 349 if (block.fd != -1) close(block.fd); 350 } 351 } 352 #else 353 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const 354 { 355 #if defined MACOSX && defined AARCH64 356 JitWriteGuard jitWriteGuard; 357 #endif 358 block.size = getBlockSize(slotCount); 359 block.start = rtl_arena_alloc(m_arena, &block.size); 360 return block.start != 0; 361 } 362 363 void VtableFactory::freeBlock(Block const & block) const { 364 #if defined MACOSX && defined AARCH64 365 JitWriteGuard jitWriteGuard; 366 #endif 367 rtl_arena_free(m_arena, block.start, block.size); 368 } 369 #endif 370 371 void VtableFactory::createVtables( 372 GuardedBlocks & blocks, BaseOffset const & baseOffset, 373 typelib_InterfaceTypeDescription * type, bool includePrimary) const 374 { 375 if (includePrimary) { 376 sal_Int32 slotCount 377 = bridges::cpp_uno::shared::getPrimaryFunctions(type); 378 Block block; 379 if (!createBlock(block, slotCount)) { 380 throw std::bad_alloc(); 381 } 382 try { 383 #if defined MACOSX && defined AARCH64 384 JitWriteGuard jitWriteGuard; 385 #endif 386 Slot * slots = initializeBlock(block.start, slotCount); 387 unsigned char * codeBegin = 388 reinterpret_cast< unsigned char * >(slots); 389 unsigned char * code = codeBegin; 390 sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *); 391 for (typelib_InterfaceTypeDescription const * type2 = type; 392 type2 != 0; type2 = type2->pBaseTypeDescription) 393 { 394 code = addLocalFunctions( 395 &slots, code, 396 #ifdef USE_DOUBLE_MMAP 397 sal_IntPtr(block.exec) - sal_IntPtr(block.start), 398 #endif 399 type2, 400 baseOffset.getFunctionOffset(type2->aBase.pTypeName), 401 bridges::cpp_uno::shared::getLocalFunctions(type2), 402 vtableOffset); 403 } 404 flushCode(codeBegin, code); 405 #ifdef USE_DOUBLE_MMAP 406 //Finished generating block, swap writable pointer with executable 407 //pointer 408 ::std::swap(block.start, block.exec); 409 #endif 410 blocks.push_back(block); 411 } catch (...) { 412 freeBlock(block); 413 throw; 414 } 415 } 416 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { 417 createVtables(blocks, baseOffset, type->ppBaseTypes[i], i != 0); 418 } 419 } 420