/* * Copyright (C) 2018 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 #include #include "jni.h" #include "jvmti.h" // Test infrastructure #include "jvmti_helper.h" #include "test_env.h" // Slicer's headers have code that triggers these warnings. b/65298177 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-compare" #pragma clang diagnostic ignored "-Wunused-parameter" #include "slicer/instrumentation.h" #include "slicer/reader.h" #include "slicer/writer.h" #pragma clang diagnostic pop namespace art { namespace Test980RedefineObject { static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env, JNIEnv* env, jclass class_being_redefined ATTRIBUTE_UNUSED, jobject loader ATTRIBUTE_UNUSED, const char* name, jobject protection_domain ATTRIBUTE_UNUSED, jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) { if (strcmp(name, "java/lang/Object") != 0) { return; } dex::Reader reader(class_data, class_data_len); dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;"); if (class_index == dex::kNoIndex) { env->ThrowNew(env->FindClass("java/lang/RuntimeException"), "Failed to find object in dex file!"); return; } reader.CreateClassIr(class_index); auto dex_ir = reader.GetIr(); slicer::MethodInstrumenter mi(dex_ir); mi.AddTransformation(ir::MethodId("Lart/test/TestWatcher;", "NotifyConstructed"), /*this_as_object*/ true); if (!mi.InstrumentMethod(ir::MethodId("Ljava/lang/Object;", "", "()V"))) { env->ThrowNew(env->FindClass("java/lang/RuntimeException"), "Failed to find Object;->()V in dex file!"); return; } dex::Writer writer(dex_ir); class JvmtiAllocator : public dex::Writer::Allocator { public: explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {} void* Allocate(size_t size) override { unsigned char* res = nullptr; jvmti_->Allocate(size, &res); return res; } void Free(void* ptr) override { jvmti_->Deallocate(reinterpret_cast(ptr)); } private: jvmtiEnv* jvmti_; }; JvmtiAllocator allocator(jvmti_env); size_t new_size; *new_class_data = writer.CreateImage(&allocator, &new_size); if (new_size > std::numeric_limits::max()) { *new_class_data = nullptr; env->ThrowNew(env->FindClass("java/lang/RuntimeException"), "transform result is too large!"); return; } *new_class_data_len = static_cast(new_size); } extern "C" JNIEXPORT void JNICALL Java_Main_addMemoryTrackingCall(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass obj_class, jthread thr) { jvmtiCapabilities caps {.can_retransform_classes = 1}; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { return; } jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook }; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { return; } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, thr))) { return; } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RetransformClasses(1, &obj_class))) { return; } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, thr))) { return; } } } // namespace Test980RedefineObject } // namespace art