1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_ 18 #define BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_ 19 20 #include <array> 21 #include <cstdint> 22 #include <type_traits> 23 24 #include "berberis/base/bit_util.h" 25 #include "berberis/base/dependent_false.h" 26 #include "berberis/calling_conventions/calling_conventions_arm.h" 27 #include "berberis/guest_abi/guest_abi.h" 28 #include "berberis/guest_state/guest_addr.h" 29 #include "berberis/guest_state/guest_state.h" 30 31 namespace berberis { 32 33 class GuestVAListParams; 34 35 class GuestParamsAndReturnHelper : protected GuestAbi { 36 protected: 37 template <typename Type, 38 GuestAbi::CallingConventionsVariant kCallingConventionsVariant, 39 typename R, 40 typename D, 41 typename S> ParamLocationAddress(R r,D d,S s,arm::ArgLocation loc)42 constexpr static auto* ParamLocationAddress(R r, D d, S s, arm::ArgLocation loc) { 43 void* address = nullptr; 44 if (loc.kind == arm::kArgLocationStack) { 45 if constexpr (std::is_same_v<S, std::nullptr_t>) { 46 address = ToHostAddr<uint32_t>(loc.offset); 47 } else { 48 address = s + loc.offset; 49 } 50 } else if (loc.kind == arm::kArgLocationInt) { 51 if constexpr (std::is_same_v<R, std::nullptr_t>) { 52 LOG_ALWAYS_FATAL("Unsupported ArgumentKind"); 53 } else { 54 address = r + loc.offset; 55 } 56 } else if (loc.kind == arm::kArgLocationSimd) { 57 if constexpr (std::is_same_v<D, std::nullptr_t>) { 58 LOG_ALWAYS_FATAL("Unsupported ArgumentKind"); 59 } else { 60 address = d + loc.offset; 61 } 62 } else if (loc.kind == arm::kArgLocationIntAndStack) { 63 LOG_ALWAYS_FATAL("Arguments split between registers are stack are not currently supported"); 64 } else { 65 LOG_ALWAYS_FATAL("Unknown ArgumentKind"); 66 } 67 68 static_assert(!std::is_same_v<Type, void>); 69 if constexpr (GuestArgumentInfo<Type, kCallingConventionsVariant>::kArgumentClass == 70 ArgumentClass::kReturnedViaIndirectPointer) { 71 return *reinterpret_cast< 72 typename GuestArgumentInfo<Type, kCallingConventionsVariant>::GuestType**>(address); 73 } else { 74 return reinterpret_cast< 75 typename GuestArgumentInfo<Type, kCallingConventionsVariant>::GuestType*>(address); 76 } 77 } 78 }; 79 80 // GuestParamsAndReturn is typesafe accessor into ThreadState. 81 // Usage looks like this: 82 // GuestParamsAndReturn<double(int, double, int, double)> params(state); 83 // auto x = params.Params<0>(); 84 // auto y = params.Params<1>(); 85 // params.Return() = x * y; 86 87 template <typename, GuestAbi::CallingConventionsVariant = GuestAbi::kAapcs> 88 class GuestParamsAndReturn; // Not defined. 89 90 template <typename ReturnType, 91 typename... ParamType, 92 bool kNoexcept, 93 GuestAbi::CallingConventionsVariant kCallingConventionsVariant> 94 class GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), kCallingConventionsVariant> 95 : GuestParamsAndReturnHelper { 96 public: GuestParamsAndReturn(ThreadState * state)97 GuestParamsAndReturn(ThreadState* state) 98 : r_(reinterpret_cast<uint32_t*>(state->cpu.r)), 99 d_(reinterpret_cast<uint32_t*>(state->cpu.d)), 100 s_(ToHostAddr<uint8_t>(state->cpu.r[13])) {} 101 102 template <size_t index> Params()103 auto* Params() const { 104 static_assert(index < sizeof...(ParamType)); 105 return this->ParamLocationAddress<std::tuple_element_t<index, std::tuple<ParamType...>>, 106 kCallingConventionsVariant>( 107 r_, d_, s_, kParamsLocations[index]); 108 } 109 Return()110 auto* Return() const { 111 return this->ParamLocationAddress<ReturnType, kCallingConventionsVariant>( 112 r_, d_, s_, kReturnLocation); 113 } 114 115 private: 116 friend class GuestVAListParams; 117 friend class GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept), 118 kCallingConventionsVariant>; 119 120 constexpr static const std::tuple<arm::CallingConventions, 121 std::array<arm::ArgLocation, sizeof...(ParamType)>> ParamsInfoHelper()122 ParamsInfoHelper() { 123 struct { 124 const ArgumentClass kArgumentClass; 125 const unsigned kSize; 126 const unsigned kAlignment; 127 } const kArgumentsInfo[] = { 128 {.kArgumentClass = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kArgumentClass, 129 .kSize = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kSize, 130 .kAlignment = GuestArgumentInfo<ParamType, kCallingConventionsVariant>::kAlignment}...}; 131 132 arm::CallingConventions conv; 133 134 // Skip hidden argument if it exists. 135 if constexpr (!std::is_same_v<ReturnType, void>) { 136 if constexpr (GuestArgumentInfo<ReturnType, kCallingConventionsVariant>::kArgumentClass == 137 ArgumentClass::kReturnedViaIndirectPointer) { 138 conv.GetNextIntArgLoc( 139 GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kSize, 140 GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kAlignment); 141 } 142 } 143 144 std::array<arm::ArgLocation, sizeof...(ParamType)> result{}; 145 for (const auto& kArgInfo : kArgumentsInfo) { 146 if (kArgInfo.kArgumentClass == ArgumentClass::kInteger) { 147 result[&kArgInfo - kArgumentsInfo] = 148 conv.GetNextIntArgLoc(kArgInfo.kSize, kArgInfo.kAlignment); 149 } else if (kArgInfo.kArgumentClass == ArgumentClass::kVFP) { 150 result[&kArgInfo - kArgumentsInfo] = 151 conv.GetNextFpArgLoc(kArgInfo.kSize, kArgInfo.kAlignment); 152 } else { 153 LOG_ALWAYS_FATAL("Unsupported ArgumentClass"); 154 } 155 } 156 157 return {conv, result}; 158 } 159 ReturnInfoHelper()160 constexpr static arm::ArgLocation ReturnInfoHelper() { 161 arm::CallingConventions conv; 162 using ReturnInfo = GuestArgumentInfo<ReturnType, kCallingConventionsVariant>; 163 if constexpr (std::is_same_v<ReturnType, void>) { 164 return {arm::kArgLocationNone, 0}; 165 } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kInteger) { 166 return conv.GetIntResLoc(ReturnInfo::kSize); 167 } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kVFP) { 168 return conv.GetFpResLoc(ReturnInfo::kSize); 169 } else if constexpr (ReturnInfo::kArgumentClass == ArgumentClass::kReturnedViaIndirectPointer) { 170 static_assert(GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kArgumentClass == 171 ArgumentClass::kInteger); 172 return conv.GetIntResLoc(GuestArgumentInfo<ReturnType*, kCallingConventionsVariant>::kSize); 173 } else { 174 static_assert(kDependentTypeFalse<ReturnType>, "Unsupported ArgumentClass"); 175 } 176 } 177 178 constexpr static std::array<arm::ArgLocation, sizeof...(ParamType)> kParamsLocations = 179 std::get<1>(ParamsInfoHelper()); 180 181 constexpr static arm::ArgLocation kReturnLocation = ReturnInfoHelper(); 182 183 uint32_t* r_; 184 uint32_t* d_; 185 uint8_t* s_; 186 }; 187 188 // Partial specialization for GuestParamsAndReturn<FunctionToPointer> - it acts the same 189 // as the corresponding GuestParamsAndReturn<Function>. 190 template <typename ReturnType, 191 typename... ParamType, 192 bool kNoexcept, 193 GuestAbi::CallingConventionsVariant kCallingConventionsVariant> 194 class GuestParamsAndReturn<ReturnType (*)(ParamType...) noexcept(kNoexcept), 195 kCallingConventionsVariant> 196 : public GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), 197 kCallingConventionsVariant> { 198 public: GuestParamsAndReturn(ThreadState * state)199 GuestParamsAndReturn(ThreadState* state) 200 : GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), 201 kCallingConventionsVariant>(state) {} 202 }; 203 204 // Partial specialization for GuestParamsAndReturn<Function> for functions with variadic 205 // arguents - it acts the same as the corresponding one without it (but with forced kAapcs ABI). 206 // “Note: There are no VFP CPRCs in a variadic procedure” ⇦ from AAPCS 207 template <typename ReturnType, 208 typename... ParamType, 209 bool kNoexcept, 210 GuestAbi::CallingConventionsVariant kCallingConventionsVariant> 211 class GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept), 212 kCallingConventionsVariant> 213 : public GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), 214 GuestAbi::kDefaultAbi> { 215 public: GuestParamsAndReturn(ThreadState * state)216 GuestParamsAndReturn(ThreadState* state) 217 : GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), GuestAbi::kDefaultAbi>( 218 state) {} 219 220 private: 221 friend class GuestVAListParams; 222 constexpr static arm::CallingConventions kVAStartBase = 223 std::get<0>(GuestParamsAndReturn<ReturnType(ParamType...) noexcept(kNoexcept), 224 GuestAbi::kDefaultAbi>::ParamsInfoHelper()); 225 }; 226 227 // Partial specialization for GuestParamsAndReturn<FunctionToPointer> for functions with 228 // variadic arguents - it acts the same as the corresponding one without it. 229 template <typename ReturnType, 230 typename... ParamType, 231 bool kNoexcept, 232 GuestAbi::CallingConventionsVariant kCallingConventionsVariant> 233 class GuestParamsAndReturn<ReturnType (*)(ParamType..., ...) noexcept(kNoexcept), 234 kCallingConventionsVariant> 235 : public GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept), 236 GuestAbi::kDefaultAbi> { 237 public: GuestParamsAndReturn(ThreadState * state)238 GuestParamsAndReturn(ThreadState* state) 239 : GuestParamsAndReturn<ReturnType(ParamType..., ...) noexcept(kNoexcept), 240 GuestAbi::kDefaultAbi>(state) {} 241 }; 242 243 template <typename FunctionType, 244 GuestAbi::CallingConventionsVariant kCallingConventionsVariant = GuestAbi::kDefaultAbi> 245 class GuestParamsValues; 246 247 class GuestVAListParams : GuestParamsAndReturnHelper { 248 public: 249 template <typename Func, GuestAbi::CallingConventionsVariant kCallingConventionsVariant> GuestVAListParams(const GuestParamsValues<Func,kCallingConventionsVariant> && named_parameters)250 GuestVAListParams(const GuestParamsValues<Func, kCallingConventionsVariant>&& named_parameters) 251 : calling_conventions_(named_parameters.kVAStartBase, 252 bit_cast<uint32_t>(named_parameters.s_)), 253 r_(named_parameters.r_) {} 254 255 // Initialize with va_list which is pointer to parameters which are located 256 // as if they are passed on the stack. GuestVAListParams(GuestAddr va)257 GuestVAListParams(GuestAddr va) 258 : calling_conventions_(arm::CallingConventions::kStackOnly, va), r_(nullptr) {} 259 260 template <typename T> GetParam()261 typename GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::GuestType& GetParam() { 262 static_assert(GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kArgumentClass == 263 ArgumentClass::kInteger); 264 return *this->ParamLocationAddress<T, GuestAbi::kDefaultAbi>( 265 r_, 266 nullptr, 267 nullptr, 268 calling_conventions_.GetNextIntArgLoc( 269 GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kSize, 270 GuestArgumentInfo<T, GuestAbi::kDefaultAbi>::kAlignment)); 271 } 272 273 template <typename T> GetPointerParam()274 T* GetPointerParam() { 275 return ToHostAddr<T>(GetParam<GuestAddr>()); 276 } 277 278 protected: get_sp(ThreadState * state)279 static GuestAddr get_sp(ThreadState* state) { return state->cpu.r[13]; } 280 281 arm::CallingConventions calling_conventions_; 282 uint32_t* r_; 283 }; 284 285 } // namespace berberis 286 287 #endif // BERBERIS_GUEST_ABI_GUEST_PARAMS_ARCH_H_ 288