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 <cstdarg>
24 #include <cstddef>
25 #include <cstdlib>
26 #include <cstring>
27 
28 #include <dlfcn.h>
29 
30 #include <com/sun/star/uno/XInterface.hpp>
31 #include <com/sun/star/uno/genfunc.hxx>
32 #include <sal/alloca.h>
33 #include <sal/types.h>
34 #include <typelib/typeclass.h>
35 #include <typelib/typedescription.h>
36 #include <typelib/typedescription.hxx>
37 
38 #include <bridge.hxx>
39 #include <cppinterfaceproxy.hxx>
40 #include <types.hxx>
41 #include <vtablefactory.hxx>
42 
43 #include "abi.hxx"
44 
45 extern "C" void vtableSlotCall_();
46 
47 namespace {
48 
call(bridges::cpp_uno::shared::CppInterfaceProxy * proxy,css::uno::TypeDescription const & description,typelib_TypeDescriptionReference * returnType,sal_Int32 count,typelib_MethodParameter * parameters,unsigned long * gpr,unsigned long * fpr,unsigned long * stack,void * indirectRet)49 void call(
50     bridges::cpp_uno::shared::CppInterfaceProxy * proxy,
51     css::uno::TypeDescription const & description,
52     typelib_TypeDescriptionReference * returnType, sal_Int32 count,
53     typelib_MethodParameter * parameters, unsigned long * gpr,
54     unsigned long * fpr, unsigned long * stack, void * indirectRet)
55 {
56     typelib_TypeDescription * rtd = 0;
57     if (returnType != 0) {
58         TYPELIB_DANGER_GET(&rtd, returnType);
59     }
60     abi_aarch64::ReturnKind retKind = rtd == 0
61         ? abi_aarch64::RETURN_KIND_REG : abi_aarch64::getReturnKind(rtd);
62     bool retConv = rtd != 0
63         && bridges::cpp_uno::shared::relatesToInterfaceType(rtd);
64     void * retin = retKind == abi_aarch64::RETURN_KIND_INDIRECT && !retConv
65         ? indirectRet : rtd == 0 ? 0 : alloca(rtd->nSize);
66     void ** args = static_cast< void ** >(alloca(count * sizeof (void *)));
67     void ** cppArgs = static_cast< void ** >(alloca(count * sizeof (void *)));
68     typelib_TypeDescription ** argtds = static_cast<typelib_TypeDescription **>(
69         alloca(count * sizeof (typelib_TypeDescription *)));
70     sal_Int32 ngpr = 1;
71     sal_Int32 nfpr = 0;
72     sal_Int32 sp = 0;
73     for (sal_Int32 i = 0; i != count; ++i) {
74         if (!parameters[i].bOut
75             && bridges::cpp_uno::shared::isSimpleType(parameters[i].pTypeRef))
76         {
77             switch (parameters[i].pTypeRef->eTypeClass) {
78             case typelib_TypeClass_BOOLEAN:
79             case typelib_TypeClass_BYTE:
80             case typelib_TypeClass_SHORT:
81             case typelib_TypeClass_UNSIGNED_SHORT:
82             case typelib_TypeClass_LONG:
83             case typelib_TypeClass_UNSIGNED_LONG:
84             case typelib_TypeClass_HYPER:
85             case typelib_TypeClass_UNSIGNED_HYPER:
86             case typelib_TypeClass_CHAR:
87             case typelib_TypeClass_ENUM:
88                 args[i] = ngpr == 8 ? stack + sp++ : gpr + ngpr++;
89                 break;
90             case typelib_TypeClass_FLOAT:
91             case typelib_TypeClass_DOUBLE:
92                 args[i] = nfpr == 8 ? stack + sp++ : fpr + nfpr++;
93                 break;
94             default:
95                 assert(false);
96             }
97             argtds[i] = 0;
98         } else {
99             cppArgs[i] = reinterpret_cast<void *>(
100                 ngpr == 8 ? stack[sp++] : gpr[ngpr++]);
101             typelib_TypeDescription * ptd = 0;
102             TYPELIB_DANGER_GET(&ptd, parameters[i].pTypeRef);
103             if (!parameters[i].bIn) {
104                 args[i] = alloca(ptd->nSize);
105                 argtds[i] = ptd;
106             } else if (bridges::cpp_uno::shared::relatesToInterfaceType(ptd)) {
107                 args[i] = alloca(ptd->nSize);
108                 uno_copyAndConvertData(
109                     args[i], cppArgs[i], ptd, proxy->getBridge()->getCpp2Uno());
110                 argtds[i] = ptd;
111             } else {
112                 args[i] = cppArgs[i];
113                 argtds[i] = 0;
114                 TYPELIB_DANGER_RELEASE(ptd);
115             }
116         }
117     }
118     uno_Any exc;
119     uno_Any * pexc = &exc;
120     proxy->getUnoI()->pDispatcher(
121         proxy->getUnoI(), description.get(), retin, args, &pexc);
122     if (pexc != 0) {
123         for (sal_Int32 i = 0; i != count; ++i) {
124             if (argtds[i] != 0) {
125                 if (parameters[i].bIn) {
126                     uno_destructData(args[i], argtds[i], 0);
127                 }
128                 TYPELIB_DANGER_RELEASE(argtds[i]);
129             }
130         }
131         if (rtd != 0) {
132             TYPELIB_DANGER_RELEASE(rtd);
133         }
134         abi_aarch64::raiseException(&exc, proxy->getBridge()->getUno2Cpp());
135     }
136     for (sal_Int32 i = 0; i != count; ++i) {
137         if (argtds[i] != 0) {
138             if (parameters[i].bOut) {
139                 uno_destructData(
140                     cppArgs[i], argtds[i],
141                     reinterpret_cast<uno_ReleaseFunc>(css::uno::cpp_release));
142                 uno_copyAndConvertData(
143                     cppArgs[i], args[i], argtds[i],
144                     proxy->getBridge()->getUno2Cpp());
145             }
146             uno_destructData(args[i], argtds[i], 0);
147             TYPELIB_DANGER_RELEASE(argtds[i]);
148         }
149     }
150     void * retout = 0; // avoid false -Werror=maybe-uninitialized
151     switch (retKind) {
152     case abi_aarch64::RETURN_KIND_REG:
153         switch (rtd == 0 ? typelib_TypeClass_VOID : rtd->eTypeClass) {
154         case typelib_TypeClass_VOID:
155             break;
156         case typelib_TypeClass_BOOLEAN:
157         case typelib_TypeClass_BYTE:
158         case typelib_TypeClass_SHORT:
159         case typelib_TypeClass_UNSIGNED_SHORT:
160         case typelib_TypeClass_LONG:
161         case typelib_TypeClass_UNSIGNED_LONG:
162         case typelib_TypeClass_HYPER:
163         case typelib_TypeClass_UNSIGNED_HYPER:
164         case typelib_TypeClass_CHAR:
165         case typelib_TypeClass_ENUM:
166             std::memcpy(gpr, retin, rtd->nSize);
167             assert(!retConv);
168             break;
169         case typelib_TypeClass_FLOAT:
170         case typelib_TypeClass_DOUBLE:
171             std::memcpy(fpr, retin, rtd->nSize);
172             assert(!retConv);
173             break;
174         case typelib_TypeClass_STRUCT:
175             if (retConv) {
176                 retout = gpr;
177             } else {
178                 std::memcpy(gpr, retin, rtd->nSize);
179             }
180             break;
181         default:
182             assert(false);
183         }
184         break;
185     case abi_aarch64::RETURN_KIND_HFA_FLOAT:
186         assert(rtd != 0);
187         switch (rtd->nSize) {
188         case 16:
189             std::memcpy(fpr + 3, static_cast<char *>(retin) + 12, 4);
190             // fall through
191         case 12:
192             std::memcpy(fpr + 2, static_cast<char *>(retin) + 8, 4);
193             // fall through
194         case 8:
195             std::memcpy(fpr + 1, static_cast<char *>(retin) + 4, 4);
196             // fall through
197         case 4:
198             std::memcpy(fpr, retin, 4);
199             break;
200         default:
201             assert(false);
202         }
203         assert(!retConv);
204         break;
205     case abi_aarch64::RETURN_KIND_HFA_DOUBLE:
206         assert(rtd != 0);
207         std::memcpy(fpr, retin, rtd->nSize);
208         assert(!retConv);
209         break;
210     case abi_aarch64::RETURN_KIND_INDIRECT:
211         retout = indirectRet;
212         break;
213     }
214     if (retConv) {
215         uno_copyAndConvertData(
216             retout, retin, rtd, proxy->getBridge()->getUno2Cpp());
217         uno_destructData(retin, rtd, 0);
218     }
219     if (rtd != 0) {
220         TYPELIB_DANGER_RELEASE(rtd);
221     }
222 }
223 
vtableCall(sal_Int32 functionIndex,sal_Int32 vtableOffset,unsigned long * gpr,unsigned long * fpr,unsigned long * stack,void * indirectRet)224 extern "C" void vtableCall(
225     sal_Int32 functionIndex, sal_Int32 vtableOffset,
226     unsigned long * gpr, unsigned long * fpr, unsigned long  * stack,
227     void * indirectRet)
228 {
229     bridges::cpp_uno::shared::CppInterfaceProxy * proxy
230         = bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy(
231             reinterpret_cast<char *>(gpr[0]) - vtableOffset);
232     typelib_InterfaceTypeDescription * type = proxy->getTypeDescr();
233     assert(functionIndex < type->nMapFunctionIndexToMemberIndex);
234     sal_Int32 pos = type->pMapFunctionIndexToMemberIndex[functionIndex];
235     css::uno::TypeDescription desc(type->ppAllMembers[pos]);
236     switch (desc.get()->eTypeClass) {
237     case typelib_TypeClass_INTERFACE_ATTRIBUTE:
238         if (type->pMapMemberIndexToFunctionIndex[pos] == functionIndex) {
239             // Getter:
240             call(
241                 proxy, desc,
242                 reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>(
243                     desc.get())->pAttributeTypeRef,
244                 0, 0, gpr, fpr, stack, indirectRet);
245         } else {
246             // Setter:
247             typelib_MethodParameter param = {
248                 0,
249                 reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>(
250                     desc.get())->pAttributeTypeRef,
251                 true, false };
252             call(proxy, desc, 0, 1, &param, gpr, fpr, stack, indirectRet);
253         }
254         break;
255     case typelib_TypeClass_INTERFACE_METHOD:
256         switch (functionIndex) {
257         case 1:
258             proxy->acquireProxy();
259             break;
260         case 2:
261             proxy->releaseProxy();
262             break;
263         case 0:
264             {
265                 typelib_TypeDescription * td = 0;
266                 TYPELIB_DANGER_GET(
267                     &td,
268                     (reinterpret_cast<css::uno::Type *>(gpr[1])
269                      ->getTypeLibType()));
270                 if (td != 0 && td->eTypeClass == typelib_TypeClass_INTERFACE) {
271                     css::uno::XInterface * ifc = 0;
272                     proxy->getBridge()->getCppEnv()->getRegisteredInterface(
273                         proxy->getBridge()->getCppEnv(),
274                         reinterpret_cast<void **>(&ifc), proxy->getOid().pData,
275                         reinterpret_cast<typelib_InterfaceTypeDescription *>(
276                             td));
277                     if (ifc != 0) {
278                         uno_any_construct(
279                             reinterpret_cast<uno_Any *>(indirectRet), &ifc, td,
280                             reinterpret_cast<uno_AcquireFunc>(
281                                 css::uno::cpp_acquire));
282                         ifc->release();
283                         TYPELIB_DANGER_RELEASE(td);
284                         break;
285                     }
286                     TYPELIB_DANGER_RELEASE(td);
287                 }
288             }
289             // fall through
290         default:
291             call(
292                 proxy, desc,
293                 reinterpret_cast<typelib_InterfaceMethodTypeDescription *>(
294                     desc.get())->pReturnTypeRef,
295                 reinterpret_cast<typelib_InterfaceMethodTypeDescription *>(
296                     desc.get())->nParams,
297                 reinterpret_cast<typelib_InterfaceMethodTypeDescription *>(
298                     desc.get())->pParams,
299                 gpr, fpr, stack, indirectRet);
300         }
301         break;
302     default:
303         assert(false);
304     }
305 }
306 
307 struct aarch64_va_list {
308     void * stack;
309     void * gr_top;
310     void * vr_top;
311     int gr_offs;
312     int vr_offs;
313 };
314 
315 #pragma GCC diagnostic push
316 #pragma GCC diagnostic ignored "-Wvolatile-register-var"
vtableSlotCall(unsigned long gpr0,unsigned long gpr1,unsigned long gpr2,unsigned long gpr3,unsigned long gpr4,unsigned long gpr5,unsigned long gpr6,unsigned long gpr7,double fpr0,double fpr1,double fpr2,double fpr3,double fpr4,double fpr5,double fpr6,double fpr7,...)317 extern "C" void vtableSlotCall(
318     unsigned long gpr0, unsigned long gpr1, unsigned long gpr2,
319     unsigned long gpr3, unsigned long gpr4, unsigned long gpr5,
320     unsigned long gpr6, unsigned long gpr7, double fpr0, double fpr1,
321     double fpr2, double fpr3, double fpr4, double fpr5, double fpr6,
322     double fpr7, ...)
323 {
324     void * volatile indirectRet;
325     sal_Int32 volatile functionIndex, vtableOffset;
326 
327     asm volatile(
328         "mov %0, x8\n"
329         "mov %1, x9\n"
330         "mov %2, x10\n"
331         : "=r" (indirectRet), "=r" (functionIndex), "=r" (vtableOffset)
332         ::);
333 
334     va_list ap;
335     va_start(ap, fpr7);
336     assert(sizeof (va_list) == sizeof (aarch64_va_list));
337     unsigned long gpr[8];
338     gpr[0] = gpr0;
339     gpr[1] = gpr1;
340     gpr[2] = gpr2;
341     gpr[3] = gpr3;
342     gpr[4] = gpr4;
343     gpr[5] = gpr5;
344     gpr[6] = gpr6;
345     gpr[7] = gpr7;
346     double fpr[8];
347     fpr[0] = fpr0;
348     fpr[1] = fpr1;
349     fpr[2] = fpr2;
350     fpr[3] = fpr3;
351     fpr[4] = fpr4;
352     fpr[5] = fpr5;
353     fpr[6] = fpr6;
354     fpr[7] = fpr7;
355     vtableCall(
356         functionIndex, vtableOffset, gpr,
357         reinterpret_cast<unsigned long *>(fpr),
358         static_cast<unsigned long *>(
359             reinterpret_cast<aarch64_va_list *>(&ap)->stack),
360         indirectRet);
361     asm volatile(
362         "ldp x0, x1, [%[gpr_]]\n\t"
363         "ldp d0, d1, [%[fpr_]]\n\t"
364         "ldp d2, d3, [%[fpr_], #16]\n\t"
365         :: [gpr_]"r" (gpr), [fpr_]"r" (fpr)
366         : "r0", "r1", "v0", "v1", "v2", "v3");
367     va_end(ap);
368 }
369 #pragma GCC diagnostic pop
370 
371 std::size_t const codeSnippetSize = 8 * 4;
372 
generateCodeSnippet(unsigned char * code,sal_Int32 functionIndex,sal_Int32 vtableOffset)373 unsigned char * generateCodeSnippet(
374     unsigned char * code, sal_Int32 functionIndex, sal_Int32 vtableOffset)
375 {
376     // movz x9, <low functionIndex>
377     reinterpret_cast<unsigned int *>(code)[0] = 0xD2800009
378         | ((functionIndex & 0xFFFF) << 5);
379     // movk x9, <high functionIndex>, LSL #16
380     reinterpret_cast<unsigned int *>(code)[1] = 0xF2A00009
381         | ((functionIndex >> 16) << 5);
382     // movz x10, <low vtableOffset>
383     reinterpret_cast<unsigned int *>(code)[2] = 0xD280000A
384         | ((vtableOffset & 0xFFFF) << 5);
385     // movk x10, <high vtableOffset>, LSL #16
386     reinterpret_cast<unsigned int *>(code)[3] = 0xF2A0000A
387         | ((vtableOffset >> 16) << 5);
388     // ldr x11, +2*4
389     reinterpret_cast<unsigned int *>(code)[4] = 0x5800004B;
390     // br x11
391     reinterpret_cast<unsigned int *>(code)[5] = 0xD61F0160;
392     reinterpret_cast<unsigned long *>(code)[3]
393         = reinterpret_cast<unsigned long>(&vtableSlotCall);
394     return code + codeSnippetSize;
395 }
396 
397 }
398 
399 struct bridges::cpp_uno::shared::VtableFactory::Slot { void * fn; };
400 
401 bridges::cpp_uno::shared::VtableFactory::Slot *
mapBlockToVtable(void * block)402 bridges::cpp_uno::shared::VtableFactory::mapBlockToVtable(void * block) {
403     return static_cast<Slot *>(block) + 2;
404 }
405 
getBlockSize(sal_Int32 slotCount)406 std::size_t bridges::cpp_uno::shared::VtableFactory::getBlockSize(
407     sal_Int32 slotCount)
408 {
409     return (slotCount + 2) * sizeof (Slot) + slotCount * codeSnippetSize;
410 }
411 
412 bridges::cpp_uno::shared::VtableFactory::Slot *
initializeBlock(void * block,sal_Int32 slotCount,sal_Int32,typelib_InterfaceTypeDescription *)413 bridges::cpp_uno::shared::VtableFactory::initializeBlock(
414     void * block, sal_Int32 slotCount, sal_Int32,
415     typelib_InterfaceTypeDescription *)
416 {
417     Slot * slots = mapBlockToVtable(block);
418     slots[-2].fn = 0;
419     slots[-1].fn = 0;
420     return slots + slotCount;
421 }
422 
addLocalFunctions(Slot ** slots,unsigned char * code,sal_PtrDiff writetoexecdiff,typelib_InterfaceTypeDescription const * type,sal_Int32 functionOffset,sal_Int32 functionCount,sal_Int32 vtableOffset)423 unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions(
424     Slot ** slots, unsigned char * code, sal_PtrDiff writetoexecdiff,
425     typelib_InterfaceTypeDescription const * type, sal_Int32 functionOffset,
426     sal_Int32 functionCount, sal_Int32 vtableOffset)
427 {
428     (*slots) -= functionCount;
429     Slot * s = *slots;
430     for (sal_Int32 i = 0; i != type->nMembers; ++i) {
431         typelib_TypeDescription * td = 0;
432         TYPELIB_DANGER_GET(&td, type->ppMembers[i]);
433         assert(td != 0);
434         switch (td->eTypeClass) {
435         case typelib_TypeClass_INTERFACE_ATTRIBUTE:
436             {
437                 typelib_InterfaceAttributeTypeDescription * atd
438                     = reinterpret_cast<
439                         typelib_InterfaceAttributeTypeDescription *>(td);
440                 // Getter:
441                 (s++)->fn = code + writetoexecdiff;
442                 code = generateCodeSnippet(
443                     code, functionOffset++, vtableOffset);
444                 // Setter:
445                 if (!atd->bReadOnly) {
446                     (s++)->fn = code + writetoexecdiff;
447                     code = generateCodeSnippet(
448                         code, functionOffset++, vtableOffset);
449                 }
450                 break;
451             }
452         case typelib_TypeClass_INTERFACE_METHOD:
453             (s++)->fn = code + writetoexecdiff;
454             code = generateCodeSnippet(code, functionOffset++, vtableOffset);
455             break;
456         default:
457             assert(false);
458         }
459         TYPELIB_DANGER_RELEASE(td);
460     }
461     return code;
462 }
463 
flushCode(unsigned char const * begin,unsigned char const * end)464 void bridges::cpp_uno::shared::VtableFactory::flushCode(
465     unsigned char const * begin, unsigned char const * end)
466 {
467    static void (*clear_cache)(unsigned char const *, unsigned char const *)
468        = (void (*)(unsigned char const *, unsigned char const *)) dlsym(
469            RTLD_DEFAULT, "__clear_cache");
470    (*clear_cache)(begin, end);
471 }
472 
473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
474