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, ¶m, 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