Home | History | Annotate | Download | only in jni
      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 <jni.h>
     18 
     19 #include <stack>
     20 #include <string>
     21 #include <unordered_map>
     22 #include <vector>
     23 
     24 #include "android-base/logging.h"
     25 #include "android-base/macros.h"
     26 #include "jni_binder.h"
     27 #include "jni_helper.h"
     28 #include "jvmti_helper.h"
     29 #include "jvmti.h"
     30 #include "scoped_primitive_array.h"
     31 #include "test_env.h"
     32 
     33 namespace art {
     34 
     35 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(
     36     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass target, jbyteArray dex_bytes) {
     37   jvmtiClassDefinition def;
     38   def.klass = target;
     39   def.class_byte_count = static_cast<jint>(env->GetArrayLength(dex_bytes));
     40   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
     41   jvmtiError res =jvmti_env->Allocate(def.class_byte_count,
     42                                       const_cast<unsigned char**>(&def.class_bytes));
     43   if (res != JVMTI_ERROR_NONE) {
     44     return static_cast<jint>(res);
     45   }
     46   memcpy(const_cast<unsigned char*>(def.class_bytes), redef_bytes, def.class_byte_count);
     47   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
     48   // Do the redefinition.
     49   res = jvmti_env->RedefineClasses(1, &def);
     50   return static_cast<jint>(res);
     51 }
     52 
     53 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(
     54     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jclass target) {
     55   return jvmti_env->RetransformClasses(1, &target);
     56 }
     57 
     58 class TransformationData {
     59  public:
     60   TransformationData() : redefinitions_(), should_pop_(false) {}
     61 
     62   void SetPop(bool val) {
     63     should_pop_ = val;
     64   }
     65 
     66   void ClearRedefinitions() {
     67     redefinitions_.clear();
     68   }
     69 
     70   void PushRedefinition(std::string name, std::vector<unsigned char> data) {
     71     if (redefinitions_.find(name) == redefinitions_.end()) {
     72       std::stack<std::vector<unsigned char>> stack;
     73       redefinitions_[name] = std::move(stack);
     74     }
     75     redefinitions_[name].push(std::move(data));
     76   }
     77 
     78   bool RetrieveRedefinition(std::string name, /*out*/std::vector<unsigned char>* data) {
     79     auto stack = redefinitions_.find(name);
     80     if (stack == redefinitions_.end() || stack->second.empty()) {
     81       return false;
     82     } else {
     83       *data = stack->second.top();
     84       return true;
     85     }
     86   }
     87 
     88   void PopRedefinition(std::string name) {
     89     if (should_pop_) {
     90       auto stack = redefinitions_.find(name);
     91       if (stack == redefinitions_.end() || stack->second.empty()) {
     92         return;
     93       } else {
     94         stack->second.pop();
     95       }
     96     }
     97   }
     98 
     99  private:
    100   std::unordered_map<std::string, std::stack<std::vector<unsigned char>>> redefinitions_;
    101   bool should_pop_;
    102 };
    103 
    104 static TransformationData data;
    105 
    106 // The hook we are using.
    107 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* local_jvmti_env,
    108                                                     JNIEnv* jni_env ATTRIBUTE_UNUSED,
    109                                                     jclass class_being_redefined ATTRIBUTE_UNUSED,
    110                                                     jobject loader ATTRIBUTE_UNUSED,
    111                                                     const char* name,
    112                                                     jobject protection_domain ATTRIBUTE_UNUSED,
    113                                                     jint class_data_len ATTRIBUTE_UNUSED,
    114                                                     const unsigned char* class_dat ATTRIBUTE_UNUSED,
    115                                                     jint* new_class_data_len,
    116                                                     unsigned char** new_class_data) {
    117   std::string name_str(name);
    118   std::vector<unsigned char> dex_data;
    119   if (data.RetrieveRedefinition(name_str, &dex_data)) {
    120     unsigned char* jvmti_dex_data;
    121     if (JVMTI_ERROR_NONE == local_jvmti_env->Allocate(dex_data.size(), &jvmti_dex_data)) {
    122       memcpy(jvmti_dex_data, dex_data.data(), dex_data.size());
    123       *new_class_data_len = dex_data.size();
    124       *new_class_data = jvmti_dex_data;
    125       data.PopRedefinition(name);
    126     } else {
    127       LOG(FATAL) << "Unable to allocate output buffer for " << name;
    128     }
    129   }
    130 }
    131 
    132 extern "C"
    133 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(
    134     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
    135   jvmtiEventCallbacks cb;
    136   memset(&cb, 0, sizeof(cb));
    137   cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
    138   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
    139     return;
    140   }
    141   JvmtiErrorToException(env,
    142                         jvmti_env,
    143                         jvmti_env->SetEventNotificationMode(
    144                             enable == JNI_TRUE ? JVMTI_ENABLE : JVMTI_DISABLE,
    145                             JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
    146                             nullptr));
    147   return;
    148 }
    149 
    150 extern "C"
    151 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(
    152     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
    153   data.ClearRedefinitions();
    154 }
    155 
    156 extern "C"
    157 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(
    158     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
    159   data.SetPop(enable == JNI_TRUE ? true : false);
    160 }
    161 
    162 extern "C"
    163 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(
    164     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring class_name, jbyteArray dex_bytes) {
    165   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
    166   std::string name_str(name_chrs);
    167   env->ReleaseStringUTFChars(class_name, name_chrs);
    168   std::vector<unsigned char> dex_data;
    169   dex_data.resize(env->GetArrayLength(dex_bytes));
    170   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
    171   memcpy(dex_data.data(), redef_bytes, env->GetArrayLength(dex_bytes));
    172   data.PushRedefinition(std::move(name_str), std::move(dex_data));
    173   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
    174 }
    175 
    176 static JNINativeMethod gMethods[] = {
    177   { "redefineClass", "(Ljava/lang/Class;[B)I",
    178           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass },
    179 
    180   { "retransformClass", "(Ljava/lang/Class;)I",
    181           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass },
    182 
    183   { "setTransformationEvent", "(Z)V",
    184           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent },
    185 
    186   { "clearTransformations", "()V",
    187           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations },
    188 
    189   { "setPopTransformations", "(Z)V",
    190           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations },
    191 
    192   { "pushTransformationResult", "(Ljava/lang/String;[B)V",
    193           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult },
    194 };
    195 
    196 void register_android_jvmti_cts_JvmtiRedefineClassesTest(jvmtiEnv* jenv, JNIEnv* env) {
    197   ScopedLocalRef<jclass> klass(env, GetClass(jenv, env,
    198           "android/jvmti/cts/JvmtiRedefineClassesTest", nullptr));
    199   if (klass.get() == nullptr) {
    200     env->ExceptionClear();
    201     return;
    202   }
    203 
    204   env->RegisterNatives(klass.get(), gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
    205   if (env->ExceptionCheck()) {
    206     env->ExceptionClear();
    207     LOG(ERROR) << "Could not register natives for JvmtiRedefineClassesTest class";
    208   }
    209 }
    210 
    211 }  // namespace art
    212 
    213