Bump for 3.6-28
[LibreOffice.git] / bridges / source / cpp_uno / shared / vtablefactory.cxx
blobd44c2e6f52bdd4b1db93ee3a9186fd406e97cc4a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include "bridges/cpp_uno/shared/vtablefactory.hxx"
32 #include "guardedarray.hxx"
34 #include "bridges/cpp_uno/shared/vtables.hxx"
36 #include "osl/thread.h"
37 #include "osl/security.hxx"
38 #include "osl/file.hxx"
39 #include "osl/diagnose.h"
40 #include "osl/mutex.hxx"
41 #include "rtl/alloc.h"
42 #include "rtl/ustring.hxx"
43 #include "sal/log.hxx"
44 #include "sal/types.h"
45 #include "typelib/typedescription.hxx"
47 #include <boost/unordered_map.hpp>
48 #include <new>
49 #include <vector>
51 #if defined SAL_UNX
52 #include <unistd.h>
53 #include <string.h>
54 #include <errno.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 #else
66 #error Unsupported platform
67 #endif
69 #if defined USE_DOUBLE_MMAP
70 #include <fcntl.h>
71 #endif
73 using bridges::cpp_uno::shared::VtableFactory;
75 namespace {
77 extern "C" void * SAL_CALL allocExec(
78 SAL_UNUSED_PARAMETER rtl_arena_type *, sal_Size * size)
80 sal_Size pagesize;
81 #if defined SAL_UNX
82 #if defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
83 pagesize = getpagesize();
84 #else
85 pagesize = sysconf(_SC_PAGESIZE);
86 #endif
87 #elif defined SAL_W32
88 SYSTEM_INFO info;
89 GetSystemInfo(&info);
90 pagesize = info.dwPageSize;
91 #else
92 #error Unsupported platform
93 #endif
94 sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1);
95 void * p;
96 #if defined SAL_UNX
97 p = mmap(
98 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1,
99 0);
100 if (p == MAP_FAILED) {
101 p = 0;
103 else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
105 munmap (static_cast<char*>(p), n);
106 p = 0;
108 #elif defined SAL_W32
109 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
110 #endif
111 if (p != 0) {
112 *size = n;
114 return p;
117 extern "C" void SAL_CALL freeExec(
118 SAL_UNUSED_PARAMETER rtl_arena_type *, void * address, sal_Size size)
120 #if defined SAL_UNX
121 munmap(static_cast< char * >(address), size);
122 #elif defined SAL_W32
123 (void) size; // unused
124 VirtualFree(address, 0, MEM_RELEASE);
125 #endif
130 class VtableFactory::GuardedBlocks: public std::vector< Block > {
131 public:
132 GuardedBlocks(VtableFactory const & factory):
133 m_factory(factory), m_guarded(true) {}
135 ~GuardedBlocks();
137 void unguard() { m_guarded = false; }
139 private:
140 GuardedBlocks(GuardedBlocks &); // not implemented
141 void operator =(GuardedBlocks); // not implemented
143 VtableFactory const & m_factory;
144 bool m_guarded;
147 VtableFactory::GuardedBlocks::~GuardedBlocks() {
148 if (m_guarded) {
149 for (iterator i(begin()); i != end(); ++i) {
150 m_factory.freeBlock(*i);
155 class VtableFactory::BaseOffset {
156 public:
157 BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); }
159 sal_Int32 getFunctionOffset(rtl::OUString const & name) const
160 { return m_map.find(name)->second; }
162 private:
163 sal_Int32 calculate(
164 typelib_InterfaceTypeDescription * type, sal_Int32 offset);
166 typedef boost::unordered_map< rtl::OUString, sal_Int32, rtl::OUStringHash > Map;
168 Map m_map;
171 sal_Int32 VtableFactory::BaseOffset::calculate(
172 typelib_InterfaceTypeDescription * type, sal_Int32 offset)
174 rtl::OUString name(type->aBase.pTypeName);
175 if (m_map.find(name) == m_map.end()) {
176 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
177 offset = calculate(type->ppBaseTypes[i], offset);
179 m_map.insert(Map::value_type(name, offset));
180 typelib_typedescription_complete(
181 reinterpret_cast< typelib_TypeDescription ** >(&type));
182 offset += bridges::cpp_uno::shared::getLocalFunctions(type);
184 return offset;
187 VtableFactory::VtableFactory(): m_arena(
188 rtl_arena_create(
189 "bridges::cpp_uno::shared::VtableFactory",
190 sizeof (void *), // to satisfy alignment requirements
191 0, reinterpret_cast< rtl_arena_type * >(-1), allocExec, freeExec, 0))
193 if (m_arena == 0) {
194 throw std::bad_alloc();
198 VtableFactory::~VtableFactory() {
200 osl::MutexGuard guard(m_mutex);
201 for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) {
202 for (sal_Int32 j = 0; j < i->second.count; ++j) {
203 freeBlock(i->second.blocks[j]);
205 delete[] i->second.blocks;
208 rtl_arena_destroy(m_arena);
211 VtableFactory::Vtables VtableFactory::getVtables(
212 typelib_InterfaceTypeDescription * type)
214 rtl::OUString name(type->aBase.pTypeName);
215 osl::MutexGuard guard(m_mutex);
216 Map::iterator i(m_map.find(name));
217 if (i == m_map.end()) {
218 GuardedBlocks blocks(*this);
219 createVtables(blocks, BaseOffset(type), type, true);
220 Vtables vtables;
221 OSL_ASSERT(blocks.size() <= SAL_MAX_INT32);
222 vtables.count = static_cast< sal_Int32 >(blocks.size());
223 bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks(
224 new Block[vtables.count]);
225 vtables.blocks = guardedBlocks.get();
226 for (sal_Int32 j = 0; j < vtables.count; ++j) {
227 vtables.blocks[j] = blocks[j];
229 i = m_map.insert(Map::value_type(name, vtables)).first;
230 guardedBlocks.release();
231 blocks.unguard();
233 return i->second;
236 #ifdef USE_DOUBLE_MMAP
237 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
239 sal_Size size = getBlockSize(slotCount);
240 sal_Size pagesize = sysconf(_SC_PAGESIZE);
241 block.size = (size + (pagesize - 1)) & ~(pagesize - 1);
242 block.start = block.exec = NULL;
243 block.fd = -1;
245 osl::Security aSecurity;
246 rtl::OUString strDirectory;
247 rtl::OUString strURLDirectory;
248 if (aSecurity.getHomeDir(strURLDirectory))
249 osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory);
251 for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i)
253 if (strDirectory.isEmpty())
254 strDirectory = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/tmp" ));
256 strDirectory += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/.execoooXXXXXX" ));
257 rtl::OString aTmpName = rtl::OUStringToOString(strDirectory, osl_getThreadTextEncoding());
258 char *tmpfname = new char[aTmpName.getLength()+1];
259 strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1);
260 if ((block.fd = mkstemp(tmpfname)) == -1)
261 fprintf(stderr, "mkstemp(\"%s\") failed: %s\n", tmpfname, strerror(errno));
262 if (block.fd == -1)
264 delete[] tmpfname;
265 break;
267 unlink(tmpfname);
268 delete[] tmpfname;
269 #if defined(HAVE_POSIX_FALLOCATE)
270 int err = posix_fallocate(block.fd, 0, block.size);
271 #else
272 int err = ftruncate(block.fd, block.size);
273 #endif
274 if (err != 0)
276 #if defined(HAVE_POSIX_FALLOCATE)
277 SAL_WARN("bridges", "posix_fallocate failed with code " << err);
278 #else
279 SAL_WARN("bridges", "truncation of executable memory area failed with code " << err);
280 #endif
281 close(block.fd);
282 block.fd = -1;
283 break;
285 block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0);
286 if (block.start== MAP_FAILED) {
287 block.start = 0;
289 block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0);
290 if (block.exec == MAP_FAILED) {
291 block.exec = 0;
294 //All good
295 if (block.start && block.exec && block.fd != -1)
296 break;
298 freeBlock(block);
300 strDirectory = rtl::OUString();
302 if (!block.start || !block.exec || block.fd == -1)
304 //Fall back to non-doublemmaped allocation
305 block.fd = -1;
306 block.start = block.exec = rtl_arena_alloc(m_arena, &block.size);
308 return (block.start != 0 && block.exec != 0);
311 void VtableFactory::freeBlock(Block const & block) const {
312 //if the double-map failed we were allocated on the arena
313 if (block.fd == -1 && block.start == block.exec && block.start != NULL)
314 rtl_arena_free(m_arena, block.start, block.size);
315 else
317 if (block.start) munmap(block.start, block.size);
318 if (block.exec) munmap(block.exec, block.size);
319 if (block.fd != -1) close(block.fd);
322 #else
323 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
325 block.size = getBlockSize(slotCount);
326 block.start = rtl_arena_alloc(m_arena, &block.size);
327 return block.start != 0;
330 void VtableFactory::freeBlock(Block const & block) const {
331 rtl_arena_free(m_arena, block.start, block.size);
333 #endif
335 void VtableFactory::createVtables(
336 GuardedBlocks & blocks, BaseOffset const & baseOffset,
337 typelib_InterfaceTypeDescription * type, bool includePrimary) const
339 if (includePrimary) {
340 sal_Int32 slotCount
341 = bridges::cpp_uno::shared::getPrimaryFunctions(type);
342 Block block;
343 if (!createBlock(block, slotCount)) {
344 throw std::bad_alloc();
346 try {
347 Slot * slots = initializeBlock(block.start, slotCount);
348 unsigned char * codeBegin =
349 reinterpret_cast< unsigned char * >(slots);
350 unsigned char * code = codeBegin;
351 sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *);
352 for (typelib_InterfaceTypeDescription const * type2 = type;
353 type2 != 0; type2 = type2->pBaseTypeDescription)
355 code = addLocalFunctions(
356 &slots, code,
357 #ifdef USE_DOUBLE_MMAP
358 sal_IntPtr(block.exec) - sal_IntPtr(block.start),
359 #endif
360 type2,
361 baseOffset.getFunctionOffset(type2->aBase.pTypeName),
362 bridges::cpp_uno::shared::getLocalFunctions(type2),
363 vtableOffset);
365 flushCode(codeBegin, code);
366 #ifdef USE_DOUBLE_MMAP
367 //Finished generating block, swap writable pointer with executable
368 //pointer
369 ::std::swap(block.start, block.exec);
370 #endif
371 blocks.push_back(block);
372 } catch (...) {
373 freeBlock(block);
374 throw;
377 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
378 createVtables(blocks, baseOffset, type->ppBaseTypes[i], i != 0);
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */