blob: ed82bb04cf06011ad9f7ecaf466962694985dc47 [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
Andreas Gampe53ae7802017-01-19 21:13:46 -080019#include <dlfcn.h>
Alex Light1e07ca62016-12-02 11:40:56 -080020#include <stdio.h>
Alex Light460d1b42017-01-10 15:37:17 +000021#include <sstream>
Alex Light6ac57502017-01-19 15:05:06 -080022#include <deque>
Alex Light1e07ca62016-12-02 11:40:56 -080023
Andreas Gampe53ae7802017-01-19 21:13:46 -080024#include "android-base/stringprintf.h"
Alex Lightdba61482016-12-21 08:20:29 -080025#include "art_method.h"
Alex Light1e07ca62016-12-02 11:40:56 -080026#include "jni.h"
Andreas Gampe53ae7802017-01-19 21:13:46 -080027#include "jni_internal.h"
Alex Light1e07ca62016-12-02 11:40:56 -080028#include "openjdkjvmti/jvmti.h"
Alex Lightdba61482016-12-21 08:20:29 -080029#include "scoped_thread_state_change-inl.h"
Andreas Gampe53ae7802017-01-19 21:13:46 -080030#include "ScopedLocalRef.h"
Alex Lightdba61482016-12-21 08:20:29 -080031#include "stack.h"
Alex Light1e07ca62016-12-02 11:40:56 -080032#include "ti-agent/common_load.h"
33#include "utils.h"
34
35namespace art {
36bool RuntimeIsJVM;
37
38bool IsJVM() {
39 return RuntimeIsJVM;
40}
41
42void SetAllCapabilities(jvmtiEnv* env) {
43 jvmtiCapabilities caps;
44 env->GetPotentialCapabilities(&caps);
45 env->AddCapabilities(&caps);
46}
47
Andreas Gampe1bdaf732017-01-09 19:21:06 -080048bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
49 if (error == JVMTI_ERROR_NONE) {
50 return false;
51 }
52
53 ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
54 if (rt_exception.get() == nullptr) {
55 // CNFE should be pending.
56 return true;
57 }
58
59 char* err;
60 jvmti_env->GetErrorName(error, &err);
61
62 env->ThrowNew(rt_exception.get(), err);
63
64 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
65 return true;
66}
67
Alex Light1e07ca62016-12-02 11:40:56 -080068
Alex Light6ac57502017-01-19 15:05:06 -080069template <bool is_redefine>
70static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
71 JNIEnv* env,
72 jint num_targets,
73 jclass* target,
74 jvmtiError res) {
Alex Light460d1b42017-01-10 15:37:17 +000075 std::stringstream err;
Alex Light460d1b42017-01-10 15:37:17 +000076 char* error = nullptr;
77 jvmti->GetErrorName(res, &error);
Alex Light6ac57502017-01-19 15:05:06 -080078 err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
Alex Light0e692732017-01-10 15:00:05 -080079 if (num_targets > 1) {
80 err << "es";
81 }
82 err << " <";
83 for (jint i = 0; i < num_targets; i++) {
84 char* signature = nullptr;
85 char* generic = nullptr;
86 jvmti->GetClassSignature(target[i], &signature, &generic);
87 if (i != 0) {
88 err << ", ";
89 }
90 err << signature;
91 jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
92 jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
93 }
94 err << "> due to " << error;
Alex Light460d1b42017-01-10 15:37:17 +000095 std::string message = err.str();
Alex Light460d1b42017-01-10 15:37:17 +000096 jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
97 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
98}
99
Alex Light6ac57502017-01-19 15:05:06 -0800100namespace common_redefine {
101
102static void throwRedefinitionError(jvmtiEnv* jvmti,
103 JNIEnv* env,
104 jint num_targets,
105 jclass* target,
106 jvmtiError res) {
107 return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
108}
109
Alex Light0e692732017-01-10 15:00:05 -0800110static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
111 JNIEnv* env,
112 jint num_redefines,
113 jclass* targets,
114 jbyteArray* class_file_bytes,
115 jbyteArray* dex_file_bytes) {
116 std::vector<jvmtiClassDefinition> defs;
117 for (jint i = 0; i < num_redefines; i++) {
118 jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
119 jint len = static_cast<jint>(env->GetArrayLength(desired_array));
120 const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
121 env->GetByteArrayElements(desired_array, nullptr));
122 defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
Alex Light1e07ca62016-12-02 11:40:56 -0800123 }
Alex Light0e692732017-01-10 15:00:05 -0800124 jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
Alex Light1e07ca62016-12-02 11:40:56 -0800125 if (res != JVMTI_ERROR_NONE) {
Alex Light0e692732017-01-10 15:00:05 -0800126 throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
Alex Light1e07ca62016-12-02 11:40:56 -0800127 }
128}
129
Alex Light0e692732017-01-10 15:00:05 -0800130static void DoClassRedefine(jvmtiEnv* jvmti_env,
131 JNIEnv* env,
132 jclass target,
133 jbyteArray class_file_bytes,
134 jbyteArray dex_file_bytes) {
135 return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
136}
137
Alex Light1e07ca62016-12-02 11:40:56 -0800138// Magic JNI export that classes can use for redefining classes.
139// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
140extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* env,
141 jclass,
142 jclass target,
143 jbyteArray class_file_bytes,
144 jbyteArray dex_file_bytes) {
Alex Light0e692732017-01-10 15:00:05 -0800145 DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
146}
147
148// Magic JNI export that classes can use for redefining classes.
149// To use classes should declare this as a native function with signature
150// ([Ljava/lang/Class;[[B[[B)V
151extern "C" JNIEXPORT void JNICALL Java_Main_doCommonMultiClassRedefinition(
152 JNIEnv* env,
153 jclass,
154 jobjectArray targets,
155 jobjectArray class_file_bytes,
156 jobjectArray dex_file_bytes) {
157 std::vector<jclass> classes;
158 std::vector<jbyteArray> class_files;
159 std::vector<jbyteArray> dex_files;
160 jint len = env->GetArrayLength(targets);
161 if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
162 env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
163 "the three array arguments passed to this function have different lengths!");
164 return;
165 }
166 for (jint i = 0; i < len; i++) {
167 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
168 dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
169 class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
170 }
171 return DoMultiClassRedefine(jvmti_env,
172 env,
173 len,
174 classes.data(),
175 class_files.data(),
176 dex_files.data());
Alex Light1e07ca62016-12-02 11:40:56 -0800177}
178
Alex Light6ac57502017-01-19 15:05:06 -0800179// Get all capabilities except those related to retransformation.
180jint OnLoad(JavaVM* vm,
181 char* options ATTRIBUTE_UNUSED,
182 void* reserved ATTRIBUTE_UNUSED) {
183 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
184 printf("Unable to get jvmti env!\n");
185 return 1;
186 }
187 jvmtiCapabilities caps;
188 jvmti_env->GetPotentialCapabilities(&caps);
189 caps.can_retransform_classes = 0;
190 caps.can_retransform_any_class = 0;
191 jvmti_env->AddCapabilities(&caps);
192 return 0;
193}
194
195} // namespace common_redefine
196
197namespace common_retransform {
198
199struct CommonTransformationResult {
200 std::vector<unsigned char> class_bytes;
201 std::vector<unsigned char> dex_bytes;
202
203 CommonTransformationResult(size_t class_size, size_t dex_size)
204 : class_bytes(class_size), dex_bytes(dex_size) {}
205
206 CommonTransformationResult() = default;
207 CommonTransformationResult(CommonTransformationResult&&) = default;
208 CommonTransformationResult(CommonTransformationResult&) = default;
209};
210
211// Map from class name to transformation result.
212std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
213
214extern "C" JNIEXPORT void JNICALL Java_Main_addCommonTransformationResult(JNIEnv* env,
215 jclass,
216 jstring class_name,
217 jbyteArray class_array,
218 jbyteArray dex_array) {
219 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
220 std::string name_str(name_chrs);
221 env->ReleaseStringUTFChars(class_name, name_chrs);
222 CommonTransformationResult trans(env->GetArrayLength(class_array),
223 env->GetArrayLength(dex_array));
224 if (env->ExceptionOccurred()) {
225 return;
226 }
227 env->GetByteArrayRegion(class_array,
228 0,
229 env->GetArrayLength(class_array),
230 reinterpret_cast<jbyte*>(trans.class_bytes.data()));
231 if (env->ExceptionOccurred()) {
232 return;
233 }
234 env->GetByteArrayRegion(dex_array,
235 0,
236 env->GetArrayLength(dex_array),
237 reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
238 if (env->ExceptionOccurred()) {
239 return;
240 }
241 if (gTransformations.find(name_str) == gTransformations.end()) {
242 std::deque<CommonTransformationResult> list;
243 gTransformations[name_str] = std::move(list);
244 }
245 gTransformations[name_str].push_back(std::move(trans));
246}
247
248// The hook we are using.
249void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
250 JNIEnv* jni_env ATTRIBUTE_UNUSED,
251 jclass class_being_redefined ATTRIBUTE_UNUSED,
252 jobject loader ATTRIBUTE_UNUSED,
253 const char* name,
254 jobject protection_domain ATTRIBUTE_UNUSED,
255 jint class_data_len ATTRIBUTE_UNUSED,
256 const unsigned char* class_dat ATTRIBUTE_UNUSED,
257 jint* new_class_data_len,
258 unsigned char** new_class_data) {
259 std::string name_str(name);
Alex Lighta7e38d82017-01-19 14:57:28 -0800260 if (gTransformations.find(name_str) != gTransformations.end() &&
261 gTransformations[name_str].size() > 0) {
Alex Light6ac57502017-01-19 15:05:06 -0800262 CommonTransformationResult& res = gTransformations[name_str][0];
263 const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
264 unsigned char* new_data;
Alex Lighta7e38d82017-01-19 14:57:28 -0800265 CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
Alex Light6ac57502017-01-19 15:05:06 -0800266 memcpy(new_data, desired_array.data(), desired_array.size());
267 *new_class_data = new_data;
268 *new_class_data_len = desired_array.size();
269 gTransformations[name_str].pop_front();
270 }
271}
272
273extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env,
274 jclass,
275 jboolean enable) {
276 jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
277 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
278 nullptr);
279 if (res != JVMTI_ERROR_NONE) {
280 JvmtiErrorToException(env, res);
281 }
282}
283
284static void throwRetransformationError(jvmtiEnv* jvmti,
285 JNIEnv* env,
286 jint num_targets,
287 jclass* targets,
288 jvmtiError res) {
289 return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
290}
291
292static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
293 std::vector<jclass> classes;
294 jint len = env->GetArrayLength(targets);
295 for (jint i = 0; i < len; i++) {
296 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
297 }
298 jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
299 if (res != JVMTI_ERROR_NONE) {
300 throwRetransformationError(jvmti_env, env, len, classes.data(), res);
301 }
302}
303
Alex Light6ac57502017-01-19 15:05:06 -0800304extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env,
305 jclass,
306 jobjectArray targets) {
Alex Light9db679d2017-01-25 15:28:04 -0800307 jvmtiCapabilities caps;
308 jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
309 if (caps_err != JVMTI_ERROR_NONE) {
310 env->ThrowNew(env->FindClass("java/lang/Exception"),
311 "Unable to get current jvmtiEnv capabilities");
312 return;
313 }
314
315 // Allocate a new environment if we don't have the can_retransform_classes capability needed to
316 // call the RetransformClasses function.
317 jvmtiEnv* real_env = nullptr;
318 if (caps.can_retransform_classes != 1) {
319 JavaVM* vm = nullptr;
320 if (env->GetJavaVM(&vm) != 0 ||
321 vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
322 env->ThrowNew(env->FindClass("java/lang/Exception"),
323 "Unable to create temporary jvmtiEnv for RetransformClasses call.");
324 return;
325 }
326 SetAllCapabilities(real_env);
327 } else {
328 real_env = jvmti_env;
329 }
330 DoClassRetransformation(real_env, env, targets);
331 if (caps.can_retransform_classes != 1) {
332 real_env->DisposeEnvironment();
333 }
Alex Light6ac57502017-01-19 15:05:06 -0800334}
335
336// Get all capabilities except those related to retransformation.
Alex Light1e07ca62016-12-02 11:40:56 -0800337jint OnLoad(JavaVM* vm,
338 char* options ATTRIBUTE_UNUSED,
339 void* reserved ATTRIBUTE_UNUSED) {
340 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
341 printf("Unable to get jvmti env!\n");
342 return 1;
343 }
344 SetAllCapabilities(jvmti_env);
Alex Light6ac57502017-01-19 15:05:06 -0800345 jvmtiEventCallbacks cb;
346 memset(&cb, 0, sizeof(cb));
347 cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
348 if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
349 printf("Unable to set class file load hook cb!\n");
350 return 1;
351 }
Alex Light1e07ca62016-12-02 11:40:56 -0800352 return 0;
353}
354
Alex Light6ac57502017-01-19 15:05:06 -0800355} // namespace common_retransform
Alex Light1e07ca62016-12-02 11:40:56 -0800356
Alex Light440b5d92017-01-24 15:32:25 -0800357namespace common_transform {
358
359using art::common_retransform::CommonClassFileLoadHookRetransformable;
360
361// Get all capabilities except those related to retransformation.
362jint OnLoad(JavaVM* vm,
363 char* options ATTRIBUTE_UNUSED,
364 void* reserved ATTRIBUTE_UNUSED) {
365 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
366 printf("Unable to get jvmti env!\n");
367 return 1;
368 }
369 // Don't set the retransform caps
370 jvmtiCapabilities caps;
371 jvmti_env->GetPotentialCapabilities(&caps);
372 caps.can_retransform_classes = 0;
373 caps.can_retransform_any_class = 0;
374 jvmti_env->AddCapabilities(&caps);
375
376 // Use the same callback as the retransform test.
377 jvmtiEventCallbacks cb;
378 memset(&cb, 0, sizeof(cb));
379 cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
380 if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
381 printf("Unable to set class file load hook cb!\n");
382 return 1;
383 }
384 return 0;
385}
386
387} // namespace common_transform
388
Andreas Gampe53ae7802017-01-19 21:13:46 -0800389static void BindMethod(jvmtiEnv* jenv,
390 JNIEnv* env,
391 jclass klass,
392 jmethodID method) {
393 char* name;
394 char* signature;
395 jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr);
396 if (name_result != JVMTI_ERROR_NONE) {
397 LOG(FATAL) << "Could not get methods";
398 }
399
Andreas Gampe53ae7802017-01-19 21:13:46 -0800400 std::string names[2];
Alex Light888a59e2017-01-25 11:41:41 -0800401 if (IsJVM()) {
402 // TODO Get the JNI long name
403 char* klass_name;
404 jvmtiError klass_result = jenv->GetClassSignature(klass, &klass_name, nullptr);
405 if (klass_result == JVMTI_ERROR_NONE) {
406 std::string name_str(name);
407 std::string klass_str(klass_name);
408 names[0] = GetJniShortName(klass_str, name_str);
409 jenv->Deallocate(reinterpret_cast<unsigned char*>(klass_name));
410 } else {
411 LOG(FATAL) << "Could not get class name!";
412 }
413 } else {
Andreas Gampe53ae7802017-01-19 21:13:46 -0800414 ScopedObjectAccess soa(Thread::Current());
Alex Light888a59e2017-01-25 11:41:41 -0800415 ArtMethod* m = jni::DecodeArtMethod(method);
Andreas Gampe53ae7802017-01-19 21:13:46 -0800416 names[0] = m->JniShortName();
417 names[1] = m->JniLongName();
418 }
419 for (const std::string& mangled_name : names) {
Alex Light888a59e2017-01-25 11:41:41 -0800420 if (mangled_name == "") {
421 continue;
422 }
Andreas Gampe9623ca62017-01-20 19:49:11 -0800423 void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
Andreas Gampe53ae7802017-01-19 21:13:46 -0800424 if (sym == nullptr) {
425 continue;
426 }
427
428 JNINativeMethod native_method;
429 native_method.fnPtr = sym;
430 native_method.name = name;
431 native_method.signature = signature;
432
433 env->RegisterNatives(klass, &native_method, 1);
434
435 jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
436 jenv->Deallocate(reinterpret_cast<unsigned char*>(signature));
437 return;
438 }
439
440 LOG(FATAL) << "Could not find " << names[0];
441}
442
443static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) {
444 // Find the system classloader.
445 ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
446 if (cl_klass.get() == nullptr) {
447 return nullptr;
448 }
449 jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
450 "getSystemClassLoader",
451 "()Ljava/lang/ClassLoader;");
452 if (getsystemclassloader_method == nullptr) {
453 return nullptr;
454 }
455 ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(),
456 getsystemclassloader_method));
457 if (cl.get() == nullptr) {
458 return nullptr;
459 }
460
461 // Create a String of the name.
462 std::string descriptor = android::base::StringPrintf("L%s;", class_name);
463 std::string dot_name = DescriptorToDot(descriptor.c_str());
464 ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
465
466 // Call Class.forName with it.
467 ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
468 if (c_klass.get() == nullptr) {
469 return nullptr;
470 }
471 jmethodID forname_method = env->GetStaticMethodID(
472 c_klass.get(),
473 "forName",
474 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
475 if (forname_method == nullptr) {
476 return nullptr;
477 }
478
479 return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
480 forname_method,
481 name_str.get(),
482 JNI_FALSE,
483 cl.get()));
484}
485
486void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) {
487 // Use JNI to load the class.
488 ScopedLocalRef<jclass> klass(env, env->FindClass(class_name));
489 if (klass.get() == nullptr) {
490 // We may be called with the wrong classloader. Try explicitly using the system classloader.
491 env->ExceptionClear();
492 klass.reset(FindClassWithSystemClassLoader(env, class_name));
493 if (klass.get() == nullptr) {
494 LOG(FATAL) << "Could not load " << class_name;
495 }
496 }
497
498 // Use JVMTI to get the methods.
499 jint method_count;
500 jmethodID* methods;
501 jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods);
502 if (methods_result != JVMTI_ERROR_NONE) {
503 LOG(FATAL) << "Could not get methods";
504 }
505
506 // Check each method.
507 for (jint i = 0; i < method_count; ++i) {
508 jint modifiers;
509 jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers);
510 if (mod_result != JVMTI_ERROR_NONE) {
511 LOG(FATAL) << "Could not get methods";
512 }
513 constexpr jint kNative = static_cast<jint>(kAccNative);
514 if ((modifiers & kNative) != 0) {
515 BindMethod(jenv, env, klass.get(), methods[i]);
516 }
517 }
518
519 jenv->Deallocate(reinterpret_cast<unsigned char*>(methods));
520}
521
Alex Light1e07ca62016-12-02 11:40:56 -0800522} // namespace art