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/mutex.hxx"
31 : #include "rtl/alloc.h"
32 : #include "rtl/ustring.hxx"
33 : #include "sal/log.hxx"
34 : #include "sal/types.h"
35 : #include "typelib/typedescription.hxx"
36 :
37 : #include <boost/noncopyable.hpp>
38 : #include <new>
39 : #include <unordered_map>
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 676 : 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 676 : 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 676 : 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 676 : 0);
91 676 : if (p == MAP_FAILED) {
92 0 : p = 0;
93 : }
94 676 : else if (mprotect (p, n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
95 : {
96 0 : munmap (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 676 : if (p != 0) {
103 676 : *size = n;
104 : }
105 676 : 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(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:
122 : public std::vector<Block>, private boost::noncopyable
123 : {
124 : public:
125 657 : GuardedBlocks(VtableFactory const & factory):
126 657 : m_factory(factory), m_guarded(true) {}
127 :
128 : ~GuardedBlocks();
129 :
130 657 : void unguard() { m_guarded = false; }
131 :
132 : private:
133 : VtableFactory const & m_factory;
134 : bool m_guarded;
135 : };
136 :
137 1314 : VtableFactory::GuardedBlocks::~GuardedBlocks() {
138 657 : if (m_guarded) {
139 0 : for (iterator i(begin()); i != end(); ++i) {
140 0 : m_factory.freeBlock(*i);
141 : }
142 : }
143 657 : }
144 :
145 657 : class VtableFactory::BaseOffset {
146 : public:
147 657 : BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); }
148 :
149 1489 : sal_Int32 getFunctionOffset(OUString const & name) const
150 1489 : { return m_map.find(name)->second; }
151 :
152 : private:
153 : sal_Int32 calculate(
154 : typelib_InterfaceTypeDescription * type, sal_Int32 offset);
155 :
156 : typedef std::unordered_map< OUString, sal_Int32, OUStringHash > Map;
157 :
158 : Map m_map;
159 : };
160 :
161 1487 : sal_Int32 VtableFactory::BaseOffset::calculate(
162 : typelib_InterfaceTypeDescription * type, sal_Int32 offset)
163 : {
164 1487 : OUString name(type->aBase.pTypeName);
165 1487 : if (m_map.find(name) == m_map.end()) {
166 2298 : for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
167 830 : offset = calculate(type->ppBaseTypes[i], offset);
168 : }
169 1468 : m_map.insert(Map::value_type(name, offset));
170 : typelib_typedescription_complete(
171 1468 : reinterpret_cast< typelib_TypeDescription ** >(&type));
172 1468 : offset += bridges::cpp_uno::shared::getLocalFunctions(type);
173 : }
174 1487 : return offset;
175 : }
176 :
177 506 : VtableFactory::VtableFactory(): m_arena(
178 : rtl_arena_create(
179 : "bridges::cpp_uno::shared::VtableFactory",
180 : sizeof (void *), // to satisfy alignment requirements
181 506 : 0, nullptr, allocExec, freeExec, 0))
182 : {
183 506 : if (m_arena == 0) {
184 0 : throw std::bad_alloc();
185 : }
186 506 : }
187 :
188 0 : VtableFactory::~VtableFactory() {
189 : {
190 0 : osl::MutexGuard guard(m_mutex);
191 0 : for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) {
192 0 : for (sal_Int32 j = 0; j < i->second.count; ++j) {
193 0 : freeBlock(i->second.blocks[j]);
194 : }
195 0 : delete[] i->second.blocks;
196 0 : }
197 : }
198 0 : rtl_arena_destroy(m_arena);
199 0 : }
200 :
201 128338 : VtableFactory::Vtables VtableFactory::getVtables(
202 : typelib_InterfaceTypeDescription * type)
203 : {
204 128338 : OUString name(type->aBase.pTypeName);
205 256676 : osl::MutexGuard guard(m_mutex);
206 128338 : Map::iterator i(m_map.find(name));
207 128338 : if (i == m_map.end()) {
208 657 : GuardedBlocks blocks(*this);
209 657 : createVtables(blocks, BaseOffset(type), type, 0, type, true);
210 657 : Vtables vtables;
211 : assert(blocks.size() <= SAL_MAX_INT32);
212 657 : vtables.count = static_cast< sal_Int32 >(blocks.size());
213 : bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks(
214 1314 : new Block[vtables.count]);
215 657 : vtables.blocks = guardedBlocks.get();
216 1333 : for (sal_Int32 j = 0; j < vtables.count; ++j) {
217 676 : vtables.blocks[j] = blocks[j];
218 : }
219 657 : i = m_map.insert(Map::value_type(name, vtables)).first;
220 657 : guardedBlocks.release();
221 1314 : blocks.unguard();
222 : }
223 256676 : return i->second;
224 : }
225 :
226 : #ifdef USE_DOUBLE_MMAP
227 676 : bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
228 : {
229 676 : sal_Size size = getBlockSize(slotCount);
230 676 : sal_Size pagesize = sysconf(_SC_PAGESIZE);
231 676 : block.size = (size + (pagesize - 1)) & ~(pagesize - 1);
232 676 : block.fd = -1;
233 :
234 : // Try non-doublemmaped allocation first:
235 676 : block.start = block.exec = rtl_arena_alloc(m_arena, &block.size);
236 676 : if (block.start != nullptr) {
237 676 : return true;
238 : }
239 :
240 0 : osl::Security aSecurity;
241 0 : OUString strDirectory;
242 0 : OUString strURLDirectory;
243 0 : if (aSecurity.getHomeDir(strURLDirectory))
244 0 : osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory);
245 :
246 0 : for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i)
247 : {
248 0 : if (strDirectory.isEmpty())
249 0 : strDirectory = "/tmp";
250 :
251 0 : strDirectory += "/.execoooXXXXXX";
252 0 : OString aTmpName = OUStringToOString(strDirectory, osl_getThreadTextEncoding());
253 0 : char *tmpfname = new char[aTmpName.getLength()+1];
254 0 : strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1);
255 : // coverity[secure_temp] - https://communities.coverity.com/thread/3179
256 0 : if ((block.fd = mkstemp(tmpfname)) == -1)
257 0 : fprintf(stderr, "mkstemp(\"%s\") failed: %s\n", tmpfname, strerror(errno));
258 0 : if (block.fd == -1)
259 : {
260 0 : delete[] tmpfname;
261 0 : break;
262 : }
263 0 : unlink(tmpfname);
264 0 : delete[] tmpfname;
265 : #if defined(HAVE_POSIX_FALLOCATE)
266 0 : int err = posix_fallocate(block.fd, 0, block.size);
267 : #else
268 : int err = ftruncate(block.fd, block.size);
269 : #endif
270 0 : if (err != 0)
271 : {
272 : #if defined(HAVE_POSIX_FALLOCATE)
273 : SAL_WARN("bridges", "posix_fallocate failed with code " << err);
274 : #else
275 : SAL_WARN("bridges", "truncation of executable memory area failed with code " << err);
276 : #endif
277 0 : close(block.fd);
278 0 : block.fd = -1;
279 0 : break;
280 : }
281 0 : block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0);
282 0 : if (block.start== MAP_FAILED) {
283 0 : block.start = 0;
284 : }
285 0 : block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0);
286 0 : if (block.exec == MAP_FAILED) {
287 0 : block.exec = 0;
288 : }
289 :
290 : //All good
291 0 : if (block.start && block.exec && block.fd != -1)
292 0 : break;
293 :
294 0 : freeBlock(block);
295 :
296 0 : strDirectory.clear();
297 0 : }
298 0 : return (block.start != 0 && block.exec != 0);
299 : }
300 :
301 0 : void VtableFactory::freeBlock(Block const & block) const {
302 : //if the double-map failed we were allocated on the arena
303 0 : if (block.fd == -1 && block.start == block.exec && block.start != NULL)
304 0 : rtl_arena_free(m_arena, block.start, block.size);
305 : else
306 : {
307 0 : if (block.start) munmap(block.start, block.size);
308 0 : if (block.exec) munmap(block.exec, block.size);
309 0 : if (block.fd != -1) close(block.fd);
310 : }
311 0 : }
312 : #else
313 : bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
314 : {
315 : block.size = getBlockSize(slotCount);
316 : block.start = rtl_arena_alloc(m_arena, &block.size);
317 : return block.start != 0;
318 : }
319 :
320 : void VtableFactory::freeBlock(Block const & block) const {
321 : rtl_arena_free(m_arena, block.start, block.size);
322 : }
323 : #endif
324 :
325 1489 : sal_Int32 VtableFactory::createVtables(
326 : GuardedBlocks & blocks, BaseOffset const & baseOffset,
327 : typelib_InterfaceTypeDescription * type, sal_Int32 vtableNumber,
328 : typelib_InterfaceTypeDescription * mostDerived, bool includePrimary) const
329 : {
330 1489 : if (includePrimary) {
331 : sal_Int32 slotCount
332 676 : = bridges::cpp_uno::shared::getPrimaryFunctions(type);
333 : Block block;
334 676 : if (!createBlock(block, slotCount)) {
335 0 : throw std::bad_alloc();
336 : }
337 : try {
338 : Slot * slots = initializeBlock(
339 676 : block.start, slotCount, vtableNumber, mostDerived);
340 : unsigned char * codeBegin =
341 676 : reinterpret_cast< unsigned char * >(slots);
342 676 : unsigned char * code = codeBegin;
343 676 : sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *);
344 2165 : for (typelib_InterfaceTypeDescription const * type2 = type;
345 : type2 != 0; type2 = type2->pBaseTypeDescription)
346 : {
347 : code = addLocalFunctions(
348 : &slots, code,
349 : #ifdef USE_DOUBLE_MMAP
350 1489 : sal_uIntPtr(block.exec) - sal_uIntPtr(block.start),
351 : #endif
352 : type2,
353 : baseOffset.getFunctionOffset(type2->aBase.pTypeName),
354 : bridges::cpp_uno::shared::getLocalFunctions(type2),
355 2978 : vtableOffset);
356 : }
357 676 : flushCode(codeBegin, code);
358 : #ifdef USE_DOUBLE_MMAP
359 : //Finished generating block, swap writable pointer with executable
360 : //pointer
361 676 : ::std::swap(block.start, block.exec);
362 : #endif
363 676 : blocks.push_back(block);
364 0 : } catch (...) {
365 0 : freeBlock(block);
366 0 : throw;
367 : }
368 : }
369 2321 : for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
370 : vtableNumber = createVtables(
371 832 : blocks, baseOffset, type->ppBaseTypes[i],
372 1664 : vtableNumber + (i == 0 ? 0 : 1), mostDerived, i != 0);
373 : }
374 1489 : return vtableNumber;
375 : }
376 :
377 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|