blob: c6e8726415e131f69d1d7adb5200c9a454e9c0f4 [file] [log] [blame]
Andreas Gampe6068a2a2017-04-03 17:28:23 -07001/*
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"
Andreas Gampe6068a2a2017-04-03 17:28:23 -070026#include "jni_helper.h"
27#include "jvmti_helper.h"
28#include "jvmti.h"
29#include "scoped_primitive_array.h"
Andreas Gampe7a10cb42017-04-05 10:20:25 -070030#include "test_env.h"
Andreas Gampe6068a2a2017-04-03 17:28:23 -070031
Andreas Gampe7a10cb42017-04-05 10:20:25 -070032namespace art {
Andreas Gampe6068a2a2017-04-03 17:28:23 -070033
34extern "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);
Andreas Gampe7a10cb42017-04-05 10:20:25 -070040 jvmtiError res =jvmti_env->Allocate(def.class_byte_count,
41 const_cast<unsigned char**>(&def.class_bytes));
Andreas Gampe6068a2a2017-04-03 17:28:23 -070042 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.
Andreas Gampe7a10cb42017-04-05 10:20:25 -070048 res = jvmti_env->RedefineClasses(1, &def);
Andreas Gampe6068a2a2017-04-03 17:28:23 -070049 return static_cast<jint>(res);
50}
51
52extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(
53 JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jclass target) {
Andreas Gampe7a10cb42017-04-05 10:20:25 -070054 return jvmti_env->RetransformClasses(1, &target);
Andreas Gampe6068a2a2017-04-03 17:28:23 -070055}
56
57class 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
103static TransformationData data;
104
105// The hook we are using.
Andreas Gampe7a10cb42017-04-05 10:20:25 -0700106void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* local_jvmti_env,
Andreas Gampe6068a2a2017-04-03 17:28:23 -0700107 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;
Andreas Gampe7a10cb42017-04-05 10:20:25 -0700120 if (JVMTI_ERROR_NONE != local_jvmti_env->Allocate(dex_data.size(), &jvmti_dex_data)) {
Andreas Gampe6068a2a2017-04-03 17:28:23 -0700121 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
131extern "C"
132JNIEXPORT 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;
Andreas Gampe7a10cb42017-04-05 10:20:25 -0700137 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
Andreas Gampe6068a2a2017-04-03 17:28:23 -0700138 return;
139 }
Andreas Gampe7a10cb42017-04-05 10:20:25 -0700140 JvmtiErrorToException(env,
141 jvmti_env,
142 jvmti_env->SetEventNotificationMode(
Andreas Gampe6068a2a2017-04-03 17:28:23 -0700143 enable == JNI_TRUE ? JVMTI_ENABLE : JVMTI_DISABLE,
144 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
145 nullptr));
146 return;
147}
148
149extern "C"
150JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(
151 JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
152 data.ClearRedefinitions();
153}
154
155extern "C"
156JNIEXPORT 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
161extern "C"
162JNIEXPORT 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
Andreas Gampe7a10cb42017-04-05 10:20:25 -0700175} // namespace art
Andreas Gampe6068a2a2017-04-03 17:28:23 -0700176