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 ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))
28 #include <exception>
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <dlfcn.h>
35 #include <cxxabi.h>
36 #include <hash_map>
37 #include <sys/param.h>
38
39 #include <rtl/strbuf.hxx>
40 #include <rtl/ustrbuf.hxx>
41 #include <osl/diagnose.h>
42 #include <osl/mutex.hxx>
43
44 #include <com/sun/star/uno/genfunc.hxx>
45 #include "com/sun/star/uno/RuntimeException.hpp"
46 #include <typelib/typedescription.hxx>
47 #include <uno/any2.h>
48
49 #include "share.hxx"
50
51
52 using namespace ::std;
53 using namespace ::osl;
54 using namespace ::rtl;
55 using namespace ::com::sun::star::uno;
56 using namespace ::__cxxabiv1;
57
58
59 namespace CPPU_CURRENT_NAMESPACE
60 {
61
62 namespace {
63
64 typedef hash_map< void *, typelib_TypeDescription * > ThrownTypes;
65 typedef hash_map< OUString, type_info *, OUStringHash > ObservedRttiMap;
66
thrownTypes()67 ThrownTypes & thrownTypes()
68 {
69 static ThrownTypes types;
70 return types;
71 }
72
observedRttis()73 ObservedRttiMap & observedRttis()
74 {
75 static ObservedRttiMap map;
76 return map;
77 }
78
79 // Guards BOTH thrownTypes() and observedRttis(). Every access to either map
80 // must hold this one mutex; they are plain hash_maps, so an insertion racing a
81 // find or erase is undefined behaviour. RTTI::m_mutex may be held while
82 // acquiring this one (see RTTI::getRTTI), never the other way round.
exceptionMapsMutex()83 Mutex & exceptionMapsMutex()
84 {
85 static Mutex mutex;
86 return mutex;
87 }
88
89 // libc++ marks a type_info whose object is not unique across images by setting
90 // the top bit of type_info::__type_name; comparison then falls back to strcmp
91 // of the mangled name (see __non_unique_arm_rtti_bit_impl in <typeinfo>). On
92 // arm64 Darwin clang emits the typeinfo of every keyless class -- which is every
93 // UNO exception -- hidden and therefore non-unique, so a synthesised object must
94 // set the bit too, or std::type_info::operator== degenerates to an address
95 // comparison and never matches the handler's real typeinfo.
96 sal_uIntPtr const NON_UNIQUE_RTTI_BIT =
97 static_cast< sal_uIntPtr >(1) << (8 * sizeof (sal_uIntPtr) - 1);
98
siDonor()99 RttiSiClassLayout const * siDonor()
100 {
101 return reinterpret_cast< RttiSiClassLayout const * >( &typeid(RttiDonorDerived) );
102 }
classDonor()103 RttiClassLayout const * classDonor()
104 {
105 return reinterpret_cast< RttiClassLayout const * >( &typeid(RttiDonorBase) );
106 }
107
108 // Refuse to synthesise unless the donors really have the layout we assume.
rttiDonorsUsable()109 bool rttiDonorsUsable()
110 {
111 return sizeof (void *) == 8
112 && siDonor()->pBase == static_cast< void const * >( classDonor() );
113 }
114
115 // Mirror the platform's own convention rather than assuming it.
rttiIsNonUnique()116 bool rttiIsNonUnique()
117 {
118 return (siDonor()->nName & NON_UNIQUE_RTTI_BIT) != 0;
119 }
120
121 }
122
dummy_can_throw_anything(char const *)123 void dummy_can_throw_anything( char const * )
124 {
125 }
126
127 //==================================================================================================
toUNOname(char const * p)128 static OUString toUNOname( char const * p ) SAL_THROW( () )
129 {
130 #if OSL_DEBUG_LEVEL > 1
131 char const * start = p;
132 #endif
133
134 // example: N3com3sun4star4lang24IllegalArgumentExceptionE
135
136 OUStringBuffer buf( 64 );
137 OSL_ASSERT( 'N' == *p );
138 ++p; // skip N
139
140 while ('E' != *p)
141 {
142 // read chars count
143 long n = (*p++ - '0');
144 while ('0' <= *p && '9' >= *p)
145 {
146 n *= 10;
147 n += (*p++ - '0');
148 }
149 buf.appendAscii( p, n );
150 p += n;
151 if ('E' != *p)
152 buf.append( (sal_Unicode)'.' );
153 }
154
155 #if OSL_DEBUG_LEVEL > 1
156 OUString ret( buf.makeStringAndClear() );
157 OString c_ret( OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US ) );
158 fprintf( stderr, "> toUNOname(): %s => %s\n", start, c_ret.getStr() );
159 return ret;
160 #else
161 return buf.makeStringAndClear();
162 #endif
163 }
164
165 //==================================================================================================
mangledRttiSymbol(OUString const & unoName)166 static OString mangledRttiSymbol( OUString const & unoName ) SAL_THROW( () )
167 {
168 OStringBuffer buf( 64 );
169 buf.append( RTL_CONSTASCII_STRINGPARAM("_ZTIN") );
170 sal_Int32 index = 0;
171 do
172 {
173 OUString token( unoName.getToken( 0, '.', index ) );
174 buf.append( token.getLength() );
175 OString c_token( OUStringToOString( token, RTL_TEXTENCODING_ASCII_US ) );
176 buf.append( c_token );
177 }
178 while (index >= 0);
179 buf.append( 'E' );
180 return buf.makeStringAndClear();
181 }
182
183 //==================================================================================================
184 class RTTI
185 {
186 typedef hash_map< OUString, type_info *, OUStringHash > t_rtti_map;
187
188 Mutex m_mutex;
189 t_rtti_map m_rttis;
190 t_rtti_map m_generatedRttis;
191
192 type_info * synthesiseRTTI(
193 OString const & rSymbolName,
194 typelib_CompoundTypeDescription * pTypeDescr ) SAL_THROW( () );
195
196 public:
197 RTTI() SAL_THROW( () );
198 ~RTTI() SAL_THROW( () );
199
200 type_info * getRTTI( typelib_CompoundTypeDescription * ) SAL_THROW( () );
201 };
202
203 //__________________________________________________________________________________________________
RTTI()204 RTTI::RTTI() SAL_THROW( () )
205 {
206 }
207
208 //__________________________________________________________________________________________________
~RTTI()209 RTTI::~RTTI() SAL_THROW( () )
210 {
211 }
212
213 //__________________________________________________________________________________________________
getRTTI(typelib_CompoundTypeDescription * pTypeDescr)214 type_info * RTTI::getRTTI( typelib_CompoundTypeDescription *pTypeDescr ) SAL_THROW( () )
215 {
216 OUString const & unoName = *(OUString const *)&pTypeDescr->aBase.pTypeName;
217
218 // Recursive: synthesiseRTTI() re-enters getRTTI() for the base chain.
219 // osl::Mutex is a PTHREAD_MUTEX_RECURSIVE (sal/osl/unx/mutex.c), so this
220 // is safe. Lock order against exceptionMapsMutex() is unchanged.
221 MutexGuard guard( m_mutex );
222
223 {
224 MutexGuard observedGuard( exceptionMapsMutex() );
225 ObservedRttiMap::const_iterator observed( observedRttis().find( unoName ) );
226 if ( observed != observedRttis().end() )
227 return observed->second; // a real typeinfo always wins
228 }
229
230 t_rtti_map::const_iterator iFind( m_rttis.find( unoName ) );
231 if (iFind != m_rttis.end())
232 return iFind->second;
233
234 OString symName( mangledRttiSymbol( unoName ) );
235 type_info * rtti = static_cast<std::type_info *>(dlsym( RTLD_DEFAULT, symName.getStr() ));
236 if (rtti != 0)
237 {
238 m_rttis.insert( t_rtti_map::value_type( unoName, rtti ) );
239 return rtti;
240 }
241
242 t_rtti_map::const_iterator iGen( m_generatedRttis.find( unoName ) );
243 if (iGen != m_generatedRttis.end())
244 return iGen->second;
245
246 // On arm64 Darwin, clang emits the typeinfo of every keyless class (which
247 // is every UNO exception) hidden, so the dlsym() lookup above can never
248 // succeed here -- see solenv/src/component.map for the full explanation.
249 // Synthesise one instead of degrading straight to a RuntimeException.
250 rtti = synthesiseRTTI( symName, pTypeDescr );
251 if (rtti != 0)
252 m_generatedRttis.insert( t_rtti_map::value_type( unoName, rtti ) );
253 return rtti;
254 }
255
256 //__________________________________________________________________________________________________
synthesiseRTTI(OString const & rSymbolName,typelib_CompoundTypeDescription * pTypeDescr)257 type_info * RTTI::synthesiseRTTI(
258 OString const & rSymbolName,
259 typelib_CompoundTypeDescription * pTypeDescr ) SAL_THROW( () )
260 {
261 if (! rttiDonorsUsable())
262 return 0; // keep the loud RuntimeException fallback
263
264 type_info * pBaseRtti = 0;
265 if (pTypeDescr->pBaseTypeDescription != 0)
266 {
267 // The whole chain must resolve: libc++abi walks __base_type when matching
268 // a handler for a base class and would dereference a null link.
269 pBaseRtti = getRTTI(
270 (typelib_CompoundTypeDescription *) pTypeDescr->pBaseTypeDescription );
271 if (pBaseRtti == 0)
272 return 0;
273 }
274
275 // The mangled type name is the symbol name without its "_ZTI" prefix.
276 char * pName = strdup( rSymbolName.getStr() + 4 );
277 if (pName == 0)
278 return 0;
279 sal_uIntPtr nName = reinterpret_cast< sal_uIntPtr >( pName );
280 if (rttiIsNonUnique())
281 nName |= NON_UNIQUE_RTTI_BIT;
282
283 // Deliberately never freed; these live for the life of the process
284 // (the module already builds with -DLEAK_STATIC_DATA).
285 if (pBaseRtti != 0)
286 {
287 RttiSiClassLayout * p = static_cast< RttiSiClassLayout * >(
288 calloc( 1, sizeof (RttiSiClassLayout) ) );
289 if (p == 0) { free( pName ); return 0; }
290 p->pVtable = siDonor()->pVtable;
291 p->nName = nName;
292 p->pBase = pBaseRtti;
293 return reinterpret_cast< type_info * >( p );
294 }
295
296 RttiClassLayout * p = static_cast< RttiClassLayout * >(
297 calloc( 1, sizeof (RttiClassLayout) ) );
298 if (p == 0) { free( pName ); return 0; }
299 p->pVtable = classDonor()->pVtable;
300 p->nName = nName;
301 return reinterpret_cast< type_info * >( p );
302 }
303
304 //--------------------------------------------------------------------------------------------------
deleteException(void * pExc)305 static void deleteException( void * pExc )
306 {
307 typelib_TypeDescription * pTD = 0;
308 {
309 MutexGuard guard( exceptionMapsMutex() );
310 ThrownTypes::iterator i = thrownTypes().find( pExc );
311 if ( i != thrownTypes().end() )
312 {
313 pTD = i->second;
314 thrownTypes().erase( i );
315 }
316 }
317 if ( pTD )
318 {
319 ::uno_destructData( pExc, pTD, cpp_release );
320 ::typelib_typedescription_release( pTD );
321 }
322 }
323
324 //==================================================================================================
raiseException(uno_Any * pUnoExc,uno_Mapping * pUno2Cpp)325 void raiseException( uno_Any * pUnoExc, uno_Mapping * pUno2Cpp )
326 {
327 #if OSL_DEBUG_LEVEL > 1
328 OString cstr(
329 OUStringToOString(
330 *reinterpret_cast< OUString const * >( &pUnoExc->pType->pTypeName ),
331 RTL_TEXTENCODING_ASCII_US ) );
332 fprintf( stderr, "> uno exception occurred: %s\n", cstr.getStr() );
333 #endif
334 void * pCppExc;
335 type_info * rtti;
336 OUString typeName(
337 *reinterpret_cast< OUString const * >( &pUnoExc->pType->pTypeName ) );
338
339 // Every UNO exception derives from com.sun.star.uno.Exception, whose first
340 // member is the Message string. Keep a copy: if the throw below cannot be
341 // completed we substitute a RuntimeException, and without this the original
342 // diagnostic would be lost silently.
343 OUString message;
344 if ( pUnoExc->pData != 0 &&
345 *reinterpret_cast< rtl_uString * const * >( pUnoExc->pData ) != 0 )
346 {
347 message = *reinterpret_cast< OUString const * >( pUnoExc->pData );
348 }
349
350 {
351 // construct cpp exception object
352 typelib_TypeDescription * pTypeDescr = 0;
353 TYPELIB_DANGER_GET( &pTypeDescr, pUnoExc->pType );
354 OSL_ASSERT( pTypeDescr );
355 if (! pTypeDescr)
356 {
357 // NOTE: pUnoExc is deliberately left alone here. Destructing an any
358 // whose type description cannot be resolved is not safe, so this path
359 // leaks it rather than risking a null dereference. It only fires if
360 // the type system has already lost the type being thrown.
361 throw RuntimeException(
362 OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get typedescription for type ") ) +
363 typeName + OUString( RTL_CONSTASCII_USTRINGPARAM(": ") ) + message,
364 Reference< XInterface >() );
365 }
366
367 pCppExc = __cxa_allocate_exception( pTypeDescr->nSize );
368 ::uno_copyAndConvertData( pCppExc, pUnoExc->pData, pTypeDescr, pUno2Cpp );
369
370 static RTTI rtti_data;
371 rtti = rtti_data.getRTTI(
372 (typelib_CompoundTypeDescription *) pTypeDescr );
373 OSL_ENSURE( rtti, "### no rtti for throwing exception!" );
374 if (! rtti)
375 {
376 // Undo everything done above: the payload was constructed into the
377 // __cxa buffer, so it has to be destructed before the buffer is
378 // released, and raiseException still owes its caller the destruction
379 // of the incoming any.
380 ::uno_destructData( pCppExc, pTypeDescr, cpp_release );
381 __cxa_free_exception( pCppExc );
382 TYPELIB_DANGER_RELEASE( pTypeDescr );
383 ::uno_any_destruct( pUnoExc, 0 );
384 throw RuntimeException(
385 OUString( RTL_CONSTASCII_USTRINGPARAM("no rtti for type ") ) +
386 typeName + OUString( RTL_CONSTASCII_USTRINGPARAM(": ") ) + message,
387 Reference< XInterface >() );
388 }
389
390 {
391 MutexGuard guard( exceptionMapsMutex() );
392 typelib_typedescription_acquire( pTypeDescr );
393 thrownTypes()[pCppExc] = pTypeDescr;
394 }
395 TYPELIB_DANGER_RELEASE( pTypeDescr );
396
397 // The C++ payload and its retained type description now own the value.
398 ::uno_any_destruct( pUnoExc, 0 );
399 }
400
401 __cxa_throw( pCppExc, rtti, deleteException );
402 }
403
fillUnoException(std::type_info const & type,void * exception,uno_Any * pUnoExc,uno_Mapping * pCpp2Uno)404 void fillUnoException(
405 std::type_info const & type, void * exception, uno_Any * pUnoExc,
406 uno_Mapping * pCpp2Uno )
407 {
408 typelib_TypeDescription * pExcTypeDescr = 0;
409 OUString unoName( toUNOname( type.name() ) );
410 {
411 MutexGuard guard( exceptionMapsMutex() );
412 observedRttis()[unoName] = const_cast<std::type_info *>( &type );
413 }
414 typelib_typedescription_getByName( &pExcTypeDescr, unoName.pData );
415 if ( pExcTypeDescr == 0 )
416 {
417 RuntimeException aRE(
418 OUString( RTL_CONSTASCII_USTRINGPARAM("exception type not found: ") ) + unoName,
419 Reference< XInterface >() );
420 Type const & rType = ::getCppuType( &aRE );
421 uno_type_any_constructAndConvert(
422 pUnoExc, &aRE, rType.getTypeLibType(), pCpp2Uno );
423 }
424 else
425 {
426 uno_any_constructAndConvert(
427 pUnoExc, exception, pExcTypeDescr, pCpp2Uno );
428 typelib_typedescription_release( pExcTypeDescr );
429 }
430 }
431
432 }
433