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