blob: 80e17973696a6498dab5ee706cf3e0e5746bce2a [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
304// TODO Write something useful.
305extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env,
306 jclass,
307 jobjectArray targets) {
308 DoClassRetransformation(jvmti_env, env, targets);
309}
310
311// Get all capabilities except those related to retransformation.
Alex Light1e07ca62016-12-02 11:40:56 -0800312jint OnLoad(JavaVM* vm,
313 char* options ATTRIBUTE_UNUSED,
314 void* reserved ATTRIBUTE_UNUSED) {
315 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
316 printf("Unable to get jvmti env!\n");
317 return 1;
318 }
319 SetAllCapabilities(jvmti_env);
Alex Light6ac57502017-01-19 15:05:06 -0800320 jvmtiEventCallbacks cb;
321 memset(&cb, 0, sizeof(cb));
322 cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
323 if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
324 printf("Unable to set class file load hook cb!\n");
325 return 1;
326 }
Alex Light1e07ca62016-12-02 11:40:56 -0800327 return 0;
328}
329
Alex Light6ac57502017-01-19 15:05:06 -0800330} // namespace common_retransform
Alex Light1e07ca62016-12-02 11:40:56 -0800331
Andreas Gampe53ae7802017-01-19 21:13:46 -0800332static void BindMethod(jvmtiEnv* jenv,
333 JNIEnv* env,
334 jclass klass,
335 jmethodID method) {
336 char* name;
337 char* signature;
338 jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr);
339 if (name_result != JVMTI_ERROR_NONE) {
340 LOG(FATAL) << "Could not get methods";
341 }
342
343 ArtMethod* m = jni::DecodeArtMethod(method);
344
345 std::string names[2];
346 {
347 ScopedObjectAccess soa(Thread::Current());
348 names[0] = m->JniShortName();
349 names[1] = m->JniLongName();
350 }
351 for (const std::string& mangled_name : names) {
Andreas Gampe9623ca62017-01-20 19:49:11 -0800352 void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
Andreas Gampe53ae7802017-01-19 21:13:46 -0800353 if (sym == nullptr) {
354 continue;
355 }
356
357 JNINativeMethod native_method;
358 native_method.fnPtr = sym;
359 native_method.name = name;
360 native_method.signature = signature;
361
362 env->RegisterNatives(klass, &native_method, 1);
363
364 jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
365 jenv->Deallocate(reinterpret_cast<unsigned char*>(signature));
366 return;
367 }
368
369 LOG(FATAL) << "Could not find " << names[0];
370}
371
372static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) {
373 // Find the system classloader.
374 ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
375 if (cl_klass.get() == nullptr) {
376 return nullptr;
377 }
378 jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
379 "getSystemClassLoader",
380 "()Ljava/lang/ClassLoader;");
381 if (getsystemclassloader_method == nullptr) {
382 return nullptr;
383 }
384 ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(),
385 getsystemclassloader_method));
386 if (cl.get() == nullptr) {
387 return nullptr;
388 }
389
390 // Create a String of the name.
391 std::string descriptor = android::base::StringPrintf("L%s;", class_name);
392 std::string dot_name = DescriptorToDot(descriptor.c_str());
393 ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
394
395 // Call Class.forName with it.
396 ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
397 if (c_klass.get() == nullptr) {
398 return nullptr;
399 }
400 jmethodID forname_method = env->GetStaticMethodID(
401 c_klass.get(),
402 "forName",
403 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
404 if (forname_method == nullptr) {
405 return nullptr;
406 }
407
408 return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
409 forname_method,
410 name_str.get(),
411 JNI_FALSE,
412 cl.get()));
413}
414
415void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) {
416 // Use JNI to load the class.
417 ScopedLocalRef<jclass> klass(env, env->FindClass(class_name));
418 if (klass.get() == nullptr) {
419 // We may be called with the wrong classloader. Try explicitly using the system classloader.
420 env->ExceptionClear();
421 klass.reset(FindClassWithSystemClassLoader(env, class_name));
422 if (klass.get() == nullptr) {
423 LOG(FATAL) << "Could not load " << class_name;
424 }
425 }
426
427 // Use JVMTI to get the methods.
428 jint method_count;
429 jmethodID* methods;
430 jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods);
431 if (methods_result != JVMTI_ERROR_NONE) {
432 LOG(FATAL) << "Could not get methods";
433 }
434
435 // Check each method.
436 for (jint i = 0; i < method_count; ++i) {
437 jint modifiers;
438 jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers);
439 if (mod_result != JVMTI_ERROR_NONE) {
440 LOG(FATAL) << "Could not get methods";
441 }
442 constexpr jint kNative = static_cast<jint>(kAccNative);
443 if ((modifiers & kNative) != 0) {
444 BindMethod(jenv, env, klass.get(), methods[i]);
445 }
446 }
447
448 jenv->Deallocate(reinterpret_cast<unsigned char*>(methods));
449}
450
Alex Light1e07ca62016-12-02 11:40:56 -0800451} // namespace art