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