blob: 4bceef53bb8a14a1a49482afacd1c87a2410b3e2 [file] [log] [blame]
Alex Light1e07ca62016-12-02 11:40:56 -08001/*
2 * Copyright (C) 2016 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 "ti-agent/common_helper.h"
18
19#include <stdio.h>
Alex Light460d1b42017-01-10 15:37:17 +000020#include <sstream>
Alex Light6ac57502017-01-19 15:05:06 -080021#include <deque>
Alex Light1e07ca62016-12-02 11:40:56 -080022
Alex Lightdba61482016-12-21 08:20:29 -080023#include "art_method.h"
Alex Light1e07ca62016-12-02 11:40:56 -080024#include "jni.h"
25#include "openjdkjvmti/jvmti.h"
Alex Lightdba61482016-12-21 08:20:29 -080026#include "scoped_thread_state_change-inl.h"
27#include "stack.h"
Alex Light1e07ca62016-12-02 11:40:56 -080028#include "ti-agent/common_load.h"
29#include "utils.h"
30
31namespace art {
32bool RuntimeIsJVM;
33
34bool IsJVM() {
35 return RuntimeIsJVM;
36}
37
38void SetAllCapabilities(jvmtiEnv* env) {
39 jvmtiCapabilities caps;
40 env->GetPotentialCapabilities(&caps);
41 env->AddCapabilities(&caps);
42}
43
Andreas Gampe1bdaf732017-01-09 19:21:06 -080044bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
45 if (error == JVMTI_ERROR_NONE) {
46 return false;
47 }
48
49 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
50 if (rt_exception.get() == nullptr) {
51 // CNFE should be pending.
52 return true;
53 }
54
55 char* err;
56 jvmti_env->GetErrorName(error, &err);
57
58 env->ThrowNew(rt_exception.get(), err);
59
60 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
61 return true;
62}
63
Alex Light1e07ca62016-12-02 11:40:56 -080064
Alex Light6ac57502017-01-19 15:05:06 -080065template <bool is_redefine>
66static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
67 JNIEnv* env,
68 jint num_targets,
69 jclass* target,
70 jvmtiError res) {
Alex Light460d1b42017-01-10 15:37:17 +000071 std::stringstream err;
Alex Light460d1b42017-01-10 15:37:17 +000072 char* error = nullptr;
73 jvmti->GetErrorName(res, &error);
Alex Light6ac57502017-01-19 15:05:06 -080074 err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
Alex Light0e692732017-01-10 15:00:05 -080075 if (num_targets > 1) {
76 err << "es";
77 }
78 err << " <";
79 for (jint i = 0; i < num_targets; i++) {
80 char* signature = nullptr;
81 char* generic = nullptr;
82 jvmti->GetClassSignature(target[i], &signature, &generic);
83 if (i != 0) {
84 err << ", ";
85 }
86 err << signature;
87 jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
88 jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
89 }
90 err << "> due to " << error;
Alex Light460d1b42017-01-10 15:37:17 +000091 std::string message = err.str();
Alex Light460d1b42017-01-10 15:37:17 +000092 jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
93 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
94}
95
Alex Light6ac57502017-01-19 15:05:06 -080096namespace common_redefine {
97
98static void throwRedefinitionError(jvmtiEnv* jvmti,
99 JNIEnv* env,
100 jint num_targets,
101 jclass* target,
102 jvmtiError res) {
103 return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
104}
105
Alex Light0e692732017-01-10 15:00:05 -0800106static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
107 JNIEnv* env,
108 jint num_redefines,
109 jclass* targets,
110 jbyteArray* class_file_bytes,
111 jbyteArray* dex_file_bytes) {
112 std::vector<jvmtiClassDefinition> defs;
113 for (jint i = 0; i < num_redefines; i++) {
114 jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
115 jint len = static_cast<jint>(env->GetArrayLength(desired_array));
116 const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
117 env->GetByteArrayElements(desired_array, nullptr));
118 defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
Alex Light1e07ca62016-12-02 11:40:56 -0800119 }
Alex Light0e692732017-01-10 15:00:05 -0800120 jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
Alex Light1e07ca62016-12-02 11:40:56 -0800121 if (res != JVMTI_ERROR_NONE) {
Alex Light0e692732017-01-10 15:00:05 -0800122 throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
Alex Light1e07ca62016-12-02 11:40:56 -0800123 }
124}
125
Alex Light0e692732017-01-10 15:00:05 -0800126static void DoClassRedefine(jvmtiEnv* jvmti_env,
127 JNIEnv* env,
128 jclass target,
129 jbyteArray class_file_bytes,
130 jbyteArray dex_file_bytes) {
131 return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
132}
133
Alex Light1e07ca62016-12-02 11:40:56 -0800134// Magic JNI export that classes can use for redefining classes.
135// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
136extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* env,
137 jclass,
138 jclass target,
139 jbyteArray class_file_bytes,
140 jbyteArray dex_file_bytes) {
Alex Light0e692732017-01-10 15:00:05 -0800141 DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
142}
143
144// Magic JNI export that classes can use for redefining classes.
145// To use classes should declare this as a native function with signature
146// ([Ljava/lang/Class;[[B[[B)V
147extern "C" JNIEXPORT void JNICALL Java_Main_doCommonMultiClassRedefinition(
148 JNIEnv* env,
149 jclass,
150 jobjectArray targets,
151 jobjectArray class_file_bytes,
152 jobjectArray dex_file_bytes) {
153 std::vector<jclass> classes;
154 std::vector<jbyteArray> class_files;
155 std::vector<jbyteArray> dex_files;
156 jint len = env->GetArrayLength(targets);
157 if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
158 env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
159 "the three array arguments passed to this function have different lengths!");
160 return;
161 }
162 for (jint i = 0; i < len; i++) {
163 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
164 dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
165 class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
166 }
167 return DoMultiClassRedefine(jvmti_env,
168 env,
169 len,
170 classes.data(),
171 class_files.data(),
172 dex_files.data());
Alex Light1e07ca62016-12-02 11:40:56 -0800173}
174
Alex Light6ac57502017-01-19 15:05:06 -0800175// Get all capabilities except those related to retransformation.
176jint OnLoad(JavaVM* vm,
177 char* options ATTRIBUTE_UNUSED,
178 void* reserved ATTRIBUTE_UNUSED) {
179 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
180 printf("Unable to get jvmti env!\n");
181 return 1;
182 }
183 jvmtiCapabilities caps;
184 jvmti_env->GetPotentialCapabilities(&caps);
185 caps.can_retransform_classes = 0;
186 caps.can_retransform_any_class = 0;
187 jvmti_env->AddCapabilities(&caps);
188 return 0;
189}
190
191} // namespace common_redefine
192
193namespace common_retransform {
194
195struct CommonTransformationResult {
196 std::vector<unsigned char> class_bytes;
197 std::vector<unsigned char> dex_bytes;
198
199 CommonTransformationResult(size_t class_size, size_t dex_size)
200 : class_bytes(class_size), dex_bytes(dex_size) {}
201
202 CommonTransformationResult() = default;
203 CommonTransformationResult(CommonTransformationResult&&) = default;
204 CommonTransformationResult(CommonTransformationResult&) = default;
205};
206
207// Map from class name to transformation result.
208std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
209
210extern "C" JNIEXPORT void JNICALL Java_Main_addCommonTransformationResult(JNIEnv* env,
211 jclass,
212 jstring class_name,
213 jbyteArray class_array,
214 jbyteArray dex_array) {
215 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
216 std::string name_str(name_chrs);
217 env->ReleaseStringUTFChars(class_name, name_chrs);
218 CommonTransformationResult trans(env->GetArrayLength(class_array),
219 env->GetArrayLength(dex_array));
220 if (env->ExceptionOccurred()) {
221 return;
222 }
223 env->GetByteArrayRegion(class_array,
224 0,
225 env->GetArrayLength(class_array),
226 reinterpret_cast<jbyte*>(trans.class_bytes.data()));
227 if (env->ExceptionOccurred()) {
228 return;
229 }
230 env->GetByteArrayRegion(dex_array,
231 0,
232 env->GetArrayLength(dex_array),
233 reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
234 if (env->ExceptionOccurred()) {
235 return;
236 }
237 if (gTransformations.find(name_str) == gTransformations.end()) {
238 std::deque<CommonTransformationResult> list;
239 gTransformations[name_str] = std::move(list);
240 }
241 gTransformations[name_str].push_back(std::move(trans));
242}
243
244// The hook we are using.
245void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
246 JNIEnv* jni_env ATTRIBUTE_UNUSED,
247 jclass class_being_redefined ATTRIBUTE_UNUSED,
248 jobject loader ATTRIBUTE_UNUSED,
249 const char* name,
250 jobject protection_domain ATTRIBUTE_UNUSED,
251 jint class_data_len ATTRIBUTE_UNUSED,
252 const unsigned char* class_dat ATTRIBUTE_UNUSED,
253 jint* new_class_data_len,
254 unsigned char** new_class_data) {
255 std::string name_str(name);
Alex Lighta7e38d82017-01-19 14:57:28 -0800256 if (gTransformations.find(name_str) != gTransformations.end() &&
257 gTransformations[name_str].size() > 0) {
Alex Light6ac57502017-01-19 15:05:06 -0800258 CommonTransformationResult& res = gTransformations[name_str][0];
259 const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
260 unsigned char* new_data;
Alex Lighta7e38d82017-01-19 14:57:28 -0800261 CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
Alex Light6ac57502017-01-19 15:05:06 -0800262 memcpy(new_data, desired_array.data(), desired_array.size());
263 *new_class_data = new_data;
264 *new_class_data_len = desired_array.size();
265 gTransformations[name_str].pop_front();
266 }
267}
268
269extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env,
270 jclass,
271 jboolean enable) {
272 jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
273 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
274 nullptr);
275 if (res != JVMTI_ERROR_NONE) {
276 JvmtiErrorToException(env, res);
277 }
278}
279
280static void throwRetransformationError(jvmtiEnv* jvmti,
281 JNIEnv* env,
282 jint num_targets,
283 jclass* targets,
284 jvmtiError res) {
285 return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
286}
287
288static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
289 std::vector<jclass> classes;
290 jint len = env->GetArrayLength(targets);
291 for (jint i = 0; i < len; i++) {
292 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
293 }
294 jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
295 if (res != JVMTI_ERROR_NONE) {
296 throwRetransformationError(jvmti_env, env, len, classes.data(), res);
297 }
298}
299
300// TODO Write something useful.
301extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env,
302 jclass,
303 jobjectArray targets) {
304 DoClassRetransformation(jvmti_env, env, targets);
305}
306
307// Get all capabilities except those related to retransformation.
Alex Light1e07ca62016-12-02 11:40:56 -0800308jint OnLoad(JavaVM* vm,
309 char* options ATTRIBUTE_UNUSED,
310 void* reserved ATTRIBUTE_UNUSED) {
311 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
312 printf("Unable to get jvmti env!\n");
313 return 1;
314 }
315 SetAllCapabilities(jvmti_env);
Alex Light6ac57502017-01-19 15:05:06 -0800316 jvmtiEventCallbacks cb;
317 memset(&cb, 0, sizeof(cb));
318 cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
319 if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
320 printf("Unable to set class file load hook cb!\n");
321 return 1;
322 }
Alex Light1e07ca62016-12-02 11:40:56 -0800323 return 0;
324}
325
Alex Light6ac57502017-01-19 15:05:06 -0800326} // namespace common_retransform
Alex Light1e07ca62016-12-02 11:40:56 -0800327
328} // namespace art