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