1 /*
2 * Copyright (C) 2023 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 #include "berberis/jni/jni_trampolines.h"
18
19 #include <vector>
20
21 #include <jni.h> // NOLINT [build/include_order]
22
23 #include "berberis/base/logging.h"
24 #include "berberis/guest_abi/function_wrappers.h"
25 #include "berberis/guest_abi/guest_arguments.h"
26 #include "berberis/guest_abi/guest_params.h"
27 #include "berberis/guest_state/guest_addr.h"
28 #include "berberis/guest_state/guest_state.h"
29 #include "berberis/native_bridge/jmethod_shorty.h"
30 #include "berberis/runtime_primitives/runtime_library.h"
31
32 #include "guest_jni_trampolines.h"
33
34 // #define LOG_JNI(...) ALOGE(__VA_ARGS__)
35 #define LOG_JNI(...)
36
37 namespace berberis {
38
39 namespace {
40
ConvertDalvikTypeCharToWrapperTypeChar(char c)41 char ConvertDalvikTypeCharToWrapperTypeChar(char c) {
42 switch (c) {
43 case 'V': // void
44 return 'v';
45 case 'Z': // boolean
46 return 'z';
47 case 'B': // byte
48 return 'b';
49 case 'S': // short
50 return 's';
51 case 'C': // char
52 return 'c';
53 case 'I': // int
54 return 'i';
55 case 'L': // class object - pointer
56 return 'p';
57 case 'J': // long
58 return 'l';
59 case 'F': // float
60 return 'f';
61 case 'D': // double
62 return 'd';
63 default:
64 FATAL("Failed to convert Dalvik char '%c'", c);
65 }
66 }
67
ConvertDalvikShortyToWrapperSignature(char * dst,int size,const char * src,bool add_jnienv_and_jobject)68 void ConvertDalvikShortyToWrapperSignature(char* dst,
69 int size,
70 const char* src,
71 bool add_jnienv_and_jobject) {
72 // return type, env and clazz.
73 CHECK_GT(size, 3);
74 char* cur = dst;
75 *cur++ = ConvertDalvikTypeCharToWrapperTypeChar(*src++);
76
77 if (add_jnienv_and_jobject) {
78 *cur++ = 'p';
79 *cur++ = 'p';
80 }
81
82 while (*src) {
83 CHECK_LT(cur, dst + (size - 1));
84 *cur++ = ConvertDalvikTypeCharToWrapperTypeChar(*src++);
85 }
86
87 *cur = '\0';
88 }
89
RunGuestJNIFunction(GuestAddr pc,GuestArgumentBuffer * buf)90 void RunGuestJNIFunction(GuestAddr pc, GuestArgumentBuffer* buf) {
91 auto [host_jni_env] = HostArgumentsValues<void(JNIEnv*)>(buf);
92 {
93 auto&& [guest_jni_env] = GuestArgumentsReferences<void(JNIEnv*)>(buf);
94 guest_jni_env = ToGuestJNIEnv(host_jni_env);
95 }
96 RunGuestCall(pc, buf);
97 }
98
RunGuestJNIOnLoad(GuestAddr pc,GuestArgumentBuffer * buf)99 void RunGuestJNIOnLoad(GuestAddr pc, GuestArgumentBuffer* buf) {
100 auto [host_java_vm, reserved] = HostArgumentsValues<decltype(JNI_OnLoad)>(buf);
101 {
102 auto&& [guest_java_vm, reserved] = GuestArgumentsReferences<decltype(JNI_OnLoad)>(buf);
103 guest_java_vm = ToGuestJavaVM(host_java_vm);
104 }
105 RunGuestCall(pc, buf);
106 }
107
108 } // namespace
109
WrapGuestJNIFunction(GuestAddr pc,const char * shorty,const char * name,bool has_jnienv_and_jobject)110 HostCode WrapGuestJNIFunction(GuestAddr pc,
111 const char* shorty,
112 const char* name,
113 bool has_jnienv_and_jobject) {
114 const int kMaxSignatureSize = 128;
115 char signature[kMaxSignatureSize];
116 ConvertDalvikShortyToWrapperSignature(
117 signature, kMaxSignatureSize, shorty, has_jnienv_and_jobject);
118 auto guest_runner = has_jnienv_and_jobject ? RunGuestJNIFunction : RunGuestCall;
119 return WrapGuestFunctionImpl(pc, signature, guest_runner, name);
120 }
121
WrapGuestJNIOnLoad(GuestAddr pc)122 HostCode WrapGuestJNIOnLoad(GuestAddr pc) {
123 return WrapGuestFunctionImpl(pc, "ipp", RunGuestJNIOnLoad, "JNI_OnLoad");
124 }
125
126 namespace {
127
ConvertVAList(JNIEnv * env,jmethodID methodID,GuestVAListParams && params)128 std::vector<jvalue> ConvertVAList(JNIEnv* env, jmethodID methodID, GuestVAListParams&& params) {
129 std::vector<jvalue> result;
130 const char* short_signature = GetJMethodShorty(env, methodID);
131 CHECK(short_signature);
132 short_signature++; // skip return value
133 int len = strlen(short_signature);
134 result.resize(len);
135 for (int i = 0; i < len; i++) {
136 jvalue& arg = result[i];
137 char c = short_signature[i];
138 switch (c) {
139 case 'Z': // boolean (u8)
140 arg.z = params.GetParam<uint8_t>();
141 break;
142 case 'B': // byte (i8)
143 arg.b = params.GetParam<int8_t>();
144 break;
145 case 'S': // short (i16)
146 arg.s = params.GetParam<int16_t>();
147 break;
148 case 'C': // char (u16)
149 arg.c = params.GetParam<uint16_t>();
150 break;
151 case 'I': // int (i32)
152 arg.i = params.GetParam<int32_t>();
153 break;
154 case 'J': // long (i64)
155 arg.j = params.GetParam<int64_t>();
156 break;
157 case 'F': // float - passed as double
158 arg.f = params.GetParam<double>();
159 break;
160 case 'D': // double
161 arg.d = params.GetParam<double>();
162 break;
163 case 'L': // class object (pointer)
164 arg.l = params.GetParam<jobject>();
165 break;
166 default:
167 LOG_ALWAYS_FATAL("Failed to convert Dalvik char '%c'", c);
168 break;
169 }
170 }
171 return result;
172 }
173
174 // jint RegisterNatives(
175 // JNIEnv *env, jclass clazz,
176 // const JNINativeMethod *methods, jint nMethods);
DoTrampoline_JNIEnv_RegisterNatives(HostCode,ProcessState * state)177 void DoTrampoline_JNIEnv_RegisterNatives(HostCode /* callee */, ProcessState* state) {
178 using PFN_callee = decltype(std::declval<JNIEnv>().functions->RegisterNatives);
179 auto [guest_env, arg_clazz, arg_methods, arg_n] = GuestParamsValues<PFN_callee>(state);
180 JNIEnv* arg_env = ToHostJNIEnv(guest_env);
181
182 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
183 ret = (arg_env->functions)->RegisterNatives(arg_env, arg_clazz, arg_methods, arg_n);
184 }
185
186 // jint GetJavaVM(
187 // JNIEnv *env, JavaVM **vm);
DoTrampoline_JNIEnv_GetJavaVM(HostCode,ProcessState * state)188 void DoTrampoline_JNIEnv_GetJavaVM(HostCode /* callee */, ProcessState* state) {
189 using PFN_callee = decltype(std::declval<JNIEnv>().functions->GetJavaVM);
190 auto [guest_env, arg_vm] = GuestParamsValues<PFN_callee>(state);
191 JNIEnv* arg_env = ToHostJNIEnv(guest_env);
192 JavaVM* host_vm;
193
194 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
195 ret = (arg_env->functions)->GetJavaVM(arg_env, &host_vm);
196 if (ret == 0) {
197 *bit_cast<GuestType<JavaVM*>*>(arg_vm) = ToGuestJavaVM(host_vm);
198 }
199 }
200
DoTrampoline_JNIEnv_CallStaticVoidMethodV(HostCode,ProcessState * state)201 void DoTrampoline_JNIEnv_CallStaticVoidMethodV(HostCode /* callee */, ProcessState* state) {
202 using PFN_callee = decltype(std::declval<JNIEnv>().functions->CallStaticVoidMethodV);
203 auto [arg_env, arg_1, arg_2, arg_va] = GuestParamsValues<PFN_callee>(state);
204 JNIEnv* arg_0 = ToHostJNIEnv(arg_env);
205 std::vector<jvalue> arg_vector = ConvertVAList(arg_0, arg_2, ToGuestAddr(arg_va));
206 jvalue* arg_3 = &arg_vector[0];
207
208 // Note, this call is the only difference from the auto-generated trampoline.
209 JNIEnv_CallStaticVoidMethodV_ForGuest(arg_0, arg_1, arg_2, arg_3);
210
211 (arg_0->functions)->CallStaticVoidMethodA(arg_0, arg_1, arg_2, arg_3);
212 }
213
214 struct KnownMethodTrampoline {
215 unsigned index;
216 TrampolineFunc marshal_and_call;
217 };
218
219 #include "jni_trampolines-inl.h" // NOLINT(build/include)
220
DoJavaVMTrampoline_DestroyJavaVM(HostCode,ProcessState * state)221 void DoJavaVMTrampoline_DestroyJavaVM(HostCode /* callee */, ProcessState* state) {
222 using PFN_callee = decltype(std::declval<JavaVM>().functions->DestroyJavaVM);
223 auto [arg_vm] = GuestParamsValues<PFN_callee>(state);
224 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
225
226 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
227 ret = (arg_java_vm->functions)->DestroyJavaVM(arg_java_vm);
228 }
229
230 // jint AttachCurrentThread(JavaVM*, JNIEnv**, void*);
DoJavaVMTrampoline_AttachCurrentThread(HostCode,ProcessState * state)231 void DoJavaVMTrampoline_AttachCurrentThread(HostCode /* callee */, ProcessState* state) {
232 using PFN_callee = decltype(std::declval<JavaVM>().functions->AttachCurrentThread);
233 auto [arg_vm, arg_env_ptr, arg_args] = GuestParamsValues<PFN_callee>(state);
234 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
235 JNIEnv* env = nullptr;
236
237 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
238 ret = (arg_java_vm->functions)->AttachCurrentThread(arg_java_vm, &env, arg_args);
239
240 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(env);
241 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
242 }
243
244 // jint DetachCurrentThread(JavaVM*);
DoJavaVMTrampoline_DetachCurrentThread(HostCode,ProcessState * state)245 void DoJavaVMTrampoline_DetachCurrentThread(HostCode /* callee */, ProcessState* state) {
246 using PFN_callee = decltype(std::declval<JavaVM>().functions->DetachCurrentThread);
247 auto [arg_vm] = GuestParamsValues<PFN_callee>(state);
248 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
249
250 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
251 ret = (arg_java_vm->functions)->DetachCurrentThread(arg_java_vm);
252 }
253
254 // jint GetEnv(JavaVM*, void**, jint);
DoJavaVMTrampoline_GetEnv(HostCode,ProcessState * state)255 void DoJavaVMTrampoline_GetEnv(HostCode /* callee */, ProcessState* state) {
256 using PFN_callee = decltype(std::declval<JavaVM>().functions->GetEnv);
257 auto [arg_vm, arg_env_ptr, arg_version] = GuestParamsValues<PFN_callee>(state);
258 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
259
260 LOG_JNI("JavaVM::GetEnv(%p, %p, %d)", arg_java_vm, arg_env_ptr, arg_version);
261
262 void* env = nullptr;
263 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
264 ret = (arg_java_vm->functions)->GetEnv(arg_java_vm, &env, arg_version);
265
266 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(env);
267 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
268
269 LOG_JNI("= jint(%d)", ret);
270 }
271
272 // jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);
DoJavaVMTrampoline_AttachCurrentThreadAsDaemon(HostCode,ProcessState * state)273 void DoJavaVMTrampoline_AttachCurrentThreadAsDaemon(HostCode /* callee */, ProcessState* state) {
274 using PFN_callee = decltype(std::declval<JavaVM>().functions->AttachCurrentThreadAsDaemon);
275 auto [arg_vm, arg_env_ptr, arg_args] = GuestParamsValues<PFN_callee>(state);
276 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
277
278 JNIEnv* env = nullptr;
279 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
280 ret = (arg_java_vm->functions)->AttachCurrentThreadAsDaemon(arg_java_vm, &env, arg_args);
281
282 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(env);
283 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
284 }
285
WrapJavaVM(void * java_vm)286 void WrapJavaVM(void* java_vm) {
287 HostCode* vtable = *reinterpret_cast<HostCode**>(java_vm);
288 // vtable[0] is NULL
289 // vtable[1] is NULL
290 // vtable[2] is NULL
291
292 WrapHostFunctionImpl(vtable[3], DoJavaVMTrampoline_DestroyJavaVM, "JavaVM::DestroyJavaVM");
293
294 WrapHostFunctionImpl(
295 vtable[4], DoJavaVMTrampoline_AttachCurrentThread, "JavaVM::AttachCurrentThread");
296
297 WrapHostFunctionImpl(
298 vtable[5], DoJavaVMTrampoline_DetachCurrentThread, "JavaVM::DetachCurrentThread");
299
300 WrapHostFunctionImpl(vtable[6], DoJavaVMTrampoline_GetEnv, "JavaVM::GetEnv");
301
302 WrapHostFunctionImpl(vtable[7],
303 DoJavaVMTrampoline_AttachCurrentThreadAsDaemon,
304 "JavaVM::AttachCurrentThreadAsDaemon");
305 }
306
307 // We set this to 1 when host JNIEnv/JavaVM functions are wrapped.
308 std::atomic<uint32_t> g_jni_env_wrapped = {0};
309 std::atomic<uint32_t> g_java_vm_wrapped = {0};
310
311 } // namespace
312
ToGuestJNIEnv(void * host_jni_env)313 GuestType<JNIEnv*> ToGuestJNIEnv(void* host_jni_env) {
314 if (!host_jni_env) {
315 return 0;
316 }
317 // We need to wrap host JNI functions only once. We use an atomic variable
318 // to guard this initialization. Since we use very simple logic without
319 // waiting here, multiple threads can wrap host JNI functions simultaneously.
320 // This is OK since wrapping is thread-safe and later wrappings override
321 // previous ones atomically.
322 // TODO(halyavin) Consider creating a general mechanism for thread-safe
323 // initialization with parameters, if we need it in more than one place.
324 if (std::atomic_load_explicit(&g_jni_env_wrapped, std::memory_order_acquire) == 0U) {
325 WrapJNIEnv(host_jni_env);
326 std::atomic_store_explicit(&g_jni_env_wrapped, 1U, std::memory_order_release);
327 }
328 return static_cast<JNIEnv*>(host_jni_env);
329 }
330
ToHostJNIEnv(GuestType<JNIEnv * > guest_jni_env)331 JNIEnv* ToHostJNIEnv(GuestType<JNIEnv*> guest_jni_env) {
332 return static_cast<JNIEnv*>(guest_jni_env);
333 }
334
ToGuestJavaVM(void * host_java_vm)335 GuestType<JavaVM*> ToGuestJavaVM(void* host_java_vm) {
336 CHECK(host_java_vm);
337 if (std::atomic_load_explicit(&g_java_vm_wrapped, std::memory_order_acquire) == 0U) {
338 WrapJavaVM(host_java_vm);
339 std::atomic_store_explicit(&g_java_vm_wrapped, 1U, std::memory_order_release);
340 }
341 return static_cast<JavaVM*>(host_java_vm);
342 }
343
ToHostJavaVM(GuestType<JavaVM * > guest_java_vm)344 JavaVM* ToHostJavaVM(GuestType<JavaVM*> guest_java_vm) {
345 return static_cast<JavaVM*>(guest_java_vm);
346 }
347
348 } // namespace berberis
349