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