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