1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 #include <cstddef>
24 #include <cstdint>
25 #include <cstring>
26 #include <typeinfo>
27 
28 #include <dlfcn.h>
29 
30 #include <com/sun/star/uno/RuntimeException.hpp>
31 #include <com/sun/star/uno/genfunc.h>
32 #include <rtl/strbuf.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <rtl/ustring.hxx>
35 #include <sal/types.h>
36 #include <typelib/typeclass.h>
37 #include <typelib/typedescription.h>
38 #include <uno/any2.h>
39 #include <uno/mapping.h>
40 
41 #include "abi.hxx"
42 #include <osl/mutex.hxx>
43 #include <unordered_map>
44 
45 namespace {
46 
toUnoName(char const * name)47 OUString toUnoName(char const * name) {
48     assert(name != nullptr);
49     OUStringBuffer b;
50     bool scoped = *name == 'N';
51     if (scoped) {
52         ++name;
53     }
54     for (;;) {
55         assert(*name >= '0' && *name <= '9');
56         std::size_t n = *name++ - '0';
57         while (*name >= '0' && *name <= '9') {
58             n = 10 * n + (*name++ - '0');
59         }
60         b.appendAscii(name, n);
61         name += n;
62         if (!scoped) {
63             assert(*name == 0);
64             break;
65         }
66         if (*name == 'E') {
67             assert(name[1] == 0);
68             break;
69         }
70         b.append('.');
71     }
72     return b.makeStringAndClear();
73 }
74 
75 class Rtti {
76 public:
Rtti()77     Rtti(): app_(dlopen(nullptr, RTLD_LAZY)) {}
78 
~Rtti()79     ~Rtti() { dlclose(app_); }
80 
81     std::type_info * getRtti(typelib_TypeDescription const & type);
82 
83 private:
84     typedef std::unordered_map<OUString, std::type_info *> Map;
85 
86     void * app_;
87 
88     osl::Mutex mutex_;
89     Map map_;
90 };
91 
getRtti(typelib_TypeDescription const & type)92 std::type_info * Rtti::getRtti(typelib_TypeDescription const & type) {
93     OUString unoName(type.pTypeName);
94     osl::MutexGuard g(mutex_);
95     Map::iterator i(map_.find(unoName));
96     if (i == map_.end()) {
97         OStringBuffer b;
98         b.append("_ZTIN");
99         for (sal_Int32 j = 0; j != -1;) {
100             OString t(
101                 OUStringToOString(
102                     unoName.getToken(0, '.', j), RTL_TEXTENCODING_ASCII_US));
103             b.append(t.getLength());
104             b.append(t);
105         }
106         b.append('E');
107         OString sym(b.makeStringAndClear());
108         std::type_info * rtti = static_cast<std::type_info *>(
109             dlsym(app_, sym.getStr()));
110         if (rtti == nullptr) {
111             char const * rttiName = strdup(sym.getStr() + std::strlen("_ZTI"));
112             if (rttiName == nullptr) {
113                 throw std::bad_alloc();
114             }
115 #if defined MACOSX
116             // For the Apple ARM64 ABI, if the most significant ("non-unique RTTI") bit is set, it
117             // means that the instance of the name is not unique (and thus RTTI equality needs to be
118             // determined by string comparison rather than by pointer comparison):
119             rttiName = reinterpret_cast<char const *>(
120                 reinterpret_cast<std::uintptr_t>(rttiName) | 0x8000'0000'0000'0000);
121 #endif
122             assert(type.eTypeClass == typelib_TypeClass_EXCEPTION);
123             typelib_CompoundTypeDescription const & ctd
124                 = reinterpret_cast<typelib_CompoundTypeDescription const &>(
125                     type);
126             if (ctd.pBaseTypeDescription == nullptr) {
127                 rtti = new __cxxabiv1::__class_type_info(rttiName);
128             } else {
129                 std::type_info * base = getRtti(
130                     ctd.pBaseTypeDescription->aBase);
131                 rtti = new __cxxabiv1::__si_class_type_info(
132                     rttiName,
133                     static_cast<__cxxabiv1::__class_type_info *>(base));
134             }
135         }
136         i = map_.insert(Map::value_type(unoName, rtti)).first;
137     }
138     return i->second;
139 }
140 
141 struct theRttiFactory: public rtl::Static<Rtti, theRttiFactory> {};
142 
getRtti(typelib_TypeDescription const & type)143 std::type_info * getRtti(typelib_TypeDescription const & type) {
144     return theRttiFactory::get().getRtti(type);
145 }
146 
deleteException(void * exception)147 extern "C" void _GLIBCXX_CDTOR_CALLABI deleteException(void * exception) {
148     __cxxabiv1::__cxa_exception * header =
149         static_cast<__cxxabiv1::__cxa_exception *>(exception) - 1;
150 #if 1
151     // First, the libcxxabi commit
152     // <http://llvm.org/viewvc/llvm-project?view=revision&revision=303175>
153     // "[libcxxabi] Align unwindHeader on a double-word boundary" towards
154     // LLVM 5.0 changed the size of __cxa_exception by adding
155     //
156     //   __attribute__((aligned))
157     //
158     // to the final member unwindHeader, on x86-64 effectively adding a hole of
159     // size 8 in front of that member (changing its offset from 88 to 96,
160     // sizeof(__cxa_exception) from 120 to 128, and alignof(__cxa_exception)
161     // from 8 to 16); the "header1" hack below to dynamically determine whether we run against a
162     // LLVM 5 libcxxabi is to look at the exceptionDestructor member, which must
163     // point to this function (the use of __cxa_exception in mapException is
164     // unaffected, as it only accesses members towards the start of the struct,
165     // through a pointer known to actually point at the start).  The libcxxabi commit
166     // <https://github.com/llvm/llvm-project/commit/9ef1daa46edb80c47d0486148c0afc4e0d83ddcf>
167     // "Insert padding before the __cxa_exception header to ensure the thrown" in LLVM 6
168     // removes the need for this hack, so the "header1" hack can be removed again once we can be
169     // sure that we only run against libcxxabi from LLVM >= 6.
170     //
171     // Second, the libcxxabi commit
172     // <https://github.com/llvm/llvm-project/commit/674ec1eb16678b8addc02a4b0534ab383d22fa77>
173     // "[libcxxabi] Insert padding in __cxa_exception struct for compatibility" in LLVM 10 changed
174     // the layout of the start of __cxa_exception to
175     //
176     //  [8 byte  void *reserve]
177     //   8 byte  size_t referenceCount
178     //
179     // so the "header2" hack below to dynamically determine whether we run against a LLVM >= 10
180     // libcxxabi is to look whether the exceptionDestructor (with its known value) has increased its
181     // offset by 8.  As described in the definition of __cxa_exception
182     // (bridges/source/cpp_uno/gcc3_linux_aarch64/abi.hxx), the "header2" hack (together with the
183     // "#ifdef MACOSX" in the definition of __cxa_exception and the corresponding hack in call in
184     // bridges/source/cpp_uno/gcc3_linux_aarch64/uno2cpp.cxx) can be dropped once we can be sure
185     // that we only run against new libcxxabi that has the reserve member.
186     if (header->exceptionDestructor != &deleteException) {
187         auto const header1 = reinterpret_cast<__cxxabiv1::__cxa_exception *>(
188             reinterpret_cast<char *>(header) - 8);
189         if (header1->exceptionDestructor == &deleteException) {
190             header = header1;
191         } else {
192             auto const header2 = reinterpret_cast<__cxxabiv1::__cxa_exception *>(
193                 reinterpret_cast<char *>(header) + 8);
194             if (header2->exceptionDestructor == &deleteException) {
195                 header = header2;
196             } else {
197                 assert(false);
198             }
199         }
200     }
201 #endif
202     assert(header->exceptionDestructor == &deleteException);
203     OUString unoName(toUnoName(header->exceptionType->name()));
204     typelib_TypeDescription * td = nullptr;
205     typelib_typedescription_getByName(&td, unoName.pData);
206     assert(td != nullptr);
207     uno_destructData(exception, td, &css::uno::cpp_release);
208     typelib_typedescription_release(td);
209 }
210 
211 enum StructKind {
212     STRUCT_KIND_EMPTY, STRUCT_KIND_FLOAT, STRUCT_KIND_DOUBLE, STRUCT_KIND_POD,
213     STRUCT_KIND_DTOR
214 };
215 
getStructKind(typelib_CompoundTypeDescription const * type)216 StructKind getStructKind(typelib_CompoundTypeDescription const * type) {
217     StructKind k = type->pBaseTypeDescription == nullptr
218         ? STRUCT_KIND_EMPTY : getStructKind(type->pBaseTypeDescription);
219     for (sal_Int32 i = 0; i != type->nMembers; ++i) {
220         StructKind k2 = StructKind();
221         switch (type->ppTypeRefs[i]->eTypeClass) {
222         case typelib_TypeClass_BOOLEAN:
223         case typelib_TypeClass_BYTE:
224         case typelib_TypeClass_SHORT:
225         case typelib_TypeClass_UNSIGNED_SHORT:
226         case typelib_TypeClass_LONG:
227         case typelib_TypeClass_UNSIGNED_LONG:
228         case typelib_TypeClass_HYPER:
229         case typelib_TypeClass_UNSIGNED_HYPER:
230         case typelib_TypeClass_CHAR:
231         case typelib_TypeClass_ENUM:
232             k2 = STRUCT_KIND_POD;
233             break;
234         case typelib_TypeClass_FLOAT:
235             k2 = STRUCT_KIND_FLOAT;
236             break;
237         case typelib_TypeClass_DOUBLE:
238             k2 = STRUCT_KIND_DOUBLE;
239             break;
240         case typelib_TypeClass_STRING:
241         case typelib_TypeClass_TYPE:
242         case typelib_TypeClass_ANY:
243         case typelib_TypeClass_SEQUENCE:
244         case typelib_TypeClass_INTERFACE:
245             k2 = STRUCT_KIND_DTOR;
246             break;
247         case typelib_TypeClass_STRUCT:
248             {
249                 typelib_TypeDescription * td = nullptr;
250                 TYPELIB_DANGER_GET(&td, type->ppTypeRefs[i]);
251                 k2 = getStructKind(
252                     reinterpret_cast<typelib_CompoundTypeDescription const *>(
253                         td));
254                 TYPELIB_DANGER_RELEASE(td);
255                 break;
256             }
257         default:
258             assert(false);
259         }
260         switch (k2) {
261         case STRUCT_KIND_EMPTY:
262             // this means an empty sub-object, which nevertheless obtains a byte
263             // of storage (TODO: does it?), so the full object cannot be a
264             // homogeneous collection of float or double
265         case STRUCT_KIND_POD:
266             assert(k != STRUCT_KIND_DTOR);
267             k = STRUCT_KIND_POD;
268             break;
269         case STRUCT_KIND_FLOAT:
270         case STRUCT_KIND_DOUBLE:
271             if (k == STRUCT_KIND_EMPTY) {
272                 k = k2;
273             } else if (k != k2) {
274                 assert(k != STRUCT_KIND_DTOR);
275                 k = STRUCT_KIND_POD;
276             }
277             break;
278         case STRUCT_KIND_DTOR:
279             return STRUCT_KIND_DTOR;
280         }
281     }
282     return k;
283 }
284 
285 }
286 
287 namespace abi_aarch64 {
288 
mapException(__cxxabiv1::__cxa_exception * exception,std::type_info const * type,uno_Any * any,uno_Mapping * mapping)289 void mapException(
290     __cxxabiv1::__cxa_exception * exception, std::type_info const * type, uno_Any * any, uno_Mapping * mapping)
291 {
292     assert(exception != nullptr);
293     assert(type != nullptr);
294     OUString unoName(toUnoName(type->name()));
295     typelib_TypeDescription * td = nullptr;
296     typelib_typedescription_getByName(&td, unoName.pData);
297     if (td == nullptr) {
298         css::uno::RuntimeException e("exception type not found: " + unoName);
299         uno_type_any_constructAndConvert(
300             any, &e,
301             cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(),
302             mapping);
303     } else {
304         uno_any_constructAndConvert(any, exception->adjustedPtr, td, mapping);
305         typelib_typedescription_release(td);
306     }
307 }
308 
raiseException(uno_Any * any,uno_Mapping * mapping)309 void raiseException(uno_Any * any, uno_Mapping * mapping) {
310     typelib_TypeDescription * td = nullptr;
311     TYPELIB_DANGER_GET(&td, any->pType);
312     if (td == nullptr) {
313         throw css::uno::RuntimeException(
314             "no typedescription for " + OUString::unacquired(&any->pType->pTypeName));
315     }
316     void * exc = __cxxabiv1::__cxa_allocate_exception(td->nSize);
317     uno_copyAndConvertData(exc, any->pData, td, mapping);
318     uno_any_destruct(any, nullptr);
319     std::type_info * rtti = getRtti(*td);
320     TYPELIB_DANGER_RELEASE(td);
321     __cxxabiv1::__cxa_throw(exc, rtti, deleteException);
322 }
323 
getReturnKind(typelib_TypeDescription const * type)324 ReturnKind getReturnKind(typelib_TypeDescription const * type) {
325     switch (type->eTypeClass) {
326     default:
327         assert(false);
328 #ifdef NDEBUG
329         [[fallthrough]];
330 #endif
331     case typelib_TypeClass_VOID:
332     case typelib_TypeClass_BOOLEAN:
333     case typelib_TypeClass_BYTE:
334     case typelib_TypeClass_SHORT:
335     case typelib_TypeClass_UNSIGNED_SHORT:
336     case typelib_TypeClass_LONG:
337     case typelib_TypeClass_UNSIGNED_LONG:
338     case typelib_TypeClass_HYPER:
339     case typelib_TypeClass_UNSIGNED_HYPER:
340     case typelib_TypeClass_FLOAT:
341     case typelib_TypeClass_DOUBLE:
342     case typelib_TypeClass_CHAR:
343     case typelib_TypeClass_ENUM:
344         assert(type->nSize <= 16);
345         return RETURN_KIND_REG;
346     case typelib_TypeClass_STRING:
347     case typelib_TypeClass_TYPE:
348     case typelib_TypeClass_ANY:
349     case typelib_TypeClass_SEQUENCE:
350     case typelib_TypeClass_INTERFACE:
351         return RETURN_KIND_INDIRECT;
352     case typelib_TypeClass_STRUCT:
353         if (type->nSize > 16) {
354             return RETURN_KIND_INDIRECT;
355         }
356         switch (getStructKind(
357                     reinterpret_cast<typelib_CompoundTypeDescription const *>(
358                         type)))
359         {
360         case STRUCT_KIND_FLOAT:
361             return RETURN_KIND_HFA_FLOAT;
362         case STRUCT_KIND_DOUBLE:
363             return RETURN_KIND_HFA_DOUBLE;
364         case STRUCT_KIND_DTOR:
365             return RETURN_KIND_INDIRECT;
366         default:
367             return RETURN_KIND_REG;
368         }
369     }
370 }
371 
372 }
373 
374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
375