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