ART: Add Search API
Add initial support for AddToBootstrapClassLoaderSearch and
AddToSystemClassLoaderSearch. The current implementation does
not support the OnLoad phase.
Add tests.
Bug: 34359699
Test: m test-art-host-run-test-929-search
Change-Id: I42955ff381cd3828bfd772bee2f9b0745195ee51
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cad674e..87b1dd1 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -649,6 +649,10 @@
ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -744,9 +748,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 42fed50..3a38761 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@
"ti_monitor.cc",
"ti_object.cc",
"ti_properties.cc",
+ "ti_search.cc",
"ti_stack.cc",
"ti_redefine.cc",
"ti_thread.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 4aedec9..2ffd7ec 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -56,6 +56,7 @@
#include "ti_object.h"
#include "ti_properties.h"
#include "ti_redefine.h"
+#include "ti_search.h"
#include "ti_stack.h"
#include "ti_thread.h"
#include "ti_timers.h"
@@ -1046,11 +1047,11 @@
}
static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
}
static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
}
static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
new file mode 100644
index 0000000..913d2b6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_search.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ if (current->GetClassLinker() == nullptr) {
+ // TODO: Support boot classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ std::string error_msg;
+ std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+ if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+ LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ jobject sys_class_loader = current->GetSystemClassLoader();
+ if (sys_class_loader == nullptr) {
+ // TODO: Support classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+
+ // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+ // exceptions are swallowed.
+
+ art::Thread* self = art::Thread::Current();
+ JNIEnv* env = self->GetJniEnv();
+ if (!env->IsInstanceOf(sys_class_loader,
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ return ERR(INTERNAL);
+ }
+
+ jmethodID add_dex_path_id = env->GetMethodID(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+ "addDexPath",
+ "(Ljava/lang/String;)V");
+ if (add_dex_path_id == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+ if (dex_path.get() == nullptr) {
+ return ERR(INTERNAL);
+ }
+ env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
new file mode 100644
index 0000000..6a52e80
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class SearchUtil {
+ public:
+ static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+ static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/test/929-search/build b/test/929-search/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/929-search/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/929-search/expected.txt b/test/929-search/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/929-search/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/929-search/info.txt b/test/929-search/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/929-search/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/929-search/run b/test/929-search/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/929-search/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti \
+ --no-app-image
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
new file mode 100644
index 0000000..d1c6984
--- /dev/null
+++ b/test/929-search/search.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test929Search {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+} // namespace Test929Search
+} // namespace art
diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/929-search/src-ex/A.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class A {
+}
\ No newline at end of file
diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/929-search/src/B.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class B {
+}
\ No newline at end of file
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
new file mode 100644
index 0000000..d253e6f
--- /dev/null
+++ b/test/929-search/src/Main.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ private static void doTest() throws Exception {
+ doTest(true, DEX1, "B");
+ doTest(false, DEX2, "A");
+ System.out.println("Done");
+ }
+
+ private static void doTest(boolean boot, String segment, String className) throws Exception {
+ ClassLoader expectedClassLoader;
+ if (boot) {
+ expectedClassLoader = Object.class.getClassLoader();
+ addToBootClassLoader(segment);
+ } else {
+ expectedClassLoader = ClassLoader.getSystemClassLoader();
+ addToSystemClassLoader(segment);
+ }
+
+ Class<?> c = Class.forName(className);
+ if (c.getClassLoader() != expectedClassLoader) {
+ throw new RuntimeException(className + "(" + boot + "/" + segment + "): " +
+ c.getClassLoader() + " vs " + expectedClassLoader);
+ }
+ }
+
+ private static native void addToBootClassLoader(String s);
+ private static native void addToSystemClassLoader(String s);
+
+ private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar";
+ private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar";
+}
diff --git a/test/Android.bp b/test/Android.bp
index 1ea1252..280cfbb 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -266,6 +266,7 @@
"923-monitors/monitors.cc",
"924-threads/threads.cc",
"927-timers/timers.cc",
+ "929-search/search.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 55cef97..6bdcc90 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -305,6 +305,7 @@
924-threads \
926-multi-obsolescence \
927-timers \
+ 929-search \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \