blob: 5654c22ca3bed1f7c88d5c40ca127ccae7e7eb11 [file] [log] [blame]
Andreas Gampece7732b2017-01-17 15:50:26 -08001/* Copyright (C) 2017 The Android Open Source Project
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This file implements interfaces from the file jvmti.h. This implementation
5 * is licensed under the same terms as the file jvmti.h. The
6 * copyright and license information for the file jvmti.h follows.
7 *
8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10 *
11 * This code is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 only, as
13 * published by the Free Software Foundation. Oracle designates this
14 * particular file as subject to the "Classpath" exception as provided
15 * by Oracle in the LICENSE file that accompanied this code.
16 *
17 * This code is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * version 2 for more details (a copy is included in the LICENSE file that
21 * accompanied this code).
22 *
23 * You should have received a copy of the GNU General Public License version
24 * 2 along with this work; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28 * or visit www.oracle.com if you need additional information or have any
29 * questions.
30 */
31
Alex Light639e73b2019-05-17 21:44:36 +000032#include <sstream>
33#include <unistd.h>
34
Andreas Gampece7732b2017-01-17 15:50:26 -080035#include "ti_search.h"
36
37#include "jni.h"
38
Andreas Gampea1d2f952017-04-20 22:53:58 -070039#include "art_field-inl.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080040#include "art_jvmti.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080041#include "base/enums.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080042#include "base/macros.h"
Alex Light639e73b2019-05-17 21:44:36 +000043#include "base/memfd.h"
44#include "base/os.h"
45#include "base/unix_file/fd_file.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080046#include "class_linker.h"
David Sehr013fd802018-01-11 22:55:24 -080047#include "dex/art_dex_file_loader.h"
David Sehr9e734c72018-01-04 17:56:19 -080048#include "dex/dex_file.h"
49#include "dex/dex_file_loader.h"
Vladimir Markoa3ad0cd2018-05-04 10:06:38 +010050#include "jni/jni_internal.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080051#include "mirror/class-inl.h"
52#include "mirror/object.h"
53#include "mirror/string.h"
Andreas Gampe373a9b52017-10-18 09:01:57 -070054#include "nativehelper/scoped_local_ref.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080055#include "obj_ptr-inl.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080056#include "runtime.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080057#include "runtime_callbacks.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080058#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070059#include "thread-current-inl.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080060#include "thread_list.h"
Alex Lightae45cbb2018-10-18 15:49:56 -070061#include "ti_logging.h"
Steven Morelande431e272017-07-18 16:53:49 -070062#include "ti_phase.h"
Andreas Gampea1d2f952017-04-20 22:53:58 -070063#include "well_known_classes.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080064
65namespace openjdkjvmti {
66
Andreas Gampecefaa142017-01-23 15:04:59 -080067static std::vector<std::string> gSystemOnloadSegments;
68
69static art::ObjPtr<art::mirror::Object> GetSystemProperties(art::Thread* self,
70 art::ClassLinker* class_linker)
71 REQUIRES_SHARED(art::Locks::mutator_lock_) {
72 art::ObjPtr<art::mirror::Class> system_class =
73 class_linker->LookupClass(self, "Ljava/lang/System;", nullptr);
74 DCHECK(system_class != nullptr);
75 DCHECK(system_class->IsInitialized());
76
77 art::ArtField* props_field =
78 system_class->FindDeclaredStaticField("props", "Ljava/util/Properties;");
79 DCHECK(props_field != nullptr);
80
81 art::ObjPtr<art::mirror::Object> props_obj = props_field->GetObject(system_class);
82 DCHECK(props_obj != nullptr);
83
84 return props_obj;
85}
86
87static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) {
88 if (gSystemOnloadSegments.empty()) {
89 return;
90 }
91
92 // In the on-load phase we have to modify java.class.path to influence the system classloader.
93 // As this is an unmodifiable system property, we have to access the "defaults" field.
94 art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
95 DCHECK(class_linker != nullptr);
96 art::Thread* self = art::Thread::Current();
97
98 // Prepare: collect classes, fields and methods.
99 art::ObjPtr<art::mirror::Class> properties_class =
100 class_linker->LookupClass(self, "Ljava/util/Properties;", nullptr);
101 DCHECK(properties_class != nullptr);
102
103 ScopedLocalRef<jobject> defaults_jobj(self->GetJniEnv(), nullptr);
104 {
105 art::ObjPtr<art::mirror::Object> props_obj = GetSystemProperties(self, class_linker);
106
107 art::ArtField* defaults_field =
108 properties_class->FindDeclaredInstanceField("defaults", "Ljava/util/Properties;");
109 DCHECK(defaults_field != nullptr);
110
111 art::ObjPtr<art::mirror::Object> defaults_obj = defaults_field->GetObject(props_obj);
112 DCHECK(defaults_obj != nullptr);
113 defaults_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(defaults_obj));
114 }
115
116 art::ArtMethod* get_property =
Vladimir Markoba118822017-06-12 15:41:56 +0100117 properties_class->FindClassMethod(
Andreas Gampecefaa142017-01-23 15:04:59 -0800118 "getProperty",
119 "(Ljava/lang/String;)Ljava/lang/String;",
120 art::kRuntimePointerSize);
121 DCHECK(get_property != nullptr);
Vladimir Markoba118822017-06-12 15:41:56 +0100122 DCHECK(!get_property->IsDirect());
123 DCHECK(get_property->GetDeclaringClass() == properties_class);
Andreas Gampecefaa142017-01-23 15:04:59 -0800124 art::ArtMethod* set_property =
Vladimir Markoba118822017-06-12 15:41:56 +0100125 properties_class->FindClassMethod(
Andreas Gampecefaa142017-01-23 15:04:59 -0800126 "setProperty",
127 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
128 art::kRuntimePointerSize);
129 DCHECK(set_property != nullptr);
Vladimir Markoba118822017-06-12 15:41:56 +0100130 DCHECK(!set_property->IsDirect());
131 DCHECK(set_property->GetDeclaringClass() == properties_class);
Andreas Gampecefaa142017-01-23 15:04:59 -0800132
133 // This is an allocation. Do this late to avoid the need for handles.
134 ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr);
135 {
136 art::ObjPtr<art::mirror::Object> cp_key =
137 art::mirror::String::AllocFromModifiedUtf8(self, "java.class.path");
138 if (cp_key == nullptr) {
139 self->AssertPendingOOMException();
140 self->ClearException();
141 return;
142 }
143 cp_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(cp_key));
144 }
145
146 // OK, now get the current value.
147 std::string str_value;
148 {
149 ScopedLocalRef<jobject> old_value(self->GetJniEnv(),
150 self->GetJniEnv()->CallObjectMethod(
151 defaults_jobj.get(),
152 art::jni::EncodeArtMethod(get_property),
153 cp_jobj.get()));
154 DCHECK(old_value.get() != nullptr);
155
156 str_value = self->DecodeJObject(old_value.get())->AsString()->ToModifiedUtf8();
157 self->GetJniEnv()->DeleteLocalRef(old_value.release());
158 }
159
160 // Update the value by appending the new segments.
161 for (const std::string& segment : gSystemOnloadSegments) {
162 if (!str_value.empty()) {
163 str_value += ":";
164 }
165 str_value += segment;
166 }
167 gSystemOnloadSegments.clear();
168
169 // Create the new value object.
170 ScopedLocalRef<jobject> new_val_jobj(self->GetJniEnv(), nullptr);
171 {
172 art::ObjPtr<art::mirror::Object> new_value =
173 art::mirror::String::AllocFromModifiedUtf8(self, str_value.c_str());
174 if (new_value == nullptr) {
175 self->AssertPendingOOMException();
176 self->ClearException();
177 return;
178 }
179
180 new_val_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(new_value));
181 }
182
183 // Write to the defaults.
184 ScopedLocalRef<jobject> res_obj(self->GetJniEnv(),
185 self->GetJniEnv()->CallObjectMethod(defaults_jobj.get(),
186 art::jni::EncodeArtMethod(set_property),
187 cp_jobj.get(),
188 new_val_jobj.get()));
189 if (self->IsExceptionPending()) {
190 self->ClearException();
191 return;
192 }
193}
194
195struct SearchCallback : public art::RuntimePhaseCallback {
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100196 void NextRuntimePhase(RuntimePhase phase) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
Andreas Gampecefaa142017-01-23 15:04:59 -0800197 if (phase == RuntimePhase::kStart) {
198 // It's time to update the system properties.
199 Update();
200 }
201 }
202};
203
204static SearchCallback gSearchCallback;
205
206void SearchUtil::Register() {
207 art::Runtime* runtime = art::Runtime::Current();
208
209 art::ScopedThreadStateChange stsc(art::Thread::Current(),
210 art::ThreadState::kWaitingForDebuggerToAttach);
211 art::ScopedSuspendAll ssa("Add search callback");
212 runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gSearchCallback);
213}
214
215void SearchUtil::Unregister() {
216 art::ScopedThreadStateChange stsc(art::Thread::Current(),
217 art::ThreadState::kWaitingForDebuggerToAttach);
218 art::ScopedSuspendAll ssa("Remove search callback");
219 art::Runtime* runtime = art::Runtime::Current();
220 runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback);
221}
222
Alex Lightae45cbb2018-10-18 15:49:56 -0700223jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env,
Andreas Gampece7732b2017-01-17 15:50:26 -0800224 const char* segment) {
225 art::Runtime* current = art::Runtime::Current();
226 if (current == nullptr) {
227 return ERR(WRONG_PHASE);
228 }
229 if (current->GetClassLinker() == nullptr) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800230 return ERR(WRONG_PHASE);
231 }
232 if (segment == nullptr) {
233 return ERR(NULL_POINTER);
234 }
235
236 std::string error_msg;
237 std::vector<std::unique_ptr<const art::DexFile>> dex_files;
David Sehr013fd802018-01-11 22:55:24 -0800238 const art::ArtDexFileLoader dex_file_loader;
Andreas Gampe6e897762018-10-16 13:09:32 -0700239 if (!dex_file_loader.Open(segment,
240 segment,
241 /* verify= */ true,
242 /* verify_checksum= */ true,
243 &error_msg,
244 &dex_files)) {
Alex Lightae45cbb2018-10-18 15:49:56 -0700245 JVMTI_LOG(WARNING, env) << "Could not open " << segment << " for boot classpath extension: "
246 << error_msg;
Andreas Gampece7732b2017-01-17 15:50:26 -0800247 return ERR(ILLEGAL_ARGUMENT);
248 }
249
250 art::ScopedObjectAccess soa(art::Thread::Current());
251 for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
252 current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
253 }
254
255 return ERR(NONE);
256}
257
Alex Light639e73b2019-05-17 21:44:36 +0000258jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
259 jobject classloader,
260 const char* dex_bytes,
261 jint dex_bytes_length) {
262 if (jvmti_env == nullptr) {
263 return ERR(INVALID_ENVIRONMENT);
264 } else if (art::Thread::Current() == nullptr) {
265 return ERR(UNATTACHED_THREAD);
266 } else if (classloader == nullptr) {
267 return ERR(NULL_POINTER);
268 } else if (dex_bytes == nullptr) {
269 return ERR(NULL_POINTER);
270 } else if (dex_bytes_length <= 0) {
271 return ERR(ILLEGAL_ARGUMENT);
272 }
273
274 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
275
276 // TODO We really should try to support doing this during the ON_LOAD phase.
277 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
278 JVMTI_LOG(INFO, jvmti_env) << "Cannot add buffers to classpath during ON_LOAD phase to "
279 << "prevent file-descriptor leaking.";
280 return ERR(WRONG_PHASE);
281 }
282
283 // We have java APIs for adding files to the classpath, we might as well use them. It simplifies a
284 // lot of code as well.
285
286 // Create a memfd
287 art::File file(art::memfd_create_compat("JVMTI InMemory Added dex file", 0), /*check-usage*/true);
288 if (file.Fd() < 0) {
289 char* reason = strerror(errno);
290 JVMTI_LOG(ERROR, jvmti_env) << "Unable to create memfd due to " << reason;
291 return ERR(INTERNAL);
292 }
293 // Fill it with the buffer.
294 if (!file.WriteFully(dex_bytes, dex_bytes_length) || file.Flush() != 0) {
295 JVMTI_LOG(ERROR, jvmti_env) << "Failed to write to memfd!";
296 return ERR(INTERNAL);
297 }
298 // Get the filename in procfs.
299 std::ostringstream oss;
300 oss << "/proc/self/fd/" << file.Fd();
301 std::string seg(oss.str());
302 // Use common code.
303
304 jvmtiError result = AddToDexClassLoader(jvmti_env, classloader, seg.c_str());
305 // We have either loaded the dex file and have a new MemMap pointing to the same pages or loading
306 // has failed and the memory isn't needed anymore. Either way we can close the memfd we created
307 // and return.
308 if (file.Close() != 0) {
309 JVMTI_LOG(WARNING, jvmti_env) << "Failed to close memfd!";
310 }
311 return result;
312}
313
314jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env,
315 jobject classloader,
316 const char* segment) {
317 if (jvmti_env == nullptr) {
318 return ERR(INVALID_ENVIRONMENT);
319 } else if (art::Thread::Current() == nullptr) {
320 return ERR(UNATTACHED_THREAD);
321 } else if (classloader == nullptr) {
322 return ERR(NULL_POINTER);
323 } else if (segment == nullptr) {
324 return ERR(NULL_POINTER);
325 }
326
327 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
328
329 // TODO We really should try to support doing this during the ON_LOAD phase.
330 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
331 JVMTI_LOG(INFO, jvmti_env) << "Cannot add to classpath of arbitrary classloaders during "
332 << "ON_LOAD phase.";
333 return ERR(WRONG_PHASE);
334 }
335
336 // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
337 // exceptions are swallowed.
338
339 art::Thread* self = art::Thread::Current();
340 JNIEnv* env = self->GetJniEnv();
341 if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
342 JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
343 return ERR(CLASS_LOADER_UNSUPPORTED);
344 }
345
346 jmethodID add_dex_path_id = env->GetMethodID(
347 art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
348 "addDexPath",
349 "(Ljava/lang/String;)V");
350 if (add_dex_path_id == nullptr) {
351 return ERR(INTERNAL);
352 }
353
354 ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
355 if (dex_path.get() == nullptr) {
356 return ERR(INTERNAL);
357 }
358 env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
359
360 if (env->ExceptionCheck()) {
361 {
362 art::ScopedObjectAccess soa(self);
363 JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
364 << self->GetException()->Dump();
365 }
366 env->ExceptionClear();
367 return ERR(ILLEGAL_ARGUMENT);
368 }
369 return OK;
370}
371
372jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const char* segment) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800373 if (segment == nullptr) {
374 return ERR(NULL_POINTER);
375 }
376
Andreas Gampecefaa142017-01-23 15:04:59 -0800377 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
378
379 if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) {
380 // We could try and see whether it is a valid path. We could also try to allocate Java
381 // objects to avoid later OOME.
382 gSystemOnloadSegments.push_back(segment);
383 return ERR(NONE);
384 } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800385 return ERR(WRONG_PHASE);
386 }
Andreas Gampecefaa142017-01-23 15:04:59 -0800387
Alex Light639e73b2019-05-17 21:44:36 +0000388 jobject loader = art::Runtime::Current()->GetSystemClassLoader();
389 if (loader == nullptr) {
Andreas Gampecefaa142017-01-23 15:04:59 -0800390 return ERR(INTERNAL);
Andreas Gampece7732b2017-01-17 15:50:26 -0800391 }
392
Andreas Gampece7732b2017-01-17 15:50:26 -0800393 art::Thread* self = art::Thread::Current();
394 JNIEnv* env = self->GetJniEnv();
Alex Light639e73b2019-05-17 21:44:36 +0000395 if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800396 return ERR(INTERNAL);
397 }
398
Alex Light639e73b2019-05-17 21:44:36 +0000399 return AddToDexClassLoader(jvmti_env, loader, segment);
Andreas Gampece7732b2017-01-17 15:50:26 -0800400}
401
402} // namespace openjdkjvmti