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