1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include "bridges/cpp_uno/shared/vtablefactory.hxx"
32 #include "guardedarray.hxx"
34 #include "bridges/cpp_uno/shared/vtables.hxx"
36 #include "osl/thread.h"
37 #include "osl/security.hxx"
38 #include "osl/file.hxx"
39 #include "osl/diagnose.h"
40 #include "osl/mutex.hxx"
41 #include "rtl/alloc.h"
42 #include "rtl/ustring.hxx"
43 #include "sal/log.hxx"
44 #include "sal/types.h"
45 #include "typelib/typedescription.hxx"
47 #include <boost/unordered_map.hpp>
57 #define WIN32_LEAN_AND_MEAN
59 #pragma warning(push,1) // disable warnings within system headers
66 #error Unsupported platform
69 #if defined USE_DOUBLE_MMAP
73 using bridges::cpp_uno::shared::VtableFactory
;
77 extern "C" void * SAL_CALL
allocExec(
78 SAL_UNUSED_PARAMETER rtl_arena_type
*, sal_Size
* size
)
82 #if defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
83 pagesize
= getpagesize();
85 pagesize
= sysconf(_SC_PAGESIZE
);
90 pagesize
= info
.dwPageSize
;
92 #error Unsupported platform
94 sal_Size n
= (*size
+ (pagesize
- 1)) & ~(pagesize
- 1);
98 0, n
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANON
, -1,
100 if (p
== MAP_FAILED
) {
103 else if (mprotect (static_cast<char*>(p
), n
, PROT_READ
| PROT_WRITE
| PROT_EXEC
) == -1)
105 munmap (static_cast<char*>(p
), n
);
108 #elif defined SAL_W32
109 p
= VirtualAlloc(0, n
, MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
117 extern "C" void SAL_CALL
freeExec(
118 SAL_UNUSED_PARAMETER rtl_arena_type
*, void * address
, sal_Size size
)
121 munmap(static_cast< char * >(address
), size
);
122 #elif defined SAL_W32
123 (void) size
; // unused
124 VirtualFree(address
, 0, MEM_RELEASE
);
130 class VtableFactory::GuardedBlocks
: public std::vector
< Block
> {
132 GuardedBlocks(VtableFactory
const & factory
):
133 m_factory(factory
), m_guarded(true) {}
137 void unguard() { m_guarded
= false; }
140 GuardedBlocks(GuardedBlocks
&); // not implemented
141 void operator =(GuardedBlocks
); // not implemented
143 VtableFactory
const & m_factory
;
147 VtableFactory::GuardedBlocks::~GuardedBlocks() {
149 for (iterator
i(begin()); i
!= end(); ++i
) {
150 m_factory
.freeBlock(*i
);
155 class VtableFactory::BaseOffset
{
157 BaseOffset(typelib_InterfaceTypeDescription
* type
) { calculate(type
, 0); }
159 sal_Int32
getFunctionOffset(rtl::OUString
const & name
) const
160 { return m_map
.find(name
)->second
; }
164 typelib_InterfaceTypeDescription
* type
, sal_Int32 offset
);
166 typedef boost::unordered_map
< rtl::OUString
, sal_Int32
, rtl::OUStringHash
> Map
;
171 sal_Int32
VtableFactory::BaseOffset::calculate(
172 typelib_InterfaceTypeDescription
* type
, sal_Int32 offset
)
174 rtl::OUString
name(type
->aBase
.pTypeName
);
175 if (m_map
.find(name
) == m_map
.end()) {
176 for (sal_Int32 i
= 0; i
< type
->nBaseTypes
; ++i
) {
177 offset
= calculate(type
->ppBaseTypes
[i
], offset
);
179 m_map
.insert(Map::value_type(name
, offset
));
180 typelib_typedescription_complete(
181 reinterpret_cast< typelib_TypeDescription
** >(&type
));
182 offset
+= bridges::cpp_uno::shared::getLocalFunctions(type
);
187 VtableFactory::VtableFactory(): m_arena(
189 "bridges::cpp_uno::shared::VtableFactory",
190 sizeof (void *), // to satisfy alignment requirements
191 0, reinterpret_cast< rtl_arena_type
* >(-1), allocExec
, freeExec
, 0))
194 throw std::bad_alloc();
198 VtableFactory::~VtableFactory() {
200 osl::MutexGuard
guard(m_mutex
);
201 for (Map::iterator
i(m_map
.begin()); i
!= m_map
.end(); ++i
) {
202 for (sal_Int32 j
= 0; j
< i
->second
.count
; ++j
) {
203 freeBlock(i
->second
.blocks
[j
]);
205 delete[] i
->second
.blocks
;
208 rtl_arena_destroy(m_arena
);
211 VtableFactory::Vtables
VtableFactory::getVtables(
212 typelib_InterfaceTypeDescription
* type
)
214 rtl::OUString
name(type
->aBase
.pTypeName
);
215 osl::MutexGuard
guard(m_mutex
);
216 Map::iterator
i(m_map
.find(name
));
217 if (i
== m_map
.end()) {
218 GuardedBlocks
blocks(*this);
219 createVtables(blocks
, BaseOffset(type
), type
, true);
221 OSL_ASSERT(blocks
.size() <= SAL_MAX_INT32
);
222 vtables
.count
= static_cast< sal_Int32
>(blocks
.size());
223 bridges::cpp_uno::shared::GuardedArray
< Block
> guardedBlocks(
224 new Block
[vtables
.count
]);
225 vtables
.blocks
= guardedBlocks
.get();
226 for (sal_Int32 j
= 0; j
< vtables
.count
; ++j
) {
227 vtables
.blocks
[j
] = blocks
[j
];
229 i
= m_map
.insert(Map::value_type(name
, vtables
)).first
;
230 guardedBlocks
.release();
236 #ifdef USE_DOUBLE_MMAP
237 bool VtableFactory::createBlock(Block
&block
, sal_Int32 slotCount
) const
239 sal_Size size
= getBlockSize(slotCount
);
240 sal_Size pagesize
= sysconf(_SC_PAGESIZE
);
241 block
.size
= (size
+ (pagesize
- 1)) & ~(pagesize
- 1);
242 block
.start
= block
.exec
= NULL
;
245 osl::Security aSecurity
;
246 rtl::OUString strDirectory
;
247 rtl::OUString strURLDirectory
;
248 if (aSecurity
.getHomeDir(strURLDirectory
))
249 osl::File::getSystemPathFromFileURL(strURLDirectory
, strDirectory
);
251 for (int i
= strDirectory
.isEmpty() ? 1 : 0; i
< 2; ++i
)
253 if (strDirectory
.isEmpty())
254 strDirectory
= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/tmp" ));
256 strDirectory
+= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/.execoooXXXXXX" ));
257 rtl::OString aTmpName
= rtl::OUStringToOString(strDirectory
, osl_getThreadTextEncoding());
258 char *tmpfname
= new char[aTmpName
.getLength()+1];
259 strncpy(tmpfname
, aTmpName
.getStr(), aTmpName
.getLength()+1);
260 if ((block
.fd
= mkstemp(tmpfname
)) == -1)
261 fprintf(stderr
, "mkstemp(\"%s\") failed: %s\n", tmpfname
, strerror(errno
));
269 #if defined(HAVE_POSIX_FALLOCATE)
270 int err
= posix_fallocate(block
.fd
, 0, block
.size
);
272 int err
= ftruncate(block
.fd
, block
.size
);
276 #if defined(HAVE_POSIX_FALLOCATE)
277 SAL_WARN("bridges", "posix_fallocate failed with code " << err
);
279 SAL_WARN("bridges", "truncation of executable memory area failed with code " << err
);
285 block
.start
= mmap(NULL
, block
.size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, block
.fd
, 0);
286 if (block
.start
== MAP_FAILED
) {
289 block
.exec
= mmap(NULL
, block
.size
, PROT_READ
| PROT_EXEC
, MAP_SHARED
, block
.fd
, 0);
290 if (block
.exec
== MAP_FAILED
) {
295 if (block
.start
&& block
.exec
&& block
.fd
!= -1)
300 strDirectory
= rtl::OUString();
302 if (!block
.start
|| !block
.exec
|| block
.fd
== -1)
304 //Fall back to non-doublemmaped allocation
306 block
.start
= block
.exec
= rtl_arena_alloc(m_arena
, &block
.size
);
308 return (block
.start
!= 0 && block
.exec
!= 0);
311 void VtableFactory::freeBlock(Block
const & block
) const {
312 //if the double-map failed we were allocated on the arena
313 if (block
.fd
== -1 && block
.start
== block
.exec
&& block
.start
!= NULL
)
314 rtl_arena_free(m_arena
, block
.start
, block
.size
);
317 if (block
.start
) munmap(block
.start
, block
.size
);
318 if (block
.exec
) munmap(block
.exec
, block
.size
);
319 if (block
.fd
!= -1) close(block
.fd
);
323 bool VtableFactory::createBlock(Block
&block
, sal_Int32 slotCount
) const
325 block
.size
= getBlockSize(slotCount
);
326 block
.start
= rtl_arena_alloc(m_arena
, &block
.size
);
327 return block
.start
!= 0;
330 void VtableFactory::freeBlock(Block
const & block
) const {
331 rtl_arena_free(m_arena
, block
.start
, block
.size
);
335 void VtableFactory::createVtables(
336 GuardedBlocks
& blocks
, BaseOffset
const & baseOffset
,
337 typelib_InterfaceTypeDescription
* type
, bool includePrimary
) const
339 if (includePrimary
) {
341 = bridges::cpp_uno::shared::getPrimaryFunctions(type
);
343 if (!createBlock(block
, slotCount
)) {
344 throw std::bad_alloc();
347 Slot
* slots
= initializeBlock(block
.start
, slotCount
);
348 unsigned char * codeBegin
=
349 reinterpret_cast< unsigned char * >(slots
);
350 unsigned char * code
= codeBegin
;
351 sal_Int32 vtableOffset
= blocks
.size() * sizeof (Slot
*);
352 for (typelib_InterfaceTypeDescription
const * type2
= type
;
353 type2
!= 0; type2
= type2
->pBaseTypeDescription
)
355 code
= addLocalFunctions(
357 #ifdef USE_DOUBLE_MMAP
358 sal_IntPtr(block
.exec
) - sal_IntPtr(block
.start
),
361 baseOffset
.getFunctionOffset(type2
->aBase
.pTypeName
),
362 bridges::cpp_uno::shared::getLocalFunctions(type2
),
365 flushCode(codeBegin
, code
);
366 #ifdef USE_DOUBLE_MMAP
367 //Finished generating block, swap writable pointer with executable
369 ::std::swap(block
.start
, block
.exec
);
371 blocks
.push_back(block
);
377 for (sal_Int32 i
= 0; i
< type
->nBaseTypes
; ++i
) {
378 createVtables(blocks
, baseOffset
, type
->ppBaseTypes
[i
], i
!= 0);
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */