blob: 2c6d3eda008f1deaf5f3a927b41af2fee2c98613 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ti-agent/common_helper.h"
#include <stdio.h>
#include <sstream>
#include "art_method.h"
#include "jni.h"
#include "openjdkjvmti/jvmti.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "ti-agent/common_load.h"
#include "utils.h"
namespace art {
bool RuntimeIsJVM;
bool IsJVM() {
return RuntimeIsJVM;
}
void SetAllCapabilities(jvmtiEnv* env) {
jvmtiCapabilities caps;
env->GetPotentialCapabilities(&caps);
env->AddCapabilities(&caps);
}
bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
if (error == JVMTI_ERROR_NONE) {
return false;
}
ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
if (rt_exception.get() == nullptr) {
// CNFE should be pending.
return true;
}
char* err;
jvmti_env->GetErrorName(error, &err);
env->ThrowNew(rt_exception.get(), err);
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
return true;
}
namespace common_redefine {
static void throwRedefinitionError(jvmtiEnv* jvmti,
JNIEnv* env,
jint num_targets,
jclass* target,
jvmtiError res) {
std::stringstream err;
char* error = nullptr;
jvmti->GetErrorName(res, &error);
err << "Failed to redefine class";
if (num_targets > 1) {
err << "es";
}
err << " <";
for (jint i = 0; i < num_targets; i++) {
char* signature = nullptr;
char* generic = nullptr;
jvmti->GetClassSignature(target[i], &signature, &generic);
if (i != 0) {
err << ", ";
}
err << signature;
jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
}
err << "> due to " << error;
std::string message = err.str();
jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
}
static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
JNIEnv* env,
jint num_redefines,
jclass* targets,
jbyteArray* class_file_bytes,
jbyteArray* dex_file_bytes) {
std::vector<jvmtiClassDefinition> defs;
for (jint i = 0; i < num_redefines; i++) {
jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
jint len = static_cast<jint>(env->GetArrayLength(desired_array));
const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
env->GetByteArrayElements(desired_array, nullptr));
defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
}
jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
if (res != JVMTI_ERROR_NONE) {
throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
}
}
static void DoClassRedefine(jvmtiEnv* jvmti_env,
JNIEnv* env,
jclass target,
jbyteArray class_file_bytes,
jbyteArray dex_file_bytes) {
return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
}
// Magic JNI export that classes can use for redefining classes.
// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* env,
jclass,
jclass target,
jbyteArray class_file_bytes,
jbyteArray dex_file_bytes) {
DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
}
// Magic JNI export that classes can use for redefining classes.
// To use classes should declare this as a native function with signature
// ([Ljava/lang/Class;[[B[[B)V
extern "C" JNIEXPORT void JNICALL Java_Main_doCommonMultiClassRedefinition(
JNIEnv* env,
jclass,
jobjectArray targets,
jobjectArray class_file_bytes,
jobjectArray dex_file_bytes) {
std::vector<jclass> classes;
std::vector<jbyteArray> class_files;
std::vector<jbyteArray> dex_files;
jint len = env->GetArrayLength(targets);
if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
"the three array arguments passed to this function have different lengths!");
return;
}
for (jint i = 0; i < len; i++) {
classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
}
return DoMultiClassRedefine(jvmti_env,
env,
len,
classes.data(),
class_files.data(),
dex_files.data());
}
// Don't do anything
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
printf("Unable to get jvmti env!\n");
return 1;
}
SetAllCapabilities(jvmti_env);
return 0;
}
} // namespace common_redefine
} // namespace art