• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
28 namespace art {
29 
30 // Should we do a 'full_rewrite' with this test?
31 static constexpr bool kDoFullRewrite = true;
32 
33 struct 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 
WriteToFile(const std::string & fname,jint data_len,const unsigned char * data)40 static 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 
ReadIntoBuffer(const std::string & fname,std::vector<unsigned char> * data)46 static 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.
DoExtractClassFromData(StressData * data,const std::string & class_name,jint in_len,const unsigned char * in_data,std::vector<unsigned char> * dex)57 static 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 
doJvmtiMethodBind(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread,jmethodID m,void * address,void ** out_address)87 static 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(WARNING) << "Unable to get thread info!";
99     info.name = const_cast<char*>("<UNKNOWN THREAD>");
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 
129 // The hook we are using.
ClassFileLoadHookSecretNoOp(jvmtiEnv * jvmti,JNIEnv * jni_env ATTRIBUTE_UNUSED,jclass class_being_redefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protection_domain ATTRIBUTE_UNUSED,jint class_data_len,const unsigned char * class_data,jint * new_class_data_len,unsigned char ** new_class_data)130 void 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}
ReadOptions(StressData * data,char * options)167 static 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.
EnsureVMClassloaderInitializedCB(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED)180 static 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     // Probably on RI. Clear the exception so we can continue but don't mark vmclassloader as
189     // initialized.
190     LOG(WARNING) << "Unable to find VMClassLoader class!";
191     jni_env->ExceptionClear();
192   } else {
193     // GetMethodID is spec'd to cause the class to be initialized.
194     jni_env->GetMethodID(klass, "hashCode", "()I");
195     jni_env->DeleteLocalRef(klass);
196     StressData* data = nullptr;
197     CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
198              JVMTI_ERROR_NONE);
199     data->vm_class_loader_initialized = true;
200   }
201 }
202 
Agent_OnLoad(JavaVM * vm,char * options,void * reserved ATTRIBUTE_UNUSED)203 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
204                                                char* options,
205                                                void* reserved ATTRIBUTE_UNUSED) {
206   jvmtiEnv* jvmti = nullptr;
207   if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
208     LOG(ERROR) << "Unable to get jvmti env.";
209     return 1;
210   }
211   StressData* data = nullptr;
212   if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
213                                           reinterpret_cast<unsigned char**>(&data))) {
214     LOG(ERROR) << "Unable to allocate data for stress test.";
215     return 1;
216   }
217   memset(data, 0, sizeof(StressData));
218   // Read the options into the static variables that hold them.
219   ReadOptions(data, options);
220   // Save the data
221   if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
222     LOG(ERROR) << "Unable to save stress test data.";
223     return 1;
224   }
225 
226   // Just get all capabilities.
227   jvmtiCapabilities caps;
228   jvmti->GetPotentialCapabilities(&caps);
229   jvmti->AddCapabilities(&caps);
230 
231   // Set callbacks.
232   jvmtiEventCallbacks cb;
233   memset(&cb, 0, sizeof(cb));
234   cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
235   cb.NativeMethodBind = doJvmtiMethodBind;
236   cb.VMInit = EnsureVMClassloaderInitializedCB;
237   if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
238     LOG(ERROR) << "Unable to set class file load hook cb!";
239     return 1;
240   }
241   if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
242                                       JVMTI_EVENT_NATIVE_METHOD_BIND,
243                                       nullptr) != JVMTI_ERROR_NONE) {
244     LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
245     return 1;
246   }
247   if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
248                                       JVMTI_EVENT_VM_INIT,
249                                       nullptr) != JVMTI_ERROR_NONE) {
250     LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
251     return 1;
252   }
253   if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
254                                       JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
255                                       nullptr) != JVMTI_ERROR_NONE) {
256     LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
257     return 1;
258   }
259   return 0;
260 }
261 
262 }  // namespace art
263