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