1 /*
2 * Copyright (C) 2018 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 <limits>
18 #include <memory>
19
20 #include "jni.h"
21 #include "jvmti.h"
22
23 // Test infrastructure
24 #include "jvmti_helper.h"
25 #include "test_env.h"
26
27 // Slicer's headers have code that triggers these warnings. b/65298177
28 #pragma clang diagnostic push
29 #pragma clang diagnostic ignored "-Wsign-compare"
30 #pragma clang diagnostic ignored "-Wunused-parameter"
31 #include "slicer/instrumentation.h"
32 #include "slicer/reader.h"
33 #include "slicer/writer.h"
34 #pragma clang diagnostic pop
35
36 namespace art {
37 namespace Test980RedefineObject {
38
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)39 static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env,
40 JNIEnv* env,
41 jclass class_being_redefined ATTRIBUTE_UNUSED,
42 jobject loader ATTRIBUTE_UNUSED,
43 const char* name,
44 jobject protection_domain ATTRIBUTE_UNUSED,
45 jint class_data_len,
46 const unsigned char* class_data,
47 jint* new_class_data_len,
48 unsigned char** new_class_data) {
49 if (strcmp(name, "java/lang/Object") != 0) {
50 return;
51 }
52
53 dex::Reader reader(class_data, class_data_len);
54 dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;");
55 if (class_index == dex::kNoIndex) {
56 env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
57 "Failed to find object in dex file!");
58 return;
59 }
60
61 reader.CreateClassIr(class_index);
62 auto dex_ir = reader.GetIr();
63
64 slicer::MethodInstrumenter mi(dex_ir);
65 mi.AddTransformation<slicer::EntryHook>(ir::MethodId("Lart/test/TestWatcher;",
66 "NotifyConstructed"),
67 /*this_as_object*/ true);
68 if (!mi.InstrumentMethod(ir::MethodId("Ljava/lang/Object;",
69 "<init>",
70 "()V"))) {
71 env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
72 "Failed to find Object;-><init>()V in dex file!");
73 return;
74 }
75
76
77 dex::Writer writer(dex_ir);
78
79 class JvmtiAllocator : public dex::Writer::Allocator {
80 public:
81 explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {}
82
83 void* Allocate(size_t size) override {
84 unsigned char* res = nullptr;
85 jvmti_->Allocate(size, &res);
86 return res;
87 }
88
89 void Free(void* ptr) override {
90 jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
91 }
92
93 private:
94 jvmtiEnv* jvmti_;
95 };
96 JvmtiAllocator allocator(jvmti_env);
97 size_t new_size;
98 *new_class_data = writer.CreateImage(&allocator, &new_size);
99 if (new_size > std::numeric_limits<jint>::max()) {
100 *new_class_data = nullptr;
101 env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
102 "transform result is too large!");
103 return;
104 }
105 *new_class_data_len = static_cast<jint>(new_size);
106 }
107
Java_Main_addMemoryTrackingCall(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass obj_class,jthread thr)108 extern "C" JNIEXPORT void JNICALL Java_Main_addMemoryTrackingCall(JNIEnv* env,
109 jclass klass ATTRIBUTE_UNUSED,
110 jclass obj_class,
111 jthread thr) {
112 jvmtiCapabilities caps {.can_retransform_classes = 1};
113 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
114 return;
115 }
116 jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook };
117 if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
118 return;
119 }
120 if (JvmtiErrorToException(env,
121 jvmti_env,
122 jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
123 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
124 thr))) {
125 return;
126 }
127 if (JvmtiErrorToException(env,
128 jvmti_env,
129 jvmti_env->RetransformClasses(1, &obj_class))) {
130 return;
131 }
132 if (JvmtiErrorToException(env,
133 jvmti_env,
134 jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
135 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
136 thr))) {
137 return;
138 }
139 }
140
141 } // namespace Test980RedefineObject
142 } // namespace art
143
144