| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <dlfcn.h> |
| #include <inttypes.h> |
| |
| #include <cstdio> |
| #include <memory> |
| |
| #include "android-base/stringprintf.h" |
| #include "jni.h" |
| #include "jvmti.h" |
| |
| // Test infrastructure |
| #include "jni_binder.h" |
| #include "jvmti_helper.h" |
| #include "scoped_local_ref.h" |
| #include "test_env.h" |
| |
| namespace art { |
| namespace Test986NativeBind { |
| |
| static void doUpPrintCall(JNIEnv* env, const char* function) { |
| ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986")); |
| jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V"); |
| env->CallStaticVoidMethod(klass.get(), targetMethod); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi__( |
| JNIEnv* env, [[maybe_unused]] jclass klass) { |
| doUpPrintCall(env, "doSayHi"); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi2( |
| JNIEnv* env, [[maybe_unused]] jclass klass) { |
| doUpPrintCall(env, "doSayHi2"); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, [[maybe_unused]] jclass klass) { |
| doUpPrintCall(env, "doSayBye"); |
| } |
| |
| static void doJvmtiMethodBind([[maybe_unused]] jvmtiEnv* jvmtienv, |
| JNIEnv* env, |
| [[maybe_unused]] jthread thread, |
| jmethodID m, |
| void* address, |
| /*out*/void** out_address) { |
| ScopedLocalRef<jclass> method_class(env, env->FindClass("java/lang/reflect/Method")); |
| ScopedLocalRef<jobject> method_obj(env, env->ToReflectedMethod(method_class.get(), m, false)); |
| Dl_info addr_info; |
| if (dladdr(address, &addr_info) == 0 || addr_info.dli_sname == nullptr) { |
| ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception")); |
| env->ThrowNew(exception_class.get(), "dladdr failure!"); |
| return; |
| } |
| ScopedLocalRef<jstring> sym_name(env, env->NewStringUTF(addr_info.dli_sname)); |
| ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986")); |
| jmethodID upcallMethod = env->GetStaticMethodID( |
| klass.get(), |
| "doNativeMethodBind", |
| "(Ljava/lang/reflect/Method;Ljava/lang/String;)Ljava/lang/String;"); |
| if (env->ExceptionCheck()) { |
| return; |
| } |
| ScopedLocalRef<jstring> new_symbol(env, |
| reinterpret_cast<jstring>( |
| env->CallStaticObjectMethod(klass.get(), |
| upcallMethod, |
| method_obj.get(), |
| sym_name.get()))); |
| const char* new_symbol_chars = env->GetStringUTFChars(new_symbol.get(), nullptr); |
| if (strcmp(new_symbol_chars, addr_info.dli_sname) != 0) { |
| *out_address = dlsym(RTLD_DEFAULT, new_symbol_chars); |
| if (*out_address == nullptr) { |
| ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception")); |
| env->ThrowNew(exception_class.get(), "dlsym failure!"); |
| return; |
| } |
| } |
| env->ReleaseStringUTFChars(new_symbol.get(), new_symbol_chars); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test986_setupNativeBindNotify( |
| [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass klass) { |
| jvmtiEventCallbacks cb; |
| memset(&cb, 0, sizeof(cb)); |
| cb.NativeMethodBind = doJvmtiMethodBind; |
| jvmti_env->SetEventCallbacks(&cb, sizeof(cb)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test986_setNativeBindNotify( |
| JNIEnv* env, [[maybe_unused]] jclass klass, jboolean enable) { |
| jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE, |
| JVMTI_EVENT_NATIVE_METHOD_BIND, |
| nullptr); |
| if (res != JVMTI_ERROR_NONE) { |
| JvmtiErrorToException(env, jvmti_env, res); |
| } |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test986_rebindTransformClass( |
| JNIEnv* env, [[maybe_unused]] jclass klass, jclass k) { |
| JNINativeMethod m[2]; |
| m[0].name = "sayHi"; |
| m[0].signature = "()V"; |
| m[0].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi__); |
| m[1].name = "sayHi2"; |
| m[1].signature = "()V"; |
| m[1].fnPtr = reinterpret_cast<void*>(Java_art_Test986_00024Transform_sayHi2); |
| env->RegisterNatives(k, m, 2); |
| } |
| |
| } // namespace Test986NativeBind |
| } // namespace art |