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