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_helper.h"
     27 #include "jvmti_helper.h"
     28 #include "jvmti.h"
     29 #include "scoped_primitive_array.h"
     30 #include "test_env.h"
     31 
     32 namespace art {
     33 
     34 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(
     35     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass target, jbyteArray dex_bytes) {
     36   jvmtiClassDefinition def;
     37   def.klass = target;
     38   def.class_byte_count = static_cast<jint>(env->GetArrayLength(dex_bytes));
     39   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
     40   jvmtiError res =jvmti_env->Allocate(def.class_byte_count,
     41                                       const_cast<unsigned char**>(&def.class_bytes));
     42   if (res != JVMTI_ERROR_NONE) {
     43     return static_cast<jint>(res);
     44   }
     45   memcpy(const_cast<unsigned char*>(def.class_bytes), redef_bytes, def.class_byte_count);
     46   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
     47   // Do the redefinition.
     48   res = jvmti_env->RedefineClasses(1, &def);
     49   return static_cast<jint>(res);
     50 }
     51 
     52 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(
     53     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jclass target) {
     54   return jvmti_env->RetransformClasses(1, &target);
     55 }
     56 
     57 class TransformationData {
     58  public:
     59   TransformationData() : redefinitions_(), should_pop_(false) {}
     60 
     61   void SetPop(bool val) {
     62     should_pop_ = val;
     63   }
     64 
     65   void ClearRedefinitions() {
     66     redefinitions_.clear();
     67   }
     68 
     69   void PushRedefinition(std::string name, std::vector<unsigned char> data) {
     70     if (redefinitions_.find(name) == redefinitions_.end()) {
     71       std::stack<std::vector<unsigned char>> stack;
     72       redefinitions_[name] = std::move(stack);
     73     }
     74     redefinitions_[name].push(std::move(data));
     75   }
     76 
     77   bool RetrieveRedefinition(std::string name, /*out*/std::vector<unsigned char>* data) {
     78     auto stack = redefinitions_.find(name);
     79     if (stack == redefinitions_.end() || stack->second.empty()) {
     80       return false;
     81     } else {
     82       *data = stack->second.top();
     83       return true;
     84     }
     85   }
     86 
     87   void PopRedefinition(std::string name) {
     88     if (should_pop_) {
     89       auto stack = redefinitions_.find(name);
     90       if (stack == redefinitions_.end() || stack->second.empty()) {
     91         return;
     92       } else {
     93         stack->second.pop();
     94       }
     95     }
     96   }
     97 
     98  private:
     99   std::unordered_map<std::string, std::stack<std::vector<unsigned char>>> redefinitions_;
    100   bool should_pop_;
    101 };
    102 
    103 static TransformationData data;
    104 
    105 // The hook we are using.
    106 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* local_jvmti_env,
    107                                                     JNIEnv* jni_env ATTRIBUTE_UNUSED,
    108                                                     jclass class_being_redefined ATTRIBUTE_UNUSED,
    109                                                     jobject loader ATTRIBUTE_UNUSED,
    110                                                     const char* name,
    111                                                     jobject protection_domain ATTRIBUTE_UNUSED,
    112                                                     jint class_data_len ATTRIBUTE_UNUSED,
    113                                                     const unsigned char* class_dat ATTRIBUTE_UNUSED,
    114                                                     jint* new_class_data_len,
    115                                                     unsigned char** new_class_data) {
    116   std::string name_str(name);
    117   std::vector<unsigned char> dex_data;
    118   if (data.RetrieveRedefinition(name_str, &dex_data)) {
    119     unsigned char* jvmti_dex_data;
    120     if (JVMTI_ERROR_NONE != local_jvmti_env->Allocate(dex_data.size(), &jvmti_dex_data)) {
    121       LOG(FATAL) << "Unable to allocate output buffer for " << name;
    122       return;
    123     }
    124     memcpy(jvmti_dex_data, dex_data.data(), dex_data.size());
    125     *new_class_data_len = dex_data.size();
    126     *new_class_data = jvmti_dex_data;
    127     data.PopRedefinition(name);
    128   }
    129 }
    130 
    131 extern "C"
    132 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(
    133     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
    134   jvmtiEventCallbacks cb;
    135   memset(&cb, 0, sizeof(cb));
    136   cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
    137   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
    138     return;
    139   }
    140   JvmtiErrorToException(env,
    141                         jvmti_env,
    142                         jvmti_env->SetEventNotificationMode(
    143                             enable == JNI_TRUE ? JVMTI_ENABLE : JVMTI_DISABLE,
    144                             JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
    145                             nullptr));
    146   return;
    147 }
    148 
    149 extern "C"
    150 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(
    151     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
    152   data.ClearRedefinitions();
    153 }
    154 
    155 extern "C"
    156 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(
    157     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
    158   data.SetPop(enable == JNI_TRUE ? true : false);
    159 }
    160 
    161 extern "C"
    162 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(
    163     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring class_name, jbyteArray dex_bytes) {
    164   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
    165   std::string name_str(name_chrs);
    166   env->ReleaseStringUTFChars(class_name, name_chrs);
    167   std::vector<unsigned char> dex_data;
    168   dex_data.resize(env->GetArrayLength(dex_bytes));
    169   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
    170   memcpy(dex_data.data(), redef_bytes, env->GetArrayLength(dex_bytes));
    171   data.PushRedefinition(std::move(name_str), std::move(dex_data));
    172   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
    173 }
    174 
    175 }  // namespace art
    176 
    177