blob: fa49a35a8ed71ce71fc58354dcc25dd7b0f37372 [file] [log] [blame]
Alex Light8f2c6d42017-04-10 16:27:35 -07001/*
2 * Copyright 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#include <stdio.h>
19#include <iostream>
20#include <fstream>
21#include <stdio.h>
22#include <sstream>
23
24#include "jvmti.h"
25#include "exec_utils.h"
26#include "utils.h"
27
28namespace art {
29
30// Should we do a 'full_rewrite' with this test?
31static constexpr bool kDoFullRewrite = true;
32
33struct StressData {
34 std::string dexter_cmd;
35 std::string out_temp_dex;
36 std::string in_temp_dex;
37 bool vm_class_loader_initialized;
38};
39
40static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
41 std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
42 file.write(reinterpret_cast<const char*>(data), data_len);
43 file.flush();
44}
45
46static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
47 std::ifstream file(fname, std::ios::binary | std::ios::in);
48 file.seekg(0, std::ios::end);
49 size_t len = file.tellg();
50 data->resize(len);
51 file.seekg(0);
52 file.read(reinterpret_cast<char*>(data->data()), len);
53 return len != 0;
54}
55
56// TODO rewrite later.
57static bool DoExtractClassFromData(StressData* data,
58 const std::string& class_name,
59 jint in_len,
60 const unsigned char* in_data,
61 /*out*/std::vector<unsigned char>* dex) {
62 // Write the dex file into a temporary file.
63 WriteToFile(data->in_temp_dex, in_len, in_data);
64 // Clear out file so even if something suppresses the exit value we will still detect dexter
65 // failure.
66 WriteToFile(data->out_temp_dex, 0, nullptr);
67 // Have dexter do the extraction.
68 std::vector<std::string> args;
69 args.push_back(data->dexter_cmd);
70 if (kDoFullRewrite) {
71 args.push_back("-x");
72 args.push_back("full_rewrite");
73 }
74 args.push_back("-e");
75 args.push_back(class_name);
76 args.push_back("-o");
77 args.push_back(data->out_temp_dex);
78 args.push_back(data->in_temp_dex);
79 std::string error;
80 if (ExecAndReturnCode(args, &error) != 0) {
81 LOG(ERROR) << "unable to execute dexter: " << error;
82 return false;
83 }
84 return ReadIntoBuffer(data->out_temp_dex, dex);
85}
86
87// The hook we are using.
88void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
89 JNIEnv* jni_env ATTRIBUTE_UNUSED,
90 jclass class_being_redefined ATTRIBUTE_UNUSED,
91 jobject loader ATTRIBUTE_UNUSED,
92 const char* name,
93 jobject protection_domain ATTRIBUTE_UNUSED,
94 jint class_data_len,
95 const unsigned char* class_data,
96 jint* new_class_data_len,
97 unsigned char** new_class_data) {
98 std::vector<unsigned char> out;
99 std::string name_str(name);
100 // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
101 // classes).
102 std::replace(name_str.begin(), name_str.end(), '/', '.');
103 StressData* data = nullptr;
104 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
105 JVMTI_ERROR_NONE);
106 if (!data->vm_class_loader_initialized) {
107 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
108 << "initialized. Transforming this class could cause spurious test failures.";
109 return;
110 } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
111 LOG(INFO) << "Extracted class: " << name;
112 unsigned char* new_data;
113 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
114 memcpy(new_data, out.data(), out.size());
115 *new_class_data_len = static_cast<jint>(out.size());
116 *new_class_data = new_data;
117 } else {
118 std::cerr << "Unable to extract class " << name_str << std::endl;
119 *new_class_data_len = 0;
120 *new_class_data = nullptr;
121 }
122}
123
124// Options are ${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2}
125static void ReadOptions(StressData* data, char* options) {
126 std::string ops(options);
127 data->dexter_cmd = ops.substr(0, ops.find(','));
128 ops = ops.substr(ops.find(',') + 1);
129 data->in_temp_dex = ops.substr(0, ops.find(','));
130 ops = ops.substr(ops.find(',') + 1);
131 data->out_temp_dex = ops;
132}
133
134// We need to make sure that VMClassLoader is initialized before we start redefining anything since
135// it can give (non-fatal) error messages if it's initialized after we've redefined BCP classes.
136// These error messages are expected and no problem but they will mess up our testing
137// infrastructure.
138static void JNICALL EnsureVMClassloaderInitializedCB(jvmtiEnv *jvmti_env,
139 JNIEnv* jni_env,
140 jthread thread ATTRIBUTE_UNUSED) {
141 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
142 // visibility but the class will be loaded behind the scenes.
143 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
144 jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
145 if (klass == nullptr) {
146 LOG(ERROR) << "Unable to find VMClassLoader class!";
147 } else {
148 // GetMethodID is spec'd to cause the class to be initialized.
149 jni_env->GetMethodID(klass, "hashCode", "()I");
150 jni_env->DeleteLocalRef(klass);
151 StressData* data = nullptr;
152 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
153 JVMTI_ERROR_NONE);
154 data->vm_class_loader_initialized = true;
155 }
156}
157
158extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
159 char* options,
160 void* reserved ATTRIBUTE_UNUSED) {
161 jvmtiEnv* jvmti = nullptr;
162 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
163 LOG(ERROR) << "Unable to get jvmti env.";
164 return 1;
165 }
166 StressData* data = nullptr;
167 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
168 reinterpret_cast<unsigned char**>(&data))) {
169 LOG(ERROR) << "Unable to allocate data for stress test.";
170 return 1;
171 }
172 memset(data, 0, sizeof(StressData));
173 // Read the options into the static variables that hold them.
174 ReadOptions(data, options);
175 // Save the data
176 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
177 LOG(ERROR) << "Unable to save stress test data.";
178 return 1;
179 }
180
181 // Just get all capabilities.
182 jvmtiCapabilities caps;
183 jvmti->GetPotentialCapabilities(&caps);
184 jvmti->AddCapabilities(&caps);
185
186 // Set callbacks.
187 jvmtiEventCallbacks cb;
188 memset(&cb, 0, sizeof(cb));
189 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
190 cb.VMInit = EnsureVMClassloaderInitializedCB;
191 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
192 LOG(ERROR) << "Unable to set class file load hook cb!";
193 return 1;
194 }
195 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
196 JVMTI_EVENT_VM_INIT,
197 nullptr) != JVMTI_ERROR_NONE) {
198 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
199 return 1;
200 }
201 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
202 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
203 nullptr) != JVMTI_ERROR_NONE) {
204 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
205 return 1;
206 }
207 return 0;
208}
209
210} // namespace art