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/noncopyable.hpp>
39 : #include <boost/unordered_map.hpp>
40 : #include <new>
41 : #include <vector>
42 :
43 : #if defined SAL_UNX
44 : #include <unistd.h>
45 : #include <string.h>
46 : #include <errno.h>
47 : #include <sys/mman.h>
48 : #elif defined SAL_W32
49 : #define WIN32_LEAN_AND_MEAN
50 : #ifdef _MSC_VER
51 : #pragma warning(push,1) // disable warnings within system headers
52 : #endif
53 : #include <windows.h>
54 : #ifdef _MSC_VER
55 : #pragma warning(pop)
56 : #endif
57 : #else
58 : #error Unsupported platform
59 : #endif
60 :
61 : #if defined USE_DOUBLE_MMAP
62 : #include <fcntl.h>
63 : #endif
64 :
65 : using bridges::cpp_uno::shared::VtableFactory;
66 :
67 : namespace {
68 :
69 0 : extern "C" void * SAL_CALL allocExec(
70 : SAL_UNUSED_PARAMETER rtl_arena_type *, sal_Size * size)
71 : {
72 : sal_Size pagesize;
73 : #if defined SAL_UNX
74 : #if defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
75 : pagesize = getpagesize();
76 : #else
77 0 : pagesize = sysconf(_SC_PAGESIZE);
78 : #endif
79 : #elif defined SAL_W32
80 : SYSTEM_INFO info;
81 : GetSystemInfo(&info);
82 : pagesize = info.dwPageSize;
83 : #else
84 : #error Unsupported platform
85 : #endif
86 0 : sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1);
87 : void * p;
88 : #if defined SAL_UNX
89 : p = mmap(
90 : 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1,
91 0 : 0);
92 0 : if (p == MAP_FAILED) {
93 0 : p = 0;
94 : }
95 0 : else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
96 : {
97 0 : munmap (static_cast<char*>(p), n);
98 0 : p = 0;
99 : }
100 : #elif defined SAL_W32
101 : p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
102 : #endif
103 0 : if (p != 0) {
104 0 : *size = n;
105 : }
106 0 : return p;
107 : }
108 :
109 0 : extern "C" void SAL_CALL freeExec(
110 : SAL_UNUSED_PARAMETER rtl_arena_type *, void * address, sal_Size size)
111 : {
112 : #if defined SAL_UNX
113 0 : munmap(static_cast< char * >(address), size);
114 : #elif defined SAL_W32
115 : (void) size; // unused
116 : VirtualFree(address, 0, MEM_RELEASE);
117 : #endif
118 0 : }
119 :
120 : }
121 :
122 : class VtableFactory::GuardedBlocks:
123 : public std::vector<Block>, private boost::noncopyable
124 : {
125 : public:
126 871 : GuardedBlocks(VtableFactory const & factory):
127 871 : m_factory(factory), m_guarded(true) {}
128 :
129 : ~GuardedBlocks();
130 :
131 871 : void unguard() { m_guarded = false; }
132 :
133 : private:
134 : VtableFactory const & m_factory;
135 : bool m_guarded;
136 : };
137 :
138 1742 : VtableFactory::GuardedBlocks::~GuardedBlocks() {
139 871 : if (m_guarded) {
140 0 : for (iterator i(begin()); i != end(); ++i) {
141 0 : m_factory.freeBlock(*i);
142 : }
143 : }
144 871 : }
145 :
146 871 : class VtableFactory::BaseOffset {
147 : public:
148 871 : BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); }
149 :
150 1998 : sal_Int32 getFunctionOffset(OUString const & name) const
151 1998 : { 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 1994 : sal_Int32 VtableFactory::BaseOffset::calculate(
163 : typelib_InterfaceTypeDescription * type, sal_Int32 offset)
164 : {
165 1994 : OUString name(type->aBase.pTypeName);
166 1994 : if (m_map.find(name) == m_map.end()) {
167 3083 : for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
168 1123 : offset = calculate(type->ppBaseTypes[i], offset);
169 : }
170 1960 : m_map.insert(Map::value_type(name, offset));
171 : typelib_typedescription_complete(
172 1960 : reinterpret_cast< typelib_TypeDescription ** >(&type));
173 1960 : offset += bridges::cpp_uno::shared::getLocalFunctions(type);
174 : }
175 1994 : return offset;
176 : }
177 :
178 642 : VtableFactory::VtableFactory(): m_arena(
179 : rtl_arena_create(
180 : "bridges::cpp_uno::shared::VtableFactory",
181 : sizeof (void *), // to satisfy alignment requirements
182 642 : 0, nullptr, allocExec, freeExec, 0))
183 : {
184 642 : if (m_arena == 0) {
185 0 : throw std::bad_alloc();
186 : }
187 642 : }
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 46008 : VtableFactory::Vtables VtableFactory::getVtables(
203 : typelib_InterfaceTypeDescription * type)
204 : {
205 46008 : OUString name(type->aBase.pTypeName);
206 92016 : osl::MutexGuard guard(m_mutex);
207 46008 : Map::iterator i(m_map.find(name));
208 46008 : if (i == m_map.end()) {
209 871 : GuardedBlocks blocks(*this);
210 871 : createVtables(blocks, BaseOffset(type), type, 0, type, true);
211 871 : Vtables vtables;
212 : OSL_ASSERT(blocks.size() <= SAL_MAX_INT32);
213 871 : vtables.count = static_cast< sal_Int32 >(blocks.size());
214 : bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks(
215 1742 : new Block[vtables.count]);
216 871 : vtables.blocks = guardedBlocks.get();
217 1776 : for (sal_Int32 j = 0; j < vtables.count; ++j) {
218 905 : vtables.blocks[j] = blocks[j];
219 : }
220 871 : i = m_map.insert(Map::value_type(name, vtables)).first;
221 871 : guardedBlocks.release();
222 1742 : blocks.unguard();
223 : }
224 92016 : return i->second;
225 : }
226 :
227 : #ifdef USE_DOUBLE_MMAP
228 905 : bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
229 : {
230 905 : sal_Size size = getBlockSize(slotCount);
231 905 : sal_Size pagesize = sysconf(_SC_PAGESIZE);
232 905 : block.size = (size + (pagesize - 1)) & ~(pagesize - 1);
233 905 : block.start = block.exec = NULL;
234 905 : block.fd = -1;
235 :
236 905 : osl::Security aSecurity;
237 1810 : OUString strDirectory;
238 1810 : OUString strURLDirectory;
239 905 : if (aSecurity.getHomeDir(strURLDirectory))
240 905 : osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory);
241 :
242 905 : for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i)
243 : {
244 905 : if (strDirectory.isEmpty())
245 0 : strDirectory = "/tmp";
246 :
247 905 : strDirectory += "/.execoooXXXXXX";
248 905 : OString aTmpName = OUStringToOString(strDirectory, osl_getThreadTextEncoding());
249 905 : char *tmpfname = new char[aTmpName.getLength()+1];
250 905 : strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1);
251 905 : if ((block.fd = mkstemp(tmpfname)) == -1)
252 0 : fprintf(stderr, "mkstemp(\"%s\") failed: %s\n", tmpfname, strerror(errno));
253 905 : if (block.fd == -1)
254 : {
255 0 : delete[] tmpfname;
256 0 : break;
257 : }
258 905 : unlink(tmpfname);
259 905 : delete[] tmpfname;
260 : #if defined(HAVE_POSIX_FALLOCATE)
261 905 : int err = posix_fallocate(block.fd, 0, block.size);
262 : #else
263 : int err = ftruncate(block.fd, block.size);
264 : #endif
265 905 : 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 0 : break;
275 : }
276 905 : block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0);
277 905 : if (block.start== MAP_FAILED) {
278 0 : block.start = 0;
279 : }
280 905 : block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0);
281 905 : if (block.exec == MAP_FAILED) {
282 0 : block.exec = 0;
283 : }
284 :
285 : //All good
286 905 : if (block.start && block.exec && block.fd != -1)
287 905 : break;
288 :
289 0 : freeBlock(block);
290 :
291 0 : strDirectory = OUString();
292 0 : }
293 905 : 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 1810 : 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 1998 : sal_Int32 VtableFactory::createVtables(
327 : GuardedBlocks & blocks, BaseOffset const & baseOffset,
328 : typelib_InterfaceTypeDescription * type, sal_Int32 vtableNumber,
329 : typelib_InterfaceTypeDescription * mostDerived, bool includePrimary) const
330 : {
331 1998 : if (includePrimary) {
332 : sal_Int32 slotCount
333 905 : = bridges::cpp_uno::shared::getPrimaryFunctions(type);
334 : Block block;
335 905 : if (!createBlock(block, slotCount)) {
336 0 : throw std::bad_alloc();
337 : }
338 : try {
339 : Slot * slots = initializeBlock(
340 905 : block.start, slotCount, vtableNumber, mostDerived);
341 : unsigned char * codeBegin =
342 905 : reinterpret_cast< unsigned char * >(slots);
343 905 : unsigned char * code = codeBegin;
344 905 : sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *);
345 2903 : for (typelib_InterfaceTypeDescription const * type2 = type;
346 : type2 != 0; type2 = type2->pBaseTypeDescription)
347 : {
348 : code = addLocalFunctions(
349 : &slots, code,
350 : #ifdef USE_DOUBLE_MMAP
351 : sal_IntPtr(block.exec) - sal_IntPtr(block.start),
352 : #endif
353 : type2,
354 : baseOffset.getFunctionOffset(type2->aBase.pTypeName),
355 : bridges::cpp_uno::shared::getLocalFunctions(type2),
356 1998 : vtableOffset);
357 : }
358 905 : flushCode(codeBegin, code);
359 : #ifdef USE_DOUBLE_MMAP
360 : //Finished generating block, swap writable pointer with executable
361 : //pointer
362 905 : ::std::swap(block.start, block.exec);
363 : #endif
364 905 : blocks.push_back(block);
365 0 : } catch (...) {
366 0 : freeBlock(block);
367 0 : throw;
368 : }
369 : }
370 3125 : for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
371 : vtableNumber = createVtables(
372 1127 : blocks, baseOffset, type->ppBaseTypes[i],
373 2254 : vtableNumber + (i == 0 ? 0 : 1), mostDerived, i != 0);
374 : }
375 1998 : return vtableNumber;
376 : }
377 :
378 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|