xref: /trunk/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/except.cxx (revision 9ff3e22531fe821dc01eb80c3b7fce8ee067d164)
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