xref: /trunk/main/bridges/source/cpp_uno/shared/vtablefactory.cxx (revision 75186a21de036465901271cdbb22555f755274df)
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