• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 <jni.h>
18 
19 #include <cstring>
20 #include <cstdlib>
21 #include <sstream>
22 
23 #include "jvmti.h"
24 
25 #include <slicer/dex_ir.h>
26 #include <slicer/writer.h>
27 #include <slicer/reader.h>
28 
29 using namespace dex;
30 
31 namespace com_android_dx_mockito_inline_tests {
32     static jvmtiEnv *localJvmtiEnv;
33 
34     // Converts a class name to a type descriptor
35     // (ex. "java.lang.String" to "Ljava/lang/String;")
36     static std::string
ClassNameToDescriptor(const char * class_name)37     ClassNameToDescriptor(const char* class_name) {
38         std::stringstream ss;
39         ss << "L";
40         for (auto p = class_name; *p != '\0'; ++p) {
41             ss << (*p == '.' ? '/' : *p);
42         }
43         ss << ";";
44         return ss.str();
45     }
46 
47     static void
Transform(jvmtiEnv * jvmti_env,JNIEnv * env,jclass classBeingRedefined,jobject loader,const char * name,jobject protectionDomain,jint classDataLen,const unsigned char * classData,jint * newClassDataLen,unsigned char ** newClassData)48     Transform(jvmtiEnv *jvmti_env,
49               JNIEnv *env,
50               jclass classBeingRedefined,
51               jobject loader,
52               const char *name,
53               jobject protectionDomain,
54               jint classDataLen,
55               const unsigned char *classData,
56               jint *newClassDataLen,
57               unsigned char **newClassData) {
58         // Isolate byte code of class class. This is needed as Android usually gives us more
59         // than the class we need.
60         // Then just return the isolated byte code without modification.
61         Reader reader(classData, (size_t) classDataLen);
62 
63         u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
64         reader.CreateClassIr(index);
65         std::shared_ptr<ir::DexFile> ir = reader.GetIr();
66 
67         class Allocator : public Writer::Allocator {
68             jvmtiEnv *jvmti_env;
69 
70         public:
71             Allocator(jvmtiEnv *jvmti_env) : Writer::Allocator(), jvmti_env(jvmti_env) {
72             }
73 
74             virtual void *Allocate(size_t size) {
75                 unsigned char *mem;
76                 jvmti_env->Allocate(size, &mem);
77                 return mem;
78             }
79 
80             virtual void Free(void *ptr) { ::free(ptr); }
81         };
82 
83         Allocator allocator(jvmti_env);
84         Writer writer(ir);
85         size_t newClassLen;
86         *newClassData = writer.CreateImage(&allocator, &newClassLen);
87         *newClassDataLen = (jint) newClassLen;
88     }
89 
90     // Initializes the agent
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)91     extern "C" jint Agent_OnAttach(JavaVM *vm,
92                                    char *options,
93                                    void *reserved) {
94         jint jvmError = vm->GetEnv(reinterpret_cast<void **>(&localJvmtiEnv), JVMTI_VERSION_1_2);
95         if (jvmError != JNI_OK) {
96             return jvmError;
97         }
98 
99         jvmtiCapabilities caps;
100         memset(&caps, 0, sizeof(caps));
101         caps.can_retransform_classes = 1;
102 
103         jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
104         if (error != JVMTI_ERROR_NONE) {
105             return error;
106         }
107 
108         jvmtiEventCallbacks cb;
109         memset(&cb, 0, sizeof(cb));
110         cb.ClassFileLoadHook = Transform;
111 
112         error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
113         if (error != JVMTI_ERROR_NONE) {
114             return error;
115         }
116 
117         error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,
118                                                         JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
119                                                         NULL);
120         if (error != JVMTI_ERROR_NONE) {
121             return error;
122         }
123 
124         return JVMTI_ERROR_NONE;
125     }
126 
127 
128     // Triggers retransformation of classes via this file's Transform method
129     extern "C" JNIEXPORT jint JNICALL
Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(JNIEnv * env,jobject thiz,jobjectArray classes)130     Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(
131             JNIEnv *env,
132             jobject thiz,
133             jobjectArray classes) {
134         jsize numTransformedClasses = env->GetArrayLength(classes);
135         jclass *transformedClasses = (jclass *) malloc(numTransformedClasses * sizeof(jclass));
136         for (int i = 0; i < numTransformedClasses; i++) {
137             transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes,
138                                                                                           i));
139         }
140 
141         jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
142                                                              transformedClasses);
143 
144         for (int i = 0; i < numTransformedClasses; i++) {
145             env->DeleteGlobalRef(transformedClasses[i]);
146         }
147         free(transformedClasses);
148 
149         return error;
150     }
151 
152     // Disable hook to not slow down test
153     extern "C" JNIEXPORT jint JNICALL
Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_disableRetransformHook(JNIEnv * env,jclass ignored)154     Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_disableRetransformHook(
155             JNIEnv *env,
156             jclass ignored) {
157         return localJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,
158                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
159                                                        NULL);
160 
161     }
162 }
163