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