xref: /aosp_15_r20/cts/hostsidetests/jvmti/base/jni/redefine.cpp (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker  *
4*b7c941bbSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker  *
8*b7c941bbSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker  *
10*b7c941bbSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker  * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker  */
16*b7c941bbSAndroid Build Coastguard Worker 
17*b7c941bbSAndroid Build Coastguard Worker #include <jni.h>
18*b7c941bbSAndroid Build Coastguard Worker 
19*b7c941bbSAndroid Build Coastguard Worker #include <stack>
20*b7c941bbSAndroid Build Coastguard Worker #include <string>
21*b7c941bbSAndroid Build Coastguard Worker #include <unordered_map>
22*b7c941bbSAndroid Build Coastguard Worker #include <vector>
23*b7c941bbSAndroid Build Coastguard Worker 
24*b7c941bbSAndroid Build Coastguard Worker #include "android-base/logging.h"
25*b7c941bbSAndroid Build Coastguard Worker #include "android-base/macros.h"
26*b7c941bbSAndroid Build Coastguard Worker #include "jni_binder.h"
27*b7c941bbSAndroid Build Coastguard Worker #include "jni_helper.h"
28*b7c941bbSAndroid Build Coastguard Worker #include "jvmti_helper.h"
29*b7c941bbSAndroid Build Coastguard Worker #include "jvmti.h"
30*b7c941bbSAndroid Build Coastguard Worker #include "scoped_primitive_array.h"
31*b7c941bbSAndroid Build Coastguard Worker #include "test_env.h"
32*b7c941bbSAndroid Build Coastguard Worker 
33*b7c941bbSAndroid Build Coastguard Worker namespace art {
34*b7c941bbSAndroid Build Coastguard Worker 
Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass target,jbyteArray dex_bytes)35*b7c941bbSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(
36*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass target, jbyteArray dex_bytes) {
37*b7c941bbSAndroid Build Coastguard Worker   jvmtiClassDefinition def;
38*b7c941bbSAndroid Build Coastguard Worker   def.klass = target;
39*b7c941bbSAndroid Build Coastguard Worker   def.class_byte_count = static_cast<jint>(env->GetArrayLength(dex_bytes));
40*b7c941bbSAndroid Build Coastguard Worker   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
41*b7c941bbSAndroid Build Coastguard Worker   jvmtiError res =jvmti_env->Allocate(def.class_byte_count,
42*b7c941bbSAndroid Build Coastguard Worker                                       const_cast<unsigned char**>(&def.class_bytes));
43*b7c941bbSAndroid Build Coastguard Worker   if (res != JVMTI_ERROR_NONE) {
44*b7c941bbSAndroid Build Coastguard Worker     return static_cast<jint>(res);
45*b7c941bbSAndroid Build Coastguard Worker   }
46*b7c941bbSAndroid Build Coastguard Worker   memcpy(const_cast<unsigned char*>(def.class_bytes), redef_bytes, def.class_byte_count);
47*b7c941bbSAndroid Build Coastguard Worker   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
48*b7c941bbSAndroid Build Coastguard Worker   // Do the redefinition.
49*b7c941bbSAndroid Build Coastguard Worker   res = jvmti_env->RedefineClasses(1, &def);
50*b7c941bbSAndroid Build Coastguard Worker   return static_cast<jint>(res);
51*b7c941bbSAndroid Build Coastguard Worker }
52*b7c941bbSAndroid Build Coastguard Worker 
Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jclass target)53*b7c941bbSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(
54*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jclass target) {
55*b7c941bbSAndroid Build Coastguard Worker   return jvmti_env->RetransformClasses(1, &target);
56*b7c941bbSAndroid Build Coastguard Worker }
57*b7c941bbSAndroid Build Coastguard Worker 
58*b7c941bbSAndroid Build Coastguard Worker class TransformationData {
59*b7c941bbSAndroid Build Coastguard Worker  public:
TransformationData()60*b7c941bbSAndroid Build Coastguard Worker   TransformationData() : redefinitions_(), should_pop_(false) {}
61*b7c941bbSAndroid Build Coastguard Worker 
SetPop(bool val)62*b7c941bbSAndroid Build Coastguard Worker   void SetPop(bool val) {
63*b7c941bbSAndroid Build Coastguard Worker     should_pop_ = val;
64*b7c941bbSAndroid Build Coastguard Worker   }
65*b7c941bbSAndroid Build Coastguard Worker 
ClearRedefinitions()66*b7c941bbSAndroid Build Coastguard Worker   void ClearRedefinitions() {
67*b7c941bbSAndroid Build Coastguard Worker     redefinitions_.clear();
68*b7c941bbSAndroid Build Coastguard Worker   }
69*b7c941bbSAndroid Build Coastguard Worker 
PushRedefinition(std::string name,std::vector<unsigned char> data)70*b7c941bbSAndroid Build Coastguard Worker   void PushRedefinition(std::string name, std::vector<unsigned char> data) {
71*b7c941bbSAndroid Build Coastguard Worker     if (redefinitions_.find(name) == redefinitions_.end()) {
72*b7c941bbSAndroid Build Coastguard Worker       std::stack<std::vector<unsigned char>> stack;
73*b7c941bbSAndroid Build Coastguard Worker       redefinitions_[name] = std::move(stack);
74*b7c941bbSAndroid Build Coastguard Worker     }
75*b7c941bbSAndroid Build Coastguard Worker     redefinitions_[name].push(std::move(data));
76*b7c941bbSAndroid Build Coastguard Worker   }
77*b7c941bbSAndroid Build Coastguard Worker 
RetrieveRedefinition(std::string name,std::vector<unsigned char> * data)78*b7c941bbSAndroid Build Coastguard Worker   bool RetrieveRedefinition(std::string name, /*out*/std::vector<unsigned char>* data) {
79*b7c941bbSAndroid Build Coastguard Worker     auto stack = redefinitions_.find(name);
80*b7c941bbSAndroid Build Coastguard Worker     if (stack == redefinitions_.end() || stack->second.empty()) {
81*b7c941bbSAndroid Build Coastguard Worker       return false;
82*b7c941bbSAndroid Build Coastguard Worker     } else {
83*b7c941bbSAndroid Build Coastguard Worker       *data = stack->second.top();
84*b7c941bbSAndroid Build Coastguard Worker       return true;
85*b7c941bbSAndroid Build Coastguard Worker     }
86*b7c941bbSAndroid Build Coastguard Worker   }
87*b7c941bbSAndroid Build Coastguard Worker 
PopRedefinition(std::string name)88*b7c941bbSAndroid Build Coastguard Worker   void PopRedefinition(std::string name) {
89*b7c941bbSAndroid Build Coastguard Worker     if (should_pop_) {
90*b7c941bbSAndroid Build Coastguard Worker       auto stack = redefinitions_.find(name);
91*b7c941bbSAndroid Build Coastguard Worker       if (stack == redefinitions_.end() || stack->second.empty()) {
92*b7c941bbSAndroid Build Coastguard Worker         return;
93*b7c941bbSAndroid Build Coastguard Worker       } else {
94*b7c941bbSAndroid Build Coastguard Worker         stack->second.pop();
95*b7c941bbSAndroid Build Coastguard Worker       }
96*b7c941bbSAndroid Build Coastguard Worker     }
97*b7c941bbSAndroid Build Coastguard Worker   }
98*b7c941bbSAndroid Build Coastguard Worker 
99*b7c941bbSAndroid Build Coastguard Worker  private:
100*b7c941bbSAndroid Build Coastguard Worker   std::unordered_map<std::string, std::stack<std::vector<unsigned char>>> redefinitions_;
101*b7c941bbSAndroid Build Coastguard Worker   bool should_pop_;
102*b7c941bbSAndroid Build Coastguard Worker };
103*b7c941bbSAndroid Build Coastguard Worker 
104*b7c941bbSAndroid Build Coastguard Worker static TransformationData data;
105*b7c941bbSAndroid Build Coastguard Worker 
106*b7c941bbSAndroid Build Coastguard Worker // The hook we are using.
CommonClassFileLoadHookRetransformable(jvmtiEnv * local_jvmti_env,JNIEnv * jni_env ATTRIBUTE_UNUSED,jclass class_being_redefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protection_domain ATTRIBUTE_UNUSED,jint class_data_len ATTRIBUTE_UNUSED,const unsigned char * class_dat ATTRIBUTE_UNUSED,jint * new_class_data_len,unsigned char ** new_class_data)107*b7c941bbSAndroid Build Coastguard Worker void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* local_jvmti_env,
108*b7c941bbSAndroid Build Coastguard Worker                                                     JNIEnv* jni_env ATTRIBUTE_UNUSED,
109*b7c941bbSAndroid Build Coastguard Worker                                                     jclass class_being_redefined ATTRIBUTE_UNUSED,
110*b7c941bbSAndroid Build Coastguard Worker                                                     jobject loader ATTRIBUTE_UNUSED,
111*b7c941bbSAndroid Build Coastguard Worker                                                     const char* name,
112*b7c941bbSAndroid Build Coastguard Worker                                                     jobject protection_domain ATTRIBUTE_UNUSED,
113*b7c941bbSAndroid Build Coastguard Worker                                                     jint class_data_len ATTRIBUTE_UNUSED,
114*b7c941bbSAndroid Build Coastguard Worker                                                     const unsigned char* class_dat ATTRIBUTE_UNUSED,
115*b7c941bbSAndroid Build Coastguard Worker                                                     jint* new_class_data_len,
116*b7c941bbSAndroid Build Coastguard Worker                                                     unsigned char** new_class_data) {
117*b7c941bbSAndroid Build Coastguard Worker   std::string name_str(name);
118*b7c941bbSAndroid Build Coastguard Worker   std::vector<unsigned char> dex_data;
119*b7c941bbSAndroid Build Coastguard Worker   if (data.RetrieveRedefinition(name_str, &dex_data)) {
120*b7c941bbSAndroid Build Coastguard Worker     unsigned char* jvmti_dex_data;
121*b7c941bbSAndroid Build Coastguard Worker     if (JVMTI_ERROR_NONE == local_jvmti_env->Allocate(dex_data.size(), &jvmti_dex_data)) {
122*b7c941bbSAndroid Build Coastguard Worker       memcpy(jvmti_dex_data, dex_data.data(), dex_data.size());
123*b7c941bbSAndroid Build Coastguard Worker       *new_class_data_len = dex_data.size();
124*b7c941bbSAndroid Build Coastguard Worker       *new_class_data = jvmti_dex_data;
125*b7c941bbSAndroid Build Coastguard Worker       data.PopRedefinition(name);
126*b7c941bbSAndroid Build Coastguard Worker     } else {
127*b7c941bbSAndroid Build Coastguard Worker       LOG(FATAL) << "Unable to allocate output buffer for " << name;
128*b7c941bbSAndroid Build Coastguard Worker     }
129*b7c941bbSAndroid Build Coastguard Worker   }
130*b7c941bbSAndroid Build Coastguard Worker }
131*b7c941bbSAndroid Build Coastguard Worker 
132*b7c941bbSAndroid Build Coastguard Worker extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable)133*b7c941bbSAndroid Build Coastguard Worker JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(
134*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
135*b7c941bbSAndroid Build Coastguard Worker   jvmtiEventCallbacks cb;
136*b7c941bbSAndroid Build Coastguard Worker   memset(&cb, 0, sizeof(cb));
137*b7c941bbSAndroid Build Coastguard Worker   cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
138*b7c941bbSAndroid Build Coastguard Worker   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
139*b7c941bbSAndroid Build Coastguard Worker     return;
140*b7c941bbSAndroid Build Coastguard Worker   }
141*b7c941bbSAndroid Build Coastguard Worker   JvmtiErrorToException(env,
142*b7c941bbSAndroid Build Coastguard Worker                         jvmti_env,
143*b7c941bbSAndroid Build Coastguard Worker                         jvmti_env->SetEventNotificationMode(
144*b7c941bbSAndroid Build Coastguard Worker                             enable == JNI_TRUE ? JVMTI_ENABLE : JVMTI_DISABLE,
145*b7c941bbSAndroid Build Coastguard Worker                             JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
146*b7c941bbSAndroid Build Coastguard Worker                             nullptr));
147*b7c941bbSAndroid Build Coastguard Worker   return;
148*b7c941bbSAndroid Build Coastguard Worker }
149*b7c941bbSAndroid Build Coastguard Worker 
150*b7c941bbSAndroid Build Coastguard Worker extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED)151*b7c941bbSAndroid Build Coastguard Worker JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(
152*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
153*b7c941bbSAndroid Build Coastguard Worker   data.ClearRedefinitions();
154*b7c941bbSAndroid Build Coastguard Worker }
155*b7c941bbSAndroid Build Coastguard Worker 
156*b7c941bbSAndroid Build Coastguard Worker extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jboolean enable)157*b7c941bbSAndroid Build Coastguard Worker JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(
158*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
159*b7c941bbSAndroid Build Coastguard Worker   data.SetPop(enable == JNI_TRUE ? true : false);
160*b7c941bbSAndroid Build Coastguard Worker }
161*b7c941bbSAndroid Build Coastguard Worker 
162*b7c941bbSAndroid Build Coastguard Worker extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jstring class_name,jbyteArray dex_bytes)163*b7c941bbSAndroid Build Coastguard Worker JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(
164*b7c941bbSAndroid Build Coastguard Worker     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring class_name, jbyteArray dex_bytes) {
165*b7c941bbSAndroid Build Coastguard Worker   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
166*b7c941bbSAndroid Build Coastguard Worker   std::string name_str(name_chrs);
167*b7c941bbSAndroid Build Coastguard Worker   env->ReleaseStringUTFChars(class_name, name_chrs);
168*b7c941bbSAndroid Build Coastguard Worker   std::vector<unsigned char> dex_data;
169*b7c941bbSAndroid Build Coastguard Worker   dex_data.resize(env->GetArrayLength(dex_bytes));
170*b7c941bbSAndroid Build Coastguard Worker   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
171*b7c941bbSAndroid Build Coastguard Worker   memcpy(dex_data.data(), redef_bytes, env->GetArrayLength(dex_bytes));
172*b7c941bbSAndroid Build Coastguard Worker   data.PushRedefinition(std::move(name_str), std::move(dex_data));
173*b7c941bbSAndroid Build Coastguard Worker   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
174*b7c941bbSAndroid Build Coastguard Worker }
175*b7c941bbSAndroid Build Coastguard Worker 
176*b7c941bbSAndroid Build Coastguard Worker static JNINativeMethod gMethods[] = {
177*b7c941bbSAndroid Build Coastguard Worker   { "redefineClass", "(Ljava/lang/Class;[B)I",
178*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass },
179*b7c941bbSAndroid Build Coastguard Worker 
180*b7c941bbSAndroid Build Coastguard Worker   { "retransformClass", "(Ljava/lang/Class;)I",
181*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass },
182*b7c941bbSAndroid Build Coastguard Worker 
183*b7c941bbSAndroid Build Coastguard Worker   { "setTransformationEvent", "(Z)V",
184*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent },
185*b7c941bbSAndroid Build Coastguard Worker 
186*b7c941bbSAndroid Build Coastguard Worker   { "clearTransformations", "()V",
187*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations },
188*b7c941bbSAndroid Build Coastguard Worker 
189*b7c941bbSAndroid Build Coastguard Worker   { "setPopTransformations", "(Z)V",
190*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations },
191*b7c941bbSAndroid Build Coastguard Worker 
192*b7c941bbSAndroid Build Coastguard Worker   { "pushTransformationResult", "(Ljava/lang/String;[B)V",
193*b7c941bbSAndroid Build Coastguard Worker           (void*)Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult },
194*b7c941bbSAndroid Build Coastguard Worker };
195*b7c941bbSAndroid Build Coastguard Worker 
register_android_jvmti_cts_JvmtiRedefineClassesTest(jvmtiEnv * jenv,JNIEnv * env)196*b7c941bbSAndroid Build Coastguard Worker void register_android_jvmti_cts_JvmtiRedefineClassesTest(jvmtiEnv* jenv, JNIEnv* env) {
197*b7c941bbSAndroid Build Coastguard Worker   ScopedLocalRef<jclass> klass(env, GetClass(jenv, env,
198*b7c941bbSAndroid Build Coastguard Worker           "android/jvmti/cts/JvmtiRedefineClassesTest", nullptr));
199*b7c941bbSAndroid Build Coastguard Worker   if (klass.get() == nullptr) {
200*b7c941bbSAndroid Build Coastguard Worker     env->ExceptionClear();
201*b7c941bbSAndroid Build Coastguard Worker     return;
202*b7c941bbSAndroid Build Coastguard Worker   }
203*b7c941bbSAndroid Build Coastguard Worker 
204*b7c941bbSAndroid Build Coastguard Worker   env->RegisterNatives(klass.get(), gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
205*b7c941bbSAndroid Build Coastguard Worker   if (env->ExceptionCheck()) {
206*b7c941bbSAndroid Build Coastguard Worker     env->ExceptionClear();
207*b7c941bbSAndroid Build Coastguard Worker     LOG(ERROR) << "Could not register natives for JvmtiRedefineClassesTest class";
208*b7c941bbSAndroid Build Coastguard Worker   }
209*b7c941bbSAndroid Build Coastguard Worker }
210*b7c941bbSAndroid Build Coastguard Worker 
211*b7c941bbSAndroid Build Coastguard Worker }  // namespace art
212*b7c941bbSAndroid Build Coastguard Worker 
213