1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <cstring>
24 #include <exception>
25 #include <typeinfo>
26 
27 #include <bridge.hxx>
28 #include <types.hxx>
29 #include <unointerfaceproxy.hxx>
30 #include <vtables.hxx>
31 #include <com/sun/star/uno/Exception.hpp>
32 #include <com/sun/star/uno/RuntimeException.hpp>
33 #include <com/sun/star/uno/genfunc.hxx>
34 #include <rtl/textenc.h>
35 #include <rtl/ustring.hxx>
36 #include <sal/alloca.h>
37 #include <sal/types.h>
38 #include <typelib/typeclass.h>
39 #include <typelib/typedescription.h>
40 #include <uno/any2.h>
41 #include <uno/data.h>
42 
43 #include "abi.hxx"
44 #include "callvirtualfunction.hxx"
45 
46 namespace {
47 
pushArgument(typelib_TypeClass typeclass,sal_Int32 * const subsp,unsigned long value,unsigned long * const stack,sal_Int32 * const sp,unsigned long * const regs,sal_Int32 * const nregs)48 void pushArgument(
49 #ifdef MACOSX
50     typelib_TypeClass typeclass,
51     sal_Int32 * const subsp,
52 #endif
53     unsigned long value, unsigned long * const stack, sal_Int32 * const sp,
54     unsigned long * const regs, sal_Int32 * const nregs)
55 {
56 #ifdef MACOSX
57     if (*nregs != 8)
58     {
59         regs[(*nregs)++] = value;
60     }
61     else
62     {
63         switch (typeclass) {
64         case typelib_TypeClass_BOOLEAN:
65         case typelib_TypeClass_BYTE:
66             *reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(stack + *sp) + *subsp) = value;
67             (*subsp) += 1;
68             if (*subsp == 8)
69             {
70                 (*sp)++;
71                 *subsp = 0;
72             }
73             break;
74         case typelib_TypeClass_SHORT:
75         case typelib_TypeClass_UNSIGNED_SHORT:
76         case typelib_TypeClass_CHAR:
77             *subsp = (*subsp + 1) & ~0x1;
78             if (*subsp == 8)
79             {
80                 (*sp)++;
81                 *subsp = 0;
82             }
83             *reinterpret_cast<uint16_t*>(reinterpret_cast<uintptr_t>(stack + *sp) + *subsp) = value;
84             (*subsp) += 2;
85             if (*subsp == 8)
86             {
87                 (*sp)++;
88                 *subsp = 0;
89             }
90             break;
91         case typelib_TypeClass_LONG:
92         case typelib_TypeClass_UNSIGNED_LONG:
93         case typelib_TypeClass_ENUM:
94         case typelib_TypeClass_FLOAT:
95             *subsp = (*subsp + 3) & ~0x3;
96             if (*subsp == 8)
97             {
98                 (*sp)++;
99                 *subsp = 0;
100             }
101             *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(stack + *sp) + *subsp) = value;
102             (*subsp) += 4;
103             if (*subsp == 8)
104             {
105                 (*sp)++;
106                 *subsp = 0;
107             }
108             break;
109         case typelib_TypeClass_HYPER:
110         case typelib_TypeClass_UNSIGNED_HYPER:
111         default:
112             if (*subsp > 0)
113             {
114                 (*sp)++;
115                 *subsp = 0;
116             }
117             stack[*sp] = value;
118             (*sp)++;
119             break;
120         }
121     }
122 #else
123     (*nregs != 8 ? regs[(*nregs)++] : stack[(*sp)++]) = value;
124 #endif
125 }
126 
call(bridges::cpp_uno::shared::UnoInterfaceProxy * proxy,bridges::cpp_uno::shared::VtableSlot slot,typelib_TypeDescriptionReference * returnType,sal_Int32 count,typelib_MethodParameter * parameters,void * returnValue,void ** arguments,uno_Any ** exception)127 void call(
128     bridges::cpp_uno::shared::UnoInterfaceProxy * proxy,
129     bridges::cpp_uno::shared::VtableSlot slot,
130     typelib_TypeDescriptionReference * returnType, sal_Int32 count,
131     typelib_MethodParameter * parameters, void * returnValue, void ** arguments,
132     uno_Any ** exception)
133 {
134     typelib_TypeDescription * rtd = nullptr;
135     TYPELIB_DANGER_GET(&rtd, returnType);
136     abi_aarch64::ReturnKind retKind = abi_aarch64::getReturnKind(rtd);
137     bool retConv = bridges::cpp_uno::shared::relatesToInterfaceType(rtd);
138     void * ret = retConv ? alloca(rtd->nSize) : returnValue;
139     unsigned long ** thisPtr
140         = reinterpret_cast<unsigned long **>(proxy->getCppI()) + slot.offset;
141     unsigned long * stack = static_cast<unsigned long *>(
142         alloca(count * sizeof (unsigned long)));
143     sal_Int32 sp = 0;
144 #ifdef MACOSX
145     sal_Int32 subsp = 0;
146 #endif
147     unsigned long gpr[8];
148     sal_Int32 ngpr = 0;
149     unsigned long fpr[8];
150     sal_Int32 nfpr = 0;
151     gpr[ngpr++] = reinterpret_cast<unsigned long>(thisPtr);
152     void ** cppArgs = static_cast<void **>(alloca(count * sizeof (void *)));
153     typelib_TypeDescription ** ptds =
154         static_cast<typelib_TypeDescription **>(
155             alloca(count * sizeof (typelib_TypeDescription *)));
156     for (sal_Int32 i = 0; i != count; ++i) {
157         if (!parameters[i].bOut &&
158             bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef))
159         {
160             cppArgs[i] = nullptr;
161             switch (parameters[i].pTypeRef->eTypeClass) {
162             case typelib_TypeClass_BOOLEAN:
163                 pushArgument(
164 #ifdef MACOSX
165                     parameters[i].pTypeRef->eTypeClass, &subsp,
166 #endif
167                     static_cast<unsigned long>(*static_cast<sal_Bool *>(arguments[i])), stack, &sp,
168                     gpr, &ngpr);
169                 break;
170             case typelib_TypeClass_BYTE:
171                 pushArgument(
172 #ifdef MACOSX
173                     parameters[i].pTypeRef->eTypeClass, &subsp,
174 #endif
175                     *static_cast<sal_Int8 *>(arguments[i]), stack, &sp, gpr,
176                     &ngpr);
177                 break;
178             case typelib_TypeClass_SHORT:
179                 pushArgument(
180 #ifdef MACOSX
181                     parameters[i].pTypeRef->eTypeClass, &subsp,
182 #endif
183                     *static_cast<sal_Int16 *>(arguments[i]), stack, &sp, gpr,
184                     &ngpr);
185                 break;
186             case typelib_TypeClass_UNSIGNED_SHORT:
187                 pushArgument(
188 #ifdef MACOSX
189                     parameters[i].pTypeRef->eTypeClass, &subsp,
190 #endif
191                     *static_cast<sal_uInt16 *>(arguments[i]), stack, &sp, gpr,
192                     &ngpr);
193                 break;
194             case typelib_TypeClass_LONG:
195             case typelib_TypeClass_ENUM:
196                 pushArgument(
197 #ifdef MACOSX
198                     parameters[i].pTypeRef->eTypeClass, &subsp,
199 #endif
200                     *static_cast<sal_Int32 *>(arguments[i]), stack, &sp, gpr,
201                     &ngpr);
202                 break;
203             case typelib_TypeClass_UNSIGNED_LONG:
204                 pushArgument(
205 #ifdef MACOSX
206                     parameters[i].pTypeRef->eTypeClass, &subsp,
207 #endif
208                     *static_cast<sal_uInt32 *>(arguments[i]), stack, &sp, gpr,
209                     &ngpr);
210                 break;
211             case typelib_TypeClass_HYPER:
212                 pushArgument(
213 #ifdef MACOSX
214                     parameters[i].pTypeRef->eTypeClass, &subsp,
215 #endif
216                     *static_cast<sal_Int64 *>(arguments[i]), stack, &sp, gpr,
217                     &ngpr);
218                 break;
219             case typelib_TypeClass_UNSIGNED_HYPER:
220                 pushArgument(
221 #ifdef MACOSX
222                     parameters[i].pTypeRef->eTypeClass, &subsp,
223 #endif
224                     *static_cast<sal_uInt64 *>(arguments[i]), stack, &sp, gpr,
225                     &ngpr);
226                 break;
227             case typelib_TypeClass_FLOAT:
228                 pushArgument(
229 #ifdef MACOSX
230                     parameters[i].pTypeRef->eTypeClass, &subsp,
231 #endif
232                     *static_cast<unsigned int *>(arguments[i]), stack, &sp, fpr,
233                     &nfpr);
234                 break;
235             case typelib_TypeClass_DOUBLE:
236                 pushArgument(
237 #ifdef MACOSX
238                     parameters[i].pTypeRef->eTypeClass, &subsp,
239 #endif
240                     *static_cast<unsigned long *>(arguments[i]), stack, &sp,
241                     fpr, &nfpr);
242                 break;
243             case typelib_TypeClass_CHAR:
244                 pushArgument(
245 #ifdef MACOSX
246                     parameters[i].pTypeRef->eTypeClass, &subsp,
247 #endif
248                     *static_cast<sal_Unicode *>(arguments[i]), stack, &sp, gpr,
249                     &ngpr);
250                 break;
251             default:
252                 assert(false);
253             }
254         } else {
255             typelib_TypeDescription * ptd = nullptr;
256             TYPELIB_DANGER_GET(&ptd, parameters[i].pTypeRef);
257             if (!parameters[i].bIn) {
258                 cppArgs[i] = alloca(ptd->nSize);
259                 uno_constructData(cppArgs[i], ptd);
260                 ptds[i] = ptd;
261                 pushArgument(
262 #ifdef MACOSX
263                     typelib_TypeClass_HYPER, &subsp,
264 #endif
265                     reinterpret_cast<unsigned long>(cppArgs[i]), stack, &sp,
266                     gpr, &ngpr);
267             } else if (bridges::cpp_uno::shared::relatesToInterfaceType(ptd)) {
268                 cppArgs[i] = alloca(ptd->nSize);
269                 uno_copyAndConvertData(
270                     cppArgs[i], arguments[i], ptd,
271                     proxy->getBridge()->getUno2Cpp());
272                 ptds[i] = ptd;
273                 pushArgument(
274 #ifdef MACOSX
275                     typelib_TypeClass_HYPER, &subsp,
276 #endif
277                     reinterpret_cast<unsigned long>(cppArgs[i]), stack, &sp,
278                     gpr, &ngpr);
279             } else {
280                 cppArgs[i] = nullptr;
281                 pushArgument(
282 #ifdef MACOSX
283                     typelib_TypeClass_HYPER, &subsp,
284 #endif
285                     reinterpret_cast<unsigned long>(arguments[i]), stack, &sp,
286                     gpr, &ngpr);
287                 TYPELIB_DANGER_RELEASE(ptd);
288             }
289         }
290     }
291     try {
292         try {
293             callVirtualFunction(
294                 (*thisPtr)[slot.index], gpr, fpr, stack, sp, ret);
295         } catch (css::uno::Exception &) {
296             throw;
297         } catch (std::exception & e) {
298             throw css::uno::RuntimeException(
299                 "C++ code threw "
300                 + OStringToOUString(typeid(e).name(), RTL_TEXTENCODING_UTF8)
301                 + ": " + OStringToOUString(e.what(), RTL_TEXTENCODING_UTF8));
302         } catch (...) {
303             throw css::uno::RuntimeException(
304                 "C++ code threw unknown exception");
305         }
306     } catch (css::uno::Exception &) {
307         __cxxabiv1::__cxa_exception * header = reinterpret_cast<__cxxabiv1::__cxa_eh_globals *>(
308             __cxxabiv1::__cxa_get_globals())->caughtExceptions;
309 #if !defined MACOSX && defined _LIBCPPABI_VERSION // detect libc++abi
310         // Very bad HACK to find out whether we run against a libcxxabi that has a new
311         // __cxa_exception::reserved member at the start, introduced with LLVM 10
312         // <https://github.com/llvm/llvm-project/commit/674ec1eb16678b8addc02a4b0534ab383d22fa77>
313         // "[libcxxabi] Insert padding in __cxa_exception struct for compatibility".  The layout of
314         // the start of __cxa_exception is
315         //
316         //  [8 byte  void *reserve]
317         //   8 byte  size_t referenceCount
318         //
319         // where the (bad, hacky) assumption is that reserve (if present) is null
320         // (__cxa_allocate_exception in at least LLVM 11 zero-fills the object, and nothing actively
321         // sets reserve) while referenceCount is non-null (__cxa_throw sets it to 1, and
322         // __cxa_decrement_exception_refcount destroys the exception as soon as it drops to 0; for a
323         // __cxa_dependent_exception, the referenceCount member is rather
324         //
325         //   8 byte  void* primaryException
326         //
327         // but which also will always be set to a non-null value in
328         // __cxa_rethrow_primary_exception).  As described in the definition of __cxa_exception
329         // (bridges/source/cpp_uno/gcc3_linux_aarch64/abi.hxx), this hack (together with the
330         // "#ifdef MACOSX" there) can be dropped once we can be sure that we only run against new
331         // libcxxabi that has the reserve member:
332         if (*reinterpret_cast<void **>(header) == nullptr) {
333             header = reinterpret_cast<__cxxabiv1::__cxa_exception*>(
334                 reinterpret_cast<void **>(header) + 1);
335         }
336 #endif
337         abi_aarch64::mapException(
338             header,
339             __cxxabiv1::__cxa_current_exception_type(), *exception,
340             proxy->getBridge()->getCpp2Uno());
341         for (sal_Int32 i = 0; i != count; ++i) {
342             if (cppArgs[i] != nullptr) {
343                 uno_destructData(
344                     cppArgs[i], ptds[i],
345                     reinterpret_cast<uno_ReleaseFunc>(css::uno::cpp_release));
346                 TYPELIB_DANGER_RELEASE(ptds[i]);
347             }
348         }
349         TYPELIB_DANGER_RELEASE(rtd);
350         return;
351     }
352     *exception = nullptr;
353     for (sal_Int32 i = 0; i != count; ++i) {
354         if (cppArgs[i] != nullptr) {
355             if (parameters[i].bOut) {
356                 if (parameters[i].bIn) {
357                     uno_destructData(arguments[i], ptds[i], nullptr);
358                 }
359                 uno_copyAndConvertData(
360                     arguments[i], cppArgs[i], ptds[i],
361                     proxy->getBridge()->getCpp2Uno());
362             }
363             uno_destructData(
364                 cppArgs[i], ptds[i],
365                 reinterpret_cast<uno_ReleaseFunc>(css::uno::cpp_release));
366             TYPELIB_DANGER_RELEASE(ptds[i]);
367         }
368     }
369     switch (retKind) {
370     case abi_aarch64::RETURN_KIND_REG:
371         switch (rtd->eTypeClass) {
372         case typelib_TypeClass_VOID:
373             break;
374         case typelib_TypeClass_BOOLEAN:
375         case typelib_TypeClass_BYTE:
376         case typelib_TypeClass_SHORT:
377         case typelib_TypeClass_UNSIGNED_SHORT:
378         case typelib_TypeClass_LONG:
379         case typelib_TypeClass_UNSIGNED_LONG:
380         case typelib_TypeClass_HYPER:
381         case typelib_TypeClass_UNSIGNED_HYPER:
382         case typelib_TypeClass_CHAR:
383         case typelib_TypeClass_ENUM:
384         case typelib_TypeClass_STRUCT:
385             std::memcpy(ret, gpr, rtd->nSize);
386             break;
387         case typelib_TypeClass_FLOAT:
388         case typelib_TypeClass_DOUBLE:
389             std::memcpy(ret, fpr, rtd->nSize);
390             break;
391         default:
392             assert(false);
393         }
394         break;
395     case abi_aarch64::RETURN_KIND_HFA_FLOAT:
396         switch (rtd->nSize) {
397         case 16:
398             std::memcpy(static_cast<char *>(ret) + 12, fpr + 3, 4);
399             [[fallthrough]];
400         case 12:
401             std::memcpy(static_cast<char *>(ret) + 8, fpr + 2, 4);
402             [[fallthrough]];
403         case 8:
404             std::memcpy(static_cast<char *>(ret) + 4, fpr + 1, 4);
405             [[fallthrough]];
406         case 4:
407             std::memcpy(ret, fpr, 4);
408             break;
409         default:
410             assert(false);
411         }
412         break;
413     case abi_aarch64::RETURN_KIND_HFA_DOUBLE:
414         std::memcpy(ret, fpr, rtd->nSize);
415         break;
416     case abi_aarch64::RETURN_KIND_INDIRECT:
417         break;
418     }
419     if (retConv) {
420         uno_copyAndConvertData(
421             returnValue, ret, rtd, proxy->getBridge()->getCpp2Uno());
422         uno_destructData(
423             ret, rtd, reinterpret_cast<uno_ReleaseFunc>(css::uno::cpp_release));
424     }
425     TYPELIB_DANGER_RELEASE(rtd);
426 }
427 
428 }
429 
430 namespace bridges::cpp_uno::shared {
431 
unoInterfaceProxyDispatch(uno_Interface * pUnoI,typelib_TypeDescription const * pMemberDescr,void * pReturn,void ** pArgs,uno_Any ** ppException)432 void unoInterfaceProxyDispatch(
433     uno_Interface * pUnoI, typelib_TypeDescription const * pMemberDescr,
434     void * pReturn, void ** pArgs, uno_Any ** ppException)
435 {
436     UnoInterfaceProxy * proxy = static_cast<UnoInterfaceProxy *>(pUnoI);
437     switch (pMemberDescr->eTypeClass) {
438     case typelib_TypeClass_INTERFACE_ATTRIBUTE:
439         {
440             typelib_InterfaceAttributeTypeDescription const * atd
441                 = reinterpret_cast<
442                     typelib_InterfaceAttributeTypeDescription const *>(
443                         pMemberDescr);
444             VtableSlot slot(getVtableSlot(atd));
445             if (pReturn != nullptr) { // getter
446                 call(
447                     proxy, slot, atd->pAttributeTypeRef, 0, nullptr, pReturn, pArgs,
448                     ppException);
449             } else { // setter
450                 typelib_MethodParameter param = {
451                     nullptr, atd->pAttributeTypeRef, true, false };
452                 typelib_TypeDescriptionReference * rtd = nullptr;
453                 typelib_typedescriptionreference_new(
454                     &rtd, typelib_TypeClass_VOID, OUString("void").pData);
455                 slot.index += 1;
456                 call(proxy, slot, rtd, 1, &param, pReturn, pArgs, ppException);
457                 typelib_typedescriptionreference_release(rtd);
458             }
459             break;
460         }
461     case typelib_TypeClass_INTERFACE_METHOD:
462         {
463             typelib_InterfaceMethodTypeDescription const * mtd
464                 = reinterpret_cast<
465                     typelib_InterfaceMethodTypeDescription const *>(
466                         pMemberDescr);
467             VtableSlot slot(getVtableSlot(mtd));
468             switch (slot.index) {
469             case 1:
470                 pUnoI->acquire(pUnoI);
471                 *ppException = nullptr;
472                 break;
473             case 2:
474                 pUnoI->release(pUnoI);
475                 *ppException = nullptr;
476                 break;
477             case 0:
478                 {
479                     typelib_TypeDescription * td = nullptr;
480                     TYPELIB_DANGER_GET(
481                         &td,
482                         (static_cast<css::uno::Type *>(pArgs[0])
483                          ->getTypeLibType()));
484                     if (td != nullptr) {
485                         uno_Interface * ifc = nullptr;
486                         proxy->pBridge->getUnoEnv()->getRegisteredInterface(
487                             proxy->pBridge->getUnoEnv(),
488                             reinterpret_cast<void **>(&ifc), proxy->oid.pData,
489                             reinterpret_cast<
490                                 typelib_InterfaceTypeDescription *>(td));
491                         if (ifc != nullptr) {
492                             uno_any_construct(
493                                 static_cast<uno_Any *>(pReturn), &ifc, td,
494                                 nullptr);
495                             ifc->release(ifc);
496                             TYPELIB_DANGER_RELEASE(td);
497                             *ppException = nullptr;
498                             break;
499                         }
500                         TYPELIB_DANGER_RELEASE(td);
501                     }
502                 }
503                 [[fallthrough]];
504             default:
505                 call(
506                     proxy, slot, mtd->pReturnTypeRef, mtd->nParams,
507                     mtd->pParams, pReturn, pArgs, ppException);
508                 break;
509             }
510             break;
511         }
512     default:
513         assert(false);
514     }
515 }
516 
517 }
518 
519 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
520