普通文本  |  163行  |  5.42 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <jni.h>

#include <cstring>
#include <cstdlib>
#include <sstream>

#include "jvmti.h"

#include <slicer/dex_ir.h>
#include <slicer/writer.h>
#include <slicer/reader.h>

using namespace dex;

namespace com_android_dx_mockito_inline_tests {
    static jvmtiEnv *localJvmtiEnv;

    // Converts a class name to a type descriptor
    // (ex. "java.lang.String" to "Ljava/lang/String;")
    static std::string
    ClassNameToDescriptor(const char* class_name) {
        std::stringstream ss;
        ss << "L";
        for (auto p = class_name; *p != '\0'; ++p) {
            ss << (*p == '.' ? '/' : *p);
        }
        ss << ";";
        return ss.str();
    }

    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) {
        // Isolate byte code of class class. This is needed as Android usually gives us more
        // than the class we need.
        // Then just return the isolated byte code without modification.
        Reader reader(classData, (size_t) classDataLen);

        u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
        reader.CreateClassIr(index);
        std::shared_ptr<ir::DexFile> ir = reader.GetIr();

        class Allocator : public Writer::Allocator {
            jvmtiEnv *jvmti_env;

        public:
            Allocator(jvmtiEnv *jvmti_env) : Writer::Allocator(), jvmti_env(jvmti_env) {
            }

            virtual void *Allocate(size_t size) {
                unsigned char *mem;
                jvmti_env->Allocate(size, &mem);
                return mem;
            }

            virtual void Free(void *ptr) { ::free(ptr); }
        };

        Allocator allocator(jvmti_env);
        Writer writer(ir);
        size_t newClassLen;
        *newClassData = writer.CreateImage(&allocator, &newClassLen);
        *newClassDataLen = (jint) newClassLen;
    }

    // Initializes the agent
    extern "C" jint Agent_OnAttach(JavaVM *vm,
                                   char *options,
                                   void *reserved) {
        jint jvmError = vm->GetEnv(reinterpret_cast<void **>(&localJvmtiEnv), JVMTI_VERSION_1_2);
        if (jvmError != JNI_OK) {
            return jvmError;
        }

        jvmtiCapabilities caps;
        memset(&caps, 0, sizeof(caps));
        caps.can_retransform_classes = 1;

        jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
        if (error != JVMTI_ERROR_NONE) {
            return error;
        }

        jvmtiEventCallbacks cb;
        memset(&cb, 0, sizeof(cb));
        cb.ClassFileLoadHook = Transform;

        error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
        if (error != JVMTI_ERROR_NONE) {
            return error;
        }

        error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,
                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                                        NULL);
        if (error != JVMTI_ERROR_NONE) {
            return error;
        }

        return JVMTI_ERROR_NONE;
    }


    // Triggers retransformation of classes via this file's Transform method
    extern "C" JNIEXPORT jint JNICALL
    Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(
            JNIEnv *env,
            jobject thiz,
            jobjectArray classes) {
        jsize numTransformedClasses = env->GetArrayLength(classes);
        jclass *transformedClasses = (jclass *) malloc(numTransformedClasses * sizeof(jclass));
        for (int i = 0; i < numTransformedClasses; i++) {
            transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes,
                                                                                          i));
        }

        jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
                                                             transformedClasses);

        for (int i = 0; i < numTransformedClasses; i++) {
            env->DeleteGlobalRef(transformedClasses[i]);
        }
        free(transformedClasses);

        return error;
    }

    // Disable hook to not slow down test
    extern "C" JNIEXPORT jint JNICALL
    Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_disableRetransformHook(
            JNIEnv *env,
            jclass ignored) {
        return localJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,
                                                       JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                                       NULL);

    }
}