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 Test1959RedefineObjectInstrument {
38 
39 // Just pull it out of the dex file but don't bother changing anything.
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)40 static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env,
41                                        JNIEnv* env,
42                                        jclass class_being_redefined ATTRIBUTE_UNUSED,
43                                        jobject loader ATTRIBUTE_UNUSED,
44                                        const char* name,
45                                        jobject protection_domain ATTRIBUTE_UNUSED,
46                                        jint class_data_len,
47                                        const unsigned char* class_data,
48                                        jint* new_class_data_len,
49                                        unsigned char** new_class_data) {
50   if (strcmp(name, "java/lang/Object") != 0) {
51     return;
52   }
53 
54   dex::Reader reader(class_data, class_data_len);
55   dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;");
56   if (class_index == dex::kNoIndex) {
57     env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
58                   "Failed to find object in dex file!");
59     return;
60   }
61 
62   reader.CreateClassIr(class_index);
63   auto dex_ir = reader.GetIr();
64   dex::Writer writer(dex_ir);
65 
66   class JvmtiAllocator : public dex::Writer::Allocator {
67    public:
68     explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {}
69 
70     void* Allocate(size_t size) override {
71       unsigned char* res = nullptr;
72       jvmti_->Allocate(size, &res);
73       return res;
74     }
75 
76     void Free(void* ptr) override {
77       jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
78     }
79 
80    private:
81     jvmtiEnv* jvmti_;
82   };
83   JvmtiAllocator allocator(jvmti_env);
84   size_t new_size;
85   *new_class_data = writer.CreateImage(&allocator, &new_size);
86   if (new_size > std::numeric_limits<jint>::max()) {
87     *new_class_data = nullptr;
88     env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
89                   "transform result is too large!");
90     return;
91   }
92   *new_class_data_len = static_cast<jint>(new_size);
93 }
94 
Java_Main_forceRedefine(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass obj_class,jthread thr)95 extern "C" JNIEXPORT void JNICALL Java_Main_forceRedefine(JNIEnv* env,
96                                                           jclass klass ATTRIBUTE_UNUSED,
97                                                           jclass obj_class,
98                                                           jthread thr) {
99   if (IsJVM()) {
100     // RI so don't do anything.
101     return;
102   }
103   jvmtiCapabilities caps {.can_retransform_classes = 1};
104   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
105     return;
106   }
107   jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook };
108   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
109     return;
110   }
111   if (JvmtiErrorToException(env,
112                             jvmti_env,
113                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
114                                                                 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
115                                                                 thr))) {
116     return;
117   }
118   if (JvmtiErrorToException(env,
119                             jvmti_env,
120                             jvmti_env->RetransformClasses(1, &obj_class))) {
121     return;
122   }
123   if (JvmtiErrorToException(env,
124                             jvmti_env,
125                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
126                                                                 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
127                                                                 thr))) {
128     return;
129   }
130 }
131 
132 }  // namespace Test1959RedefineObjectInstrument
133 }  // namespace art
134 
135