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