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
Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass target,jbyteArray dex_bytes)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
Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jclass target)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:
TransformationData()59 TransformationData() : redefinitions_(), should_pop_(false) {}
60
SetPop(bool val)61 void SetPop(bool val) {
62 should_pop_ = val;
63 }
64
ClearRedefinitions()65 void ClearRedefinitions() {
66 redefinitions_.clear();
67 }
68
PushRedefinition(std::string name,std::vector<unsigned char> data)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
RetrieveRedefinition(std::string name,std::vector<unsigned char> * data)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
PopRedefinition(std::string name)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.
CommonClassFileLoadHookRetransformable(jvmtiEnv * local_jvmti_env,JNIEnv * jni_env ATTRIBUTE_UNUSED,jclass class_being_redefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protection_domain ATTRIBUTE_UNUSED,jint class_data_len ATTRIBUTE_UNUSED,const unsigned char * class_dat ATTRIBUTE_UNUSED,jint * new_class_data_len,unsigned char ** new_class_data)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"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable)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"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED)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"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jboolean enable)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"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jstring class_name,jbyteArray dex_bytes)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