1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_bridges.hxx"
26
27 #if defined OS2
28 #define INCL_DOS
29 #define INCL_DOSMISC
30 #endif
31
32 #include "bridges/cpp_uno/shared/vtablefactory.hxx"
33
34 #include "guardedarray.hxx"
35
36 #include "bridges/cpp_uno/shared/vtables.hxx"
37
38 #include "osl/thread.h"
39 #include "osl/security.hxx"
40 #include "osl/file.hxx"
41 #include "osl/diagnose.h"
42 #include "osl/mutex.hxx"
43 #include "rtl/alloc.h"
44 #include "rtl/ustring.hxx"
45 #include "sal/types.h"
46 #include "typelib/typedescription.hxx"
47
48 #include <hash_map>
49 #include <new>
50 #include <vector>
51
52 #if defined SAL_UNX
53 #include <unistd.h>
54 #include <string.h>
55 #include <sys/mman.h>
56 #if defined MACOSX && defined AARCH64
57 #include <pthread.h>
58 #endif
59 #elif defined SAL_W32
60 #define WIN32_LEAN_AND_MEAN
61 #ifdef _MSC_VER
62 #pragma warning(push,1) // disable warnings within system headers
63 #endif
64 #include <windows.h>
65 #ifdef _MSC_VER
66 #pragma warning(pop)
67 #endif
68 #elif defined SAL_OS2
69 #define INCL_DOS
70 #define INCL_DOSMISC
71 #include <os2.h>
72 #else
73 #error Unsupported platform
74 #endif
75
76 using bridges::cpp_uno::shared::VtableFactory;
77
78 namespace {
79
80 #if defined MACOSX && defined AARCH64
81 class JitWriteGuard
82 {
83 public:
JitWriteGuard()84 JitWriteGuard(): m_active(pthread_jit_write_protect_supported_np() != 0)
85 {
86 if (m_active)
87 pthread_jit_write_protect_np(0);
88 }
89
~JitWriteGuard()90 ~JitWriteGuard()
91 {
92 if (m_active)
93 pthread_jit_write_protect_np(1);
94 }
95
96 private:
97 bool m_active;
98 };
99 #endif
100
allocExec(rtl_arena_type *,sal_Size * size)101 extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) {
102 sal_Size pagesize;
103 #if defined SAL_UNX
104 #if defined FREEBSD || defined NETBSD
105 pagesize = getpagesize();
106 #else
107 pagesize = sysconf(_SC_PAGESIZE);
108 #endif
109 #elif defined SAL_W32
110 SYSTEM_INFO info;
111 GetSystemInfo(&info);
112 pagesize = info.dwPageSize;
113 #elif defined(SAL_OS2)
114 ULONG ulPageSize;
115 DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, &ulPageSize, sizeof(ULONG));
116 pagesize = (sal_Size)ulPageSize;
117 #else
118 #error Unsupported platform
119 #endif
120 sal_Size n = (*size + (pagesize - 1)) & ~(pagesize - 1);
121 void * p;
122 #if defined SAL_UNX
123 #if defined MACOSX && defined AARCH64
124 p = mmap(
125 0, n, PROT_READ | PROT_WRITE | PROT_EXEC,
126 MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
127 #else
128 p = mmap(
129 0, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1,
130 0);
131 #endif
132 if (p == MAP_FAILED) {
133 p = 0;
134 }
135 #if !defined MACOSX || !defined AARCH64
136 else if (mprotect (static_cast<char*>(p), n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
137 {
138 munmap (static_cast<char*>(p), n);
139 p = 0;
140 }
141 #endif
142 #elif defined SAL_W32
143 p = VirtualAlloc(0, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
144 #elif defined(SAL_OS2)
145 p = 0;
146 DosAllocMem( &p, n, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY);
147 #endif
148 if (p != 0) {
149 *size = n;
150 }
151 return p;
152 }
153
freeExec(rtl_arena_type *,void * address,sal_Size size)154 extern "C" void SAL_CALL freeExec(
155 rtl_arena_type *, void * address, sal_Size size)
156 {
157 #if defined SAL_UNX
158 munmap(static_cast< char * >(address), size);
159 #elif defined SAL_W32
160 (void) size; // unused
161 VirtualFree(address, 0, MEM_RELEASE);
162 #elif defined(SAL_OS2)
163 (void) DosFreeMem( address);
164 #endif
165 }
166
167 }
168
169 class VtableFactory::GuardedBlocks: public std::vector< Block > {
170 public:
GuardedBlocks(VtableFactory const & factory)171 GuardedBlocks(VtableFactory const & factory):
172 m_factory(factory), m_guarded(true) {}
173
174 ~GuardedBlocks();
175
unguard()176 void unguard() { m_guarded = false; }
177
178 private:
179 GuardedBlocks(GuardedBlocks &); // not implemented
180 void operator =(GuardedBlocks); // not implemented
181
182 VtableFactory const & m_factory;
183 bool m_guarded;
184 };
185
~GuardedBlocks()186 VtableFactory::GuardedBlocks::~GuardedBlocks() {
187 if (m_guarded) {
188 for (iterator i(begin()); i != end(); ++i) {
189 m_factory.freeBlock(*i);
190 }
191 }
192 }
193
194 class VtableFactory::BaseOffset {
195 public:
BaseOffset(typelib_InterfaceTypeDescription * type)196 BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); }
197
getFunctionOffset(rtl::OUString const & name) const198 sal_Int32 getFunctionOffset(rtl::OUString const & name) const
199 { return m_map.find(name)->second; }
200
201 private:
202 sal_Int32 calculate(
203 typelib_InterfaceTypeDescription * type, sal_Int32 offset);
204
205 typedef std::hash_map< rtl::OUString, sal_Int32, rtl::OUStringHash > Map;
206
207 Map m_map;
208 };
209
calculate(typelib_InterfaceTypeDescription * type,sal_Int32 offset)210 sal_Int32 VtableFactory::BaseOffset::calculate(
211 typelib_InterfaceTypeDescription * type, sal_Int32 offset)
212 {
213 rtl::OUString name(type->aBase.pTypeName);
214 if (m_map.find(name) == m_map.end()) {
215 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
216 offset = calculate(type->ppBaseTypes[i], offset);
217 }
218 m_map.insert(Map::value_type(name, offset));
219 typelib_typedescription_complete(
220 reinterpret_cast< typelib_TypeDescription ** >(&type));
221 offset += bridges::cpp_uno::shared::getLocalFunctions(type);
222 }
223 return offset;
224 }
225
VtableFactory()226 VtableFactory::VtableFactory(): m_arena(
227 rtl_arena_create(
228 "bridges::cpp_uno::shared::VtableFactory",
229 sizeof (void *), // to satisfy alignment requirements
230 0, reinterpret_cast< rtl_arena_type * >( 0 ), allocExec, freeExec, 0))
231 {
232 if (m_arena == 0) {
233 throw std::bad_alloc();
234 }
235 }
236
~VtableFactory()237 VtableFactory::~VtableFactory() {
238 {
239 osl::MutexGuard guard(m_mutex);
240 for (Map::iterator i(m_map.begin()); i != m_map.end(); ++i) {
241 for (sal_Int32 j = 0; j < i->second.count; ++j) {
242 freeBlock(i->second.blocks[j]);
243 }
244 delete[] i->second.blocks;
245 }
246 }
247 rtl_arena_destroy(m_arena);
248 }
249
getVtables(typelib_InterfaceTypeDescription * type)250 VtableFactory::Vtables VtableFactory::getVtables(
251 typelib_InterfaceTypeDescription * type)
252 {
253 rtl::OUString name(type->aBase.pTypeName);
254 osl::MutexGuard guard(m_mutex);
255 Map::iterator i(m_map.find(name));
256 if (i == m_map.end()) {
257 GuardedBlocks blocks(*this);
258 createVtables(blocks, BaseOffset(type), type, true);
259 Vtables vtables;
260 OSL_ASSERT(blocks.size() <= SAL_MAX_INT32);
261 vtables.count = static_cast< sal_Int32 >(blocks.size());
262 bridges::cpp_uno::shared::GuardedArray< Block > guardedBlocks(
263 new Block[vtables.count]);
264 vtables.blocks = guardedBlocks.get();
265 for (sal_Int32 j = 0; j < vtables.count; ++j) {
266 vtables.blocks[j] = blocks[j];
267 }
268 i = m_map.insert(Map::value_type(name, vtables)).first;
269 guardedBlocks.release();
270 blocks.unguard();
271 }
272 return i->second;
273 }
274
275 #ifdef USE_DOUBLE_MMAP
createBlock(Block & block,sal_Int32 slotCount) const276 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
277 {
278 sal_Size size = getBlockSize(slotCount);
279 sal_Size pagesize = sysconf(_SC_PAGESIZE);
280 block.size = (size + (pagesize - 1)) & ~(pagesize - 1);
281 block.start = block.exec = NULL;
282 block.fd = -1;
283
284 osl::Security aSecurity;
285 rtl::OUString strDirectory;
286 rtl::OUString strURLDirectory;
287 if (aSecurity.getHomeDir(strURLDirectory))
288 osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory);
289
290 for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i)
291 {
292 if (strDirectory.isEmpty())
293 strDirectory = rtl::OUString::createFromAscii("/tmp");
294
295 strDirectory += rtl::OUString::createFromAscii("/.execoooXXXXXX");
296 rtl::OString aTmpName = rtl::OUStringToOString(strDirectory, osl_getThreadTextEncoding());
297 char *tmpfname = new char[aTmpName.getLength()+1];
298 strncpy(tmpfname, aTmpName.getStr(), aTmpName.getLength()+1);
299 if ((block.fd = mkstemp(tmpfname)) == -1)
300 perror("creation of executable memory area failed");
301 if (block.fd == -1)
302 {
303 delete[] tmpfname;
304 break;
305 }
306 unlink(tmpfname);
307 delete[] tmpfname;
308 if (ftruncate(block.fd, block.size) == -1)
309 {
310 perror("truncation of executable memory area failed");
311 close(block.fd);
312 block.fd = -1;
313 break;
314 }
315 block.start = mmap(NULL, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0);
316 if (block.start== MAP_FAILED) {
317 block.start = 0;
318 }
319 block.exec = mmap(NULL, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0);
320 if (block.exec == MAP_FAILED) {
321 block.exec = 0;
322 }
323
324 //All good
325 if (block.start && block.exec && block.fd != -1)
326 break;
327
328 freeBlock(block);
329
330 strDirectory = rtl::OUString();
331 }
332 if (!block.start || !block.exec || block.fd == -1)
333 {
334 //Fall back to non-doublemmaped allocation
335 block.fd = -1;
336 block.start = block.exec = rtl_arena_alloc(m_arena, &block.size);
337 }
338 return (block.start != 0 && block.exec != 0);
339 }
340
freeBlock(Block const & block) const341 void VtableFactory::freeBlock(Block const & block) const {
342 //if the double-map failed we were allocated on the arena
343 if (block.fd == -1 && block.start == block.exec && block.start != NULL)
344 rtl_arena_free(m_arena, block.start, block.size);
345 else
346 {
347 if (block.start) munmap(block.start, block.size);
348 if (block.exec) munmap(block.exec, block.size);
349 if (block.fd != -1) close(block.fd);
350 }
351 }
352 #else
createBlock(Block & block,sal_Int32 slotCount) const353 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
354 {
355 #if defined MACOSX && defined AARCH64
356 JitWriteGuard jitWriteGuard;
357 #endif
358 block.size = getBlockSize(slotCount);
359 block.start = rtl_arena_alloc(m_arena, &block.size);
360 return block.start != 0;
361 }
362
freeBlock(Block const & block) const363 void VtableFactory::freeBlock(Block const & block) const {
364 #if defined MACOSX && defined AARCH64
365 JitWriteGuard jitWriteGuard;
366 #endif
367 rtl_arena_free(m_arena, block.start, block.size);
368 }
369 #endif
370
createVtables(GuardedBlocks & blocks,BaseOffset const & baseOffset,typelib_InterfaceTypeDescription * type,bool includePrimary) const371 void VtableFactory::createVtables(
372 GuardedBlocks & blocks, BaseOffset const & baseOffset,
373 typelib_InterfaceTypeDescription * type, bool includePrimary) const
374 {
375 if (includePrimary) {
376 sal_Int32 slotCount
377 = bridges::cpp_uno::shared::getPrimaryFunctions(type);
378 Block block;
379 if (!createBlock(block, slotCount)) {
380 throw std::bad_alloc();
381 }
382 try {
383 #if defined MACOSX && defined AARCH64
384 JitWriteGuard jitWriteGuard;
385 #endif
386 Slot * slots = initializeBlock(block.start, slotCount);
387 unsigned char * codeBegin =
388 reinterpret_cast< unsigned char * >(slots);
389 unsigned char * code = codeBegin;
390 sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *);
391 for (typelib_InterfaceTypeDescription const * type2 = type;
392 type2 != 0; type2 = type2->pBaseTypeDescription)
393 {
394 code = addLocalFunctions(
395 &slots, code,
396 #ifdef USE_DOUBLE_MMAP
397 sal_IntPtr(block.exec) - sal_IntPtr(block.start),
398 #endif
399 type2,
400 baseOffset.getFunctionOffset(type2->aBase.pTypeName),
401 bridges::cpp_uno::shared::getLocalFunctions(type2),
402 vtableOffset);
403 }
404 flushCode(codeBegin, code);
405 #ifdef USE_DOUBLE_MMAP
406 //Finished generating block, swap writable pointer with executable
407 //pointer
408 ::std::swap(block.start, block.exec);
409 #endif
410 blocks.push_back(block);
411 } catch (...) {
412 freeBlock(block);
413 throw;
414 }
415 }
416 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
417 createVtables(blocks, baseOffset, type->ppBaseTypes[i], i != 0);
418 }
419 }
420