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 | |
66 | using bridges::cpp_uno::shared::VtableFactory; |
67 | |
68 | namespace { |
69 | |
70 | extern "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 | |
110 | extern "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 | |
123 | class VtableFactory::GuardedBlocks: public std::vector< Block > { |
124 | public: |
125 | GuardedBlocks(VtableFactory const & factory): |
126 | m_factory(factory), m_guarded(true) {} |
127 | |
128 | ~GuardedBlocks(); |
129 | |
130 | void unguard() { m_guarded = false; } |
131 | |
132 | private: |
133 | GuardedBlocks(GuardedBlocks &); // not implemented |
134 | void operator =(GuardedBlocks); // not implemented |
135 | |
136 | VtableFactory const & m_factory; |
137 | bool m_guarded; |
138 | }; |
139 | |
140 | VtableFactory::GuardedBlocks::~GuardedBlocks() { |
141 | if (m_guarded) { |
142 | for (iterator i(begin()); i != end(); ++i) { |
143 | m_factory.freeBlock(*i); |
144 | } |
145 | } |
146 | } |
147 | |
148 | class VtableFactory::BaseOffset { |
149 | public: |
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 | |
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 | sal_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 | |
180 | VtableFactory::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 | |
191 | VtableFactory::~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 | |
204 | VtableFactory::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 |
230 | bool 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 | |
306 | void 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 |
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 | void 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 | |