Revert^2 "Add AddToDexClassloader JVMTI extension functions"
This reverts commit 799e536da9733ab638946f56e1ceb62d62cd3c81.
It seems that on some of our test devices the kernel does not have an
implementation for memfd_create. To work around this I added a basic
wrapper that will simulate memfd_create using temp files. This should
be sufficient for testing. All actual devices are expected to support
the memfd_create syscall natively.
Reason for revert: Implemented fallback for memfd_create
Bug: 132699522
Bug: 132914283
Test: ./test.py --host
Change-Id: I63b36464df24193fff27624c1e2350d65545ad1d
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index f12cb0a..a21a97f 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -42,6 +42,7 @@
#include "ti_heap.h"
#include "ti_logging.h"
#include "ti_monitor.h"
+#include "ti_search.h"
#include "thread-inl.h"
@@ -327,6 +328,48 @@
return error;
}
+ // AddToDexClassLoader
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(SearchUtil::AddToDexClassLoader),
+ "com.android.art.classloader.add_to_dex_class_loader",
+ "Adds a dexfile to a given dalvik.system.BaseDexClassLoader in a manner similar to"
+ " AddToSystemClassLoader.",
+ {
+ { "classloader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+ { "segment", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, false },
+ },
+ {
+ ERR(NULL_POINTER),
+ ERR(CLASS_LOADER_UNSUPPORTED),
+ ERR(ILLEGAL_ARGUMENT),
+ ERR(WRONG_PHASE),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ // AddToDexClassLoaderInMemory
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(SearchUtil::AddToDexClassLoaderInMemory),
+ "com.android.art.classloader.add_to_dex_class_loader_in_memory",
+ "Adds a dexfile buffer to a given dalvik.system.BaseDexClassLoader in a manner similar to"
+ " AddToSystemClassLoader. This may only be done during the LIVE phase. The buffer is copied"
+ " and the caller is responsible for deallocating it after this call.",
+ {
+ { "classloader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+ { "dex_bytes", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CCHAR, false },
+ { "dex_bytes_len", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ },
+ {
+ ERR(NULL_POINTER),
+ ERR(CLASS_LOADER_UNSUPPORTED),
+ ERR(ILLEGAL_ARGUMENT),
+ ERR(WRONG_PHASE),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 2187825..5654c22 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -29,6 +29,9 @@
* questions.
*/
+#include <sstream>
+#include <unistd.h>
+
#include "ti_search.h"
#include "jni.h"
@@ -37,6 +40,9 @@
#include "art_jvmti.h"
#include "base/enums.h"
#include "base/macros.h"
+#include "base/memfd.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file.h"
@@ -249,8 +255,121 @@
return ERR(NONE);
}
-jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
- const char* segment) {
+jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
+ jobject classloader,
+ const char* dex_bytes,
+ jint dex_bytes_length) {
+ if (jvmti_env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art::Thread::Current() == nullptr) {
+ return ERR(UNATTACHED_THREAD);
+ } else if (classloader == nullptr) {
+ return ERR(NULL_POINTER);
+ } else if (dex_bytes == nullptr) {
+ return ERR(NULL_POINTER);
+ } else if (dex_bytes_length <= 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+ // TODO We really should try to support doing this during the ON_LOAD phase.
+ if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
+ JVMTI_LOG(INFO, jvmti_env) << "Cannot add buffers to classpath during ON_LOAD phase to "
+ << "prevent file-descriptor leaking.";
+ return ERR(WRONG_PHASE);
+ }
+
+ // We have java APIs for adding files to the classpath, we might as well use them. It simplifies a
+ // lot of code as well.
+
+ // Create a memfd
+ art::File file(art::memfd_create_compat("JVMTI InMemory Added dex file", 0), /*check-usage*/true);
+ if (file.Fd() < 0) {
+ char* reason = strerror(errno);
+ JVMTI_LOG(ERROR, jvmti_env) << "Unable to create memfd due to " << reason;
+ return ERR(INTERNAL);
+ }
+ // Fill it with the buffer.
+ if (!file.WriteFully(dex_bytes, dex_bytes_length) || file.Flush() != 0) {
+ JVMTI_LOG(ERROR, jvmti_env) << "Failed to write to memfd!";
+ return ERR(INTERNAL);
+ }
+ // Get the filename in procfs.
+ std::ostringstream oss;
+ oss << "/proc/self/fd/" << file.Fd();
+ std::string seg(oss.str());
+ // Use common code.
+
+ jvmtiError result = AddToDexClassLoader(jvmti_env, classloader, seg.c_str());
+ // We have either loaded the dex file and have a new MemMap pointing to the same pages or loading
+ // has failed and the memory isn't needed anymore. Either way we can close the memfd we created
+ // and return.
+ if (file.Close() != 0) {
+ JVMTI_LOG(WARNING, jvmti_env) << "Failed to close memfd!";
+ }
+ return result;
+}
+
+jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env,
+ jobject classloader,
+ const char* segment) {
+ if (jvmti_env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art::Thread::Current() == nullptr) {
+ return ERR(UNATTACHED_THREAD);
+ } else if (classloader == nullptr) {
+ return ERR(NULL_POINTER);
+ } else if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+ // TODO We really should try to support doing this during the ON_LOAD phase.
+ if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
+ JVMTI_LOG(INFO, jvmti_env) << "Cannot add to classpath of arbitrary classloaders during "
+ << "ON_LOAD phase.";
+ 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(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
+ return ERR(CLASS_LOADER_UNSUPPORTED);
+ }
+
+ 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(classloader, add_dex_path_id, dex_path.get());
+
+ if (env->ExceptionCheck()) {
+ {
+ art::ScopedObjectAccess soa(self);
+ JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
+ << self->GetException()->Dump();
+ }
+ env->ExceptionClear();
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ return OK;
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const char* segment) {
if (segment == nullptr) {
return ERR(NULL_POINTER);
}
@@ -266,41 +385,18 @@
return ERR(WRONG_PHASE);
}
- jobject sys_class_loader = art::Runtime::Current()->GetSystemClassLoader();
- if (sys_class_loader == nullptr) {
- // This is unexpected.
+ jobject loader = art::Runtime::Current()->GetSystemClassLoader();
+ if (loader == nullptr) {
return ERR(INTERNAL);
}
- // 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)) {
+ if (!env->IsInstanceOf(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);
+ return AddToDexClassLoader(jvmti_env, loader, segment);
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_search.h b/openjdkjvmti/ti_search.h
index 81a28cc..b8d08bf 100644
--- a/openjdkjvmti/ti_search.h
+++ b/openjdkjvmti/ti_search.h
@@ -46,6 +46,12 @@
static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+ static jvmtiError AddToDexClassLoader(jvmtiEnv* env, jobject classloader, const char* segment);
+ static jvmtiError AddToDexClassLoaderInMemory(jvmtiEnv* env,
+ jobject classloader,
+ const char* dex_bytes,
+ jint dex_bytes_length);
};
} // namespace openjdkjvmti