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