Home | History | Annotate | Download | only in multiplejvmtiagentsinterferenceagent
      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
     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
     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
     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
    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
    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