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