1 /*
2  * Copyright (C) 2017 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 <dlfcn.h>
18 #include <inttypes.h>
19 
20 #include <cstdio>
21 #include <memory>
22 
23 #include "android-base/stringprintf.h"
24 #include "jni.h"
25 #include "jvmti.h"
26 
27 // Test infrastructure
28 #include "jni_binder.h"
29 #include "jvmti_helper.h"
30 #include "scoped_local_ref.h"
31 #include "test_env.h"
32 
33 namespace art {
34 namespace Test986NativeBind {
35 
doUpPrintCall(JNIEnv * env,const char * function)36 static void doUpPrintCall(JNIEnv* env, const char* function) {
37   ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
38   jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V");
39   env->CallStaticVoidMethod(klass.get(), targetMethod);
40 }
41 
Java_art_Test986_00024Transform_sayHi__(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED)42 extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi__(
43     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
44   doUpPrintCall(env, "doSayHi");
45 }
46 
Java_art_Test986_00024Transform_sayHi2(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED)47 extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi2(
48     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
49   doUpPrintCall(env, "doSayHi2");
50 }
51 
NoReallySayGoodbye(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED)52 extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
53   doUpPrintCall(env, "doSayBye");
54 }
55 
doJvmtiMethodBind(jvmtiEnv * jvmtienv ATTRIBUTE_UNUSED,JNIEnv * env,jthread thread ATTRIBUTE_UNUSED,jmethodID m,void * address,void ** out_address)56 static void doJvmtiMethodBind(jvmtiEnv* jvmtienv ATTRIBUTE_UNUSED,
57                               JNIEnv* env,
58                               jthread thread ATTRIBUTE_UNUSED,
59                               jmethodID m,
60                               void* address,
61                               /*out*/void** out_address) {
62   ScopedLocalRef<jclass> method_class(env, env->FindClass("java/lang/reflect/Method"));
63   ScopedLocalRef<jobject> method_obj(env, env->ToReflectedMethod(method_class.get(), m, false));
64   Dl_info addr_info;
65   if (dladdr(address, &addr_info) == 0 || addr_info.dli_sname == nullptr) {
66     ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
67     env->ThrowNew(exception_class.get(), "dladdr failure!");
68     return;
69   }
70   ScopedLocalRef<jstring> sym_name(env, env->NewStringUTF(addr_info.dli_sname));
71   ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986"));
72   jmethodID upcallMethod = env->GetStaticMethodID(
73       klass.get(),
74       "doNativeMethodBind",
75       "(Ljava/lang/reflect/Method;Ljava/lang/String;)Ljava/lang/String;");
76   if (env->ExceptionCheck()) {
77     return;
78   }
79   ScopedLocalRef<jstring> new_symbol(env,
80                                      reinterpret_cast<jstring>(
81                                          env->CallStaticObjectMethod(klass.get(),
82                                                                  upcallMethod,
83                                                                  method_obj.get(),
84                                                                  sym_name.get())));
85   const char* new_symbol_chars = env->GetStringUTFChars(new_symbol.get(), nullptr);
86   if (strcmp(new_symbol_chars, addr_info.dli_sname) != 0) {
87     *out_address = dlsym(RTLD_DEFAULT, new_symbol_chars);
88     if (*out_address == nullptr) {
89       ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception"));
90       env->ThrowNew(exception_class.get(), "dlsym failure!");
91       return;
92     }
93   }
94   env->ReleaseStringUTFChars(new_symbol.get(), new_symbol_chars);
95 }
96 
Java_art_Test986_setupNativeBindNotify(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED)97 extern "C" JNIEXPORT void JNICALL Java_art_Test986_setupNativeBindNotify(
98     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
99   jvmtiEventCallbacks cb;
100   memset(&cb, 0, sizeof(cb));
101   cb.NativeMethodBind = doJvmtiMethodBind;
102   jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
103 }
104 
Java_art_Test986_setNativeBindNotify(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable)105 extern "C" JNIEXPORT void JNICALL Java_art_Test986_setNativeBindNotify(
106     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
107   jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
108                                                        JVMTI_EVENT_NATIVE_METHOD_BIND,
109                                                        nullptr);
110   if (res != JVMTI_ERROR_NONE) {
111     JvmtiErrorToException(env, jvmti_env, res);
112   }
113 }
114 
Java_art_Test986_rebindTransformClass(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass k)115 extern "C" JNIEXPORT void JNICALL Java_art_Test986_rebindTransformClass(
116     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass k) {
117   JNINativeMethod m[2];
118   m[0].name = "sayHi";
119   m[0].signature = "()V";
120   m[0].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi__);
121   m[1].name = "sayHi2";
122   m[1].signature = "()V";
123   m[1].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi2);
124   env->RegisterNatives(k, m, 2);
125 }
126 
127 }  // namespace Test986NativeBind
128 }  // namespace art
129