blob: e7d76ddd55e713aaf11376f1c25d9c81675b8dca [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
Alex Light0af8cde2017-04-20 13:35:05 -070087static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
88 JNIEnv* env,
89 jthread thread,
90 jmethodID m,
91 void* address,
92 /*out*/void** out_address) {
93 *out_address = address;
94 jvmtiThreadInfo info;
95 if (thread == nullptr) {
96 info.name = const_cast<char*>("<NULLPTR>");
97 } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) {
98 LOG(ERROR) << "Unable to get thread info!";
99 return;
100 }
101 char *fname, *fsig, *fgen;
102 char *cname, *cgen;
103 jclass klass = nullptr;
104 if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) {
105 LOG(ERROR) << "Unable to get method declaring class!";
106 return;
107 }
108 if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) {
109 LOG(ERROR) << "Unable to get method name!";
110 env->DeleteLocalRef(klass);
111 return;
112 }
113 if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
114 LOG(ERROR) << "Unable to get class name!";
115 env->DeleteLocalRef(klass);
116 return;
117 }
118 LOG(INFO) << "Loading native method \"" << cname << "->" << fname << fsig << "\". Thread is "
119 << info.name;
120 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
121 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
122 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
123 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
124 jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
125 env->DeleteLocalRef(klass);
126 return;
127}
128
Alex Light8f2c6d42017-04-10 16:27:35 -0700129// The hook we are using.
130void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
131 JNIEnv* jni_env ATTRIBUTE_UNUSED,
132 jclass class_being_redefined ATTRIBUTE_UNUSED,
133 jobject loader ATTRIBUTE_UNUSED,
134 const char* name,
135 jobject protection_domain ATTRIBUTE_UNUSED,
136 jint class_data_len,
137 const unsigned char* class_data,
138 jint* new_class_data_len,
139 unsigned char** new_class_data) {
140 std::vector<unsigned char> out;
141 std::string name_str(name);
142 // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
143 // classes).
144 std::replace(name_str.begin(), name_str.end(), '/', '.');
145 StressData* data = nullptr;
146 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
147 JVMTI_ERROR_NONE);
148 if (!data->vm_class_loader_initialized) {
149 LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
150 << "initialized. Transforming this class could cause spurious test failures.";
151 return;
152 } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
153 LOG(INFO) << "Extracted class: " << name;
154 unsigned char* new_data;
155 CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
156 memcpy(new_data, out.data(), out.size());
157 *new_class_data_len = static_cast<jint>(out.size());
158 *new_class_data = new_data;
159 } else {
160 std::cerr << "Unable to extract class " << name_str << std::endl;
161 *new_class_data_len = 0;
162 *new_class_data = nullptr;
163 }
164}
165
166// Options are ${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2}
167static void ReadOptions(StressData* data, char* options) {
168 std::string ops(options);
169 data->dexter_cmd = ops.substr(0, ops.find(','));
170 ops = ops.substr(ops.find(',') + 1);
171 data->in_temp_dex = ops.substr(0, ops.find(','));
172 ops = ops.substr(ops.find(',') + 1);
173 data->out_temp_dex = ops;
174}
175
176// We need to make sure that VMClassLoader is initialized before we start redefining anything since
177// it can give (non-fatal) error messages if it's initialized after we've redefined BCP classes.
178// These error messages are expected and no problem but they will mess up our testing
179// infrastructure.
180static void JNICALL EnsureVMClassloaderInitializedCB(jvmtiEnv *jvmti_env,
181 JNIEnv* jni_env,
182 jthread thread ATTRIBUTE_UNUSED) {
183 // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
184 // visibility but the class will be loaded behind the scenes.
185 LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
186 jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
187 if (klass == nullptr) {
188 LOG(ERROR) << "Unable to find VMClassLoader class!";
189 } else {
190 // GetMethodID is spec'd to cause the class to be initialized.
191 jni_env->GetMethodID(klass, "hashCode", "()I");
192 jni_env->DeleteLocalRef(klass);
193 StressData* data = nullptr;
194 CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
195 JVMTI_ERROR_NONE);
196 data->vm_class_loader_initialized = true;
197 }
198}
199
200extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
201 char* options,
202 void* reserved ATTRIBUTE_UNUSED) {
203 jvmtiEnv* jvmti = nullptr;
204 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
205 LOG(ERROR) << "Unable to get jvmti env.";
206 return 1;
207 }
208 StressData* data = nullptr;
209 if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
210 reinterpret_cast<unsigned char**>(&data))) {
211 LOG(ERROR) << "Unable to allocate data for stress test.";
212 return 1;
213 }
214 memset(data, 0, sizeof(StressData));
215 // Read the options into the static variables that hold them.
216 ReadOptions(data, options);
217 // Save the data
218 if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
219 LOG(ERROR) << "Unable to save stress test data.";
220 return 1;
221 }
222
223 // Just get all capabilities.
224 jvmtiCapabilities caps;
225 jvmti->GetPotentialCapabilities(&caps);
226 jvmti->AddCapabilities(&caps);
227
228 // Set callbacks.
229 jvmtiEventCallbacks cb;
230 memset(&cb, 0, sizeof(cb));
231 cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
Alex Light0af8cde2017-04-20 13:35:05 -0700232 cb.NativeMethodBind = doJvmtiMethodBind;
Alex Light8f2c6d42017-04-10 16:27:35 -0700233 cb.VMInit = EnsureVMClassloaderInitializedCB;
234 if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
235 LOG(ERROR) << "Unable to set class file load hook cb!";
236 return 1;
237 }
238 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light0af8cde2017-04-20 13:35:05 -0700239 JVMTI_EVENT_NATIVE_METHOD_BIND,
240 nullptr) != JVMTI_ERROR_NONE) {
241 LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
242 return 1;
243 }
244 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
Alex Light8f2c6d42017-04-10 16:27:35 -0700245 JVMTI_EVENT_VM_INIT,
246 nullptr) != JVMTI_ERROR_NONE) {
247 LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
248 return 1;
249 }
250 if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
251 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
252 nullptr) != JVMTI_ERROR_NONE) {
253 LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
254 return 1;
255 }
256 return 0;
257}
258
259} // namespace art