1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 16 #include "__mutex_base" 17 #include <cstddef> 18 #include <fcntl.h> 19 #include <fstream> 20 #include <memory> 21 #include <sstream> 22 #include <string> 23 #include <unistd.h> 24 #include <unordered_set> 25 26 #include <android-base/logging.h> 27 #include <android-base/macros.h> 28 29 #include <nativehelper/scoped_local_ref.h> 30 31 #include <jni.h> 32 #include <jvmti.h> 33 34 // Slicer's headers have code that triggers these warnings. b/65298177 35 #pragma clang diagnostic push 36 #pragma clang diagnostic ignored "-Wunused-parameter" 37 #pragma clang diagnostic ignored "-Wsign-compare" 38 #include <slicer/code_ir.h> 39 #include <slicer/dex_bytecode.h> 40 #include <slicer/dex_ir.h> 41 #include <slicer/dex_ir_builder.h> 42 #include <slicer/reader.h> 43 #include <slicer/writer.h> 44 #pragma clang diagnostic pop 45 46 namespace forceredefine { 47 48 namespace { 49 50 struct AgentInfo { 51 std::fstream stream; 52 std::unordered_set<std::string> classes; 53 std::mutex mutex; 54 }; 55 56 // Converts a class name to a type descriptor 57 // (ex. "java.lang.String" to "Ljava/lang/String;") 58 std::string classNameToDescriptor(const char* className) { 59 std::stringstream ss; 60 ss << "L"; 61 for (auto p = className; *p != '\0'; ++p) { 62 ss << (*p == '.' ? '/' : *p); 63 } 64 ss << ";"; 65 return ss.str(); 66 } 67 68 // Converts a descriptor (Lthis/style/of/name;) to a jni-FindClass style Fully-qualified class name 69 // (this/style/of/name). 70 std::string DescriptorToFQCN(const std::string& descriptor) { 71 return descriptor.substr(1, descriptor.size() - 2); 72 } 73 74 static AgentInfo* GetAgentInfo(jvmtiEnv* jvmti) { 75 AgentInfo* ai = nullptr; 76 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&ai)), JVMTI_ERROR_NONE); 77 CHECK(ai != nullptr); 78 return ai; 79 } 80 81 class JvmtiAllocator : public dex::Writer::Allocator { 82 public: 83 explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {} 84 void* Allocate(size_t size) override { 85 unsigned char* res = nullptr; 86 jvmti_->Allocate(size, &res); 87 return res; 88 } 89 void Free(void* ptr) override { 90 jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); 91 } 92 93 private: 94 jvmtiEnv* jvmti_; 95 }; 96 97 static void Transform(std::shared_ptr<ir::DexFile> ir) { 98 std::unique_ptr<ir::Builder> builder; 99 for (auto& method : ir->encoded_methods) { 100 // Do not look into abstract/bridge/native/synthetic methods. 101 if ((method->access_flags & 102 (dex::kAccAbstract | dex::kAccBridge | dex::kAccNative | dex::kAccSynthetic)) != 0) { 103 continue; 104 } 105 106 struct AddNopVisitor : public lir::Visitor { 107 explicit AddNopVisitor(lir::CodeIr* cir) : cir_(cir) {} 108 109 bool Visit(lir::Bytecode* bc) override { 110 if (seen_first_inst) { 111 return false; 112 } 113 seen_first_inst = true; 114 auto new_inst = cir_->Alloc<lir::Bytecode>(); 115 new_inst->opcode = dex::OP_NOP; 116 cir_->instructions.InsertBefore(bc, new_inst); 117 return true; 118 } 119 120 lir::CodeIr* cir_; 121 bool seen_first_inst = false; 122 }; 123 124 lir::CodeIr c(method.get(), ir); 125 AddNopVisitor visitor(&c); 126 for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) { 127 lir::Instruction* fi = *it; 128 if (fi->Accept(&visitor)) { 129 break; 130 } 131 } 132 c.Assemble(); 133 } 134 } 135 136 static void CbClassFileLoadHook(jvmtiEnv* jvmti, 137 JNIEnv* env ATTRIBUTE_UNUSED, 138 jclass classBeingRedefined ATTRIBUTE_UNUSED, 139 jobject loader ATTRIBUTE_UNUSED, 140 const char* name, 141 jobject protectionDomain ATTRIBUTE_UNUSED, 142 jint classDataLen, 143 const unsigned char* classData, 144 jint* newClassDataLen, 145 unsigned char** newClassData) { 146 std::string desc(classNameToDescriptor(name)); 147 std::string fqcn(DescriptorToFQCN(desc)); 148 AgentInfo* ai = GetAgentInfo(jvmti); 149 { 150 std::lock_guard<std::mutex> mu(ai->mutex); 151 if (ai->classes.find(fqcn) == ai->classes.end()) { 152 return; 153 } 154 } 155 LOG(INFO) << "Got CFLH for " << name << " on env " << static_cast<void*>(jvmti); 156 JvmtiAllocator allocator(jvmti); 157 dex::Reader reader(classData, classDataLen); 158 dex::u4 index = reader.FindClassIndex(desc.c_str()); 159 reader.CreateClassIr(index); 160 std::shared_ptr<ir::DexFile> ir(reader.GetIr()); 161 Transform(ir); 162 dex::Writer writer(ir); 163 size_t new_size; 164 *newClassData = writer.CreateImage(&allocator, &new_size); 165 *newClassDataLen = new_size; 166 } 167 168 static jclass FindClass(jvmtiEnv* jvmti, JNIEnv* env, const std::string& name) { 169 jclass res = env->FindClass(name.c_str()); 170 if (res != nullptr) { 171 return res; 172 } 173 ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred()); 174 env->ExceptionClear(); 175 // Try to find it in other classloaders. 176 env->PushLocalFrame(1 << 18); 177 do { 178 jint cnt; 179 jclass* klasses; 180 if (jvmti->GetLoadedClasses(&cnt, &klasses) != JVMTI_ERROR_NONE) { 181 LOG(ERROR) << "Unable to get loaded classes!"; 182 break; 183 } 184 for (jint i = 0; i < cnt; i++) { 185 char* sig; 186 if (jvmti->GetClassSignature(klasses[i], &sig, nullptr) != JVMTI_ERROR_NONE) { 187 continue; 188 } 189 if (sig[0] == 'L' && DescriptorToFQCN(sig) == name) { 190 res = klasses[i]; 191 break; 192 } 193 } 194 jvmti->Deallocate(reinterpret_cast<unsigned char*>(klasses)); 195 } while (false); 196 res = reinterpret_cast<jclass>(env->PopLocalFrame(res)); 197 if (res == nullptr && exc.get() != nullptr) { 198 env->Throw(exc.get()); 199 } 200 return res; 201 } 202 203 static void RedefineClass(jvmtiEnv* jvmti, JNIEnv* env, const std::string& klass_name) { 204 jclass klass = nullptr; 205 if ((klass = FindClass(jvmti, env, klass_name)) == nullptr) { 206 LOG(WARNING) << "Failed to find class for " << klass_name; 207 env->ExceptionDescribe(); 208 env->ExceptionClear(); 209 return; 210 } 211 jvmti->RetransformClasses(1, &klass); 212 env->DeleteLocalRef(klass); 213 } 214 215 static void AgentMain(jvmtiEnv* jvmti, JNIEnv* jni, void* arg ATTRIBUTE_UNUSED) { 216 AgentInfo* ai = GetAgentInfo(jvmti); 217 std::string klass_name; 218 jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr); 219 // TODO Replace this with something that can read from a fifo and ignore the 'EOF's. 220 while (std::getline(ai->stream, klass_name, '\n')) { 221 LOG(INFO) << "Redefining class " << klass_name << " with " << static_cast<void*>(jvmti); 222 { 223 std::lock_guard<std::mutex> mu(ai->mutex); 224 ai->classes.insert(klass_name); 225 } 226 RedefineClass(jvmti, jni, klass_name); 227 } 228 } 229 230 static void CbVmInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thr ATTRIBUTE_UNUSED) { 231 // Create a Thread object. 232 ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread")); 233 if (thread_name.get() == nullptr) { 234 env->ExceptionDescribe(); 235 env->ExceptionClear(); 236 return; 237 } 238 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread")); 239 if (thread_klass.get() == nullptr) { 240 env->ExceptionDescribe(); 241 env->ExceptionClear(); 242 return; 243 } 244 ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get())); 245 if (thread.get() == nullptr) { 246 env->ExceptionDescribe(); 247 env->ExceptionClear(); 248 return; 249 } 250 251 env->CallNonvirtualVoidMethod( 252 thread.get(), 253 thread_klass.get(), 254 env->GetMethodID(thread_klass.get(), "<init>", "(Ljava/lang/String;)V"), 255 thread_name.get()); 256 env->CallVoidMethod(thread.get(), env->GetMethodID(thread_klass.get(), "setPriority", "(I)V"), 1); 257 env->CallVoidMethod( 258 thread.get(), env->GetMethodID(thread_klass.get(), "setDaemon", "(Z)V"), JNI_TRUE); 259 260 jvmti->RunAgentThread(thread.get(), AgentMain, nullptr, JVMTI_THREAD_MIN_PRIORITY); 261 } 262 263 } // namespace 264 265 template <bool kIsOnLoad> 266 static jint AgentStart(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) { 267 jvmtiEnv* jvmti = nullptr; 268 269 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1) != JNI_OK || 270 jvmti == nullptr) { 271 LOG(ERROR) << "unable to obtain JVMTI env."; 272 return JNI_ERR; 273 } 274 std::string sopts(options); 275 AgentInfo* ai = new AgentInfo; 276 ai->stream.open(options, std::ios_base::in); 277 if (!ai->stream.is_open()) { 278 PLOG(ERROR) << "Could not open file " << options << " for triggering class-reload"; 279 return JNI_ERR; 280 } 281 282 jvmtiCapabilities caps{ 283 .can_retransform_classes = 1, 284 }; 285 if (jvmti->AddCapabilities(&caps) != JVMTI_ERROR_NONE) { 286 LOG(ERROR) << "Unable to get retransform_classes capability!"; 287 return JNI_ERR; 288 } 289 jvmtiEventCallbacks cb{ 290 .ClassFileLoadHook = CbClassFileLoadHook, 291 .VMInit = CbVmInit, 292 }; 293 jvmti->SetEventCallbacks(&cb, sizeof(cb)); 294 jvmti->SetEnvironmentLocalStorage(reinterpret_cast<void*>(ai)); 295 if (kIsOnLoad) { 296 jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr); 297 } else { 298 JNIEnv* jni = nullptr; 299 vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_2); 300 jthread thr; 301 jvmti->GetCurrentThread(&thr); 302 CbVmInit(jvmti, jni, thr); 303 } 304 return JNI_OK; 305 } 306 307 // Late attachment (e.g. 'am attach-agent'). 308 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { 309 return AgentStart<false>(vm, options, reserved); 310 } 311 312 // Early attachment 313 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { 314 return AgentStart<true>(jvm, options, reserved); 315 } 316 317 } // namespace forceredefine 318