1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /* Platform specific code to invoke XPCOM methods on native objects */
7 
8 #include "xptcprivate.h"
9 
10 #if !defined(__aarch64__)
11 #error "This code is for Linux AArch64 only."
12 #endif
13 
14 
15 /* "Procedure Call Standard for the ARM 64-bit Architecture" document, sections
16  * "5.4 Parameter Passing" and "6.1.2 Procedure Calling" contain all the
17  * needed information.
18  *
19  * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf
20  */
21 
22 #ifndef __AARCH64EL__
23 #error "Only little endian compatibility was tested"
24 #endif
25 
26 // The AAPCS doesn't require argument widening, but Apple's calling convention
27 // does. If we are really fortunate, the compiler will clean up all the
28 // copying for us.
29 template<typename T>
normalize_arg(T value)30 inline uint64_t normalize_arg(T value) {
31     return (uint64_t)value;
32 }
33 
34 template<>
normalize_arg(float value)35 inline uint64_t normalize_arg(float value) {
36     uint64_t result = 0;
37     memcpy(&result, &value, sizeof(value));
38     return result;
39 }
40 
41 template<>
normalize_arg(double value)42 inline uint64_t normalize_arg(double value) {
43     uint64_t result = 0;
44     memcpy(&result, &value, sizeof(value));
45     return result;
46 }
47 
48 /*
49  * Allocation of function arguments to their appropriate place in registers
50  * if possible and then to the stack.  Handling of 'that' argument which
51  * goes to register r0 is handled separately and does not belong here.
52  *
53  * Note that we are handling integer arguments and floating-point arguments
54  * identically, depending on which register area is passed to this function.
55  *
56  * 'reg_args'     - pointer to the current position in the buffer,
57  *                  corresponding to the register arguments.
58  * 'reg_args_end' - pointer to the end of the registers argument
59  *                  buffer.
60  * 'stack_args'   - pointer to the current position in the buffer,
61  *                  corresponding to the arguments on stack.
62  * 'data'         - typed data to put on the stack.
63  */
64 template<typename T>
alloc_arg(uint64_t * & reg_args,uint64_t * reg_args_end,void * & stack_args,T * data)65 static inline void alloc_arg(uint64_t* &reg_args,
66                              uint64_t* reg_args_end,
67                              void* &stack_args,
68                              T*     data)
69 {
70     if (reg_args < reg_args_end) {
71         *reg_args = normalize_arg(*data);
72         reg_args++;
73     } else {
74         // According to the ABI, types that are smaller than 8 bytes are
75         // passed in registers or 8-byte stack slots.  This rule is only
76         // partially true on Apple platforms, where types smaller than 8
77         // bytes occupy only the space they require on the stack and
78         // their stack slot must be properly aligned.
79 #ifdef __APPLE__
80         const size_t aligned_size = sizeof(T);
81 #else
82         const size_t aligned_size = 8;
83 #endif
84         // Ensure the pointer is aligned for the type
85         uintptr_t addr = (reinterpret_cast<uintptr_t>(stack_args) + aligned_size - 1) & ~(aligned_size - 1);
86         memcpy(reinterpret_cast<void*>(addr), data, sizeof(T));
87         // Point the stack to the next slot.
88         stack_args = reinterpret_cast<void*>(addr + aligned_size);
89     }
90 }
91 
92 extern "C" void
invoke_copy_to_stack(uint64_t * stk,uint64_t * end,uint32_t paramCount,nsXPTCVariant * s)93 invoke_copy_to_stack(uint64_t* stk, uint64_t *end,
94                      uint32_t paramCount, nsXPTCVariant* s)
95 {
96     uint64_t* ireg_args = stk;
97     uint64_t* ireg_end  = ireg_args + 8;
98     // Pun on integer and floating-point registers being the same size.
99     uint64_t* freg_args = ireg_end;
100     uint64_t* freg_end  = freg_args + 8;
101     void* stack_args = freg_end;
102 
103     // leave room for 'that' argument in x0
104     ++ireg_args;
105 
106     for (uint32_t i = 0; i < paramCount; i++, s++) {
107         if (s->IsIndirect()) {
108             void* ptr = &s->val;
109             alloc_arg(ireg_args, ireg_end, stack_args, &ptr);
110         } else {
111             switch (s->type) {
112                 case nsXPTType::T_FLOAT:
113                     alloc_arg(freg_args, freg_end, stack_args, &s->val.f);
114                     break;
115                 case nsXPTType::T_DOUBLE:
116                     alloc_arg(freg_args, freg_end, stack_args, &s->val.d);
117                     break;
118                 case nsXPTType::T_I8:
119                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i8);
120                     break;
121                 case nsXPTType::T_I16:
122                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i16);
123                     break;
124                 case nsXPTType::T_I32:
125                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i32);
126                     break;
127                 case nsXPTType::T_I64:
128                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i64);
129                     break;
130                 case nsXPTType::T_U8:
131                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u8);
132                     break;
133                 case nsXPTType::T_U16:
134                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u16);
135                     break;
136                 case nsXPTType::T_U32:
137                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u32);
138                     break;
139                 case nsXPTType::T_U64:
140                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u64);
141                     break;
142                 case nsXPTType::T_BOOL:
143                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.b);
144                     break;
145                 case nsXPTType::T_CHAR:
146                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.c);
147                     break;
148                 case nsXPTType::T_WCHAR:
149                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.wc);
150                     break;
151                 default:
152                     // all the others are plain pointer types
153                     alloc_arg(ireg_args, ireg_end, stack_args, &s->val.p);
154                     break;
155             }
156         }
157     }
158 }
159 
160 extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
161                                       uint32_t paramCount, nsXPTCVariant* params);
162 
163 EXPORT_XPCOM_API(nsresult)
NS_InvokeByIndex(nsISupports * that,uint32_t methodIndex,uint32_t paramCount,nsXPTCVariant * params)164 NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
165                  uint32_t paramCount, nsXPTCVariant* params)
166 {
167     return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
168 }
169