Support for loading classes from DEX files stored in a ByteBuffer.
Bug: http://b/26878838
Test: test-art-target-gtest-dex_file_test
Change-Id: I68845c35784386bde6270102f0079d4f07bc4d03
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a8fb27a..2547359 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -135,6 +135,7 @@
"native_bridge_art_interface.cc",
"native_stack_dump.cc",
"native/dalvik_system_DexFile.cc",
+ "native/dalvik_system_InMemoryDexClassLoader_DexData.cc",
"native/dalvik_system_VMDebug.cc",
"native/dalvik_system_VMRuntime.cc",
"native/dalvik_system_VMStack.cc",
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 90c678c..53838fd 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -222,6 +222,10 @@
nullptr,
oat_dex_file,
error_msg);
+ if (dex_file == nullptr) {
+ return nullptr;
+ }
+
if (verify && !DexFileVerifier::Verify(dex_file.get(),
dex_file->Begin(),
dex_file->Size(),
@@ -230,7 +234,32 @@
error_msg)) {
return nullptr;
}
+ return dex_file;
+}
+std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
+ uint32_t location_checksum,
+ std::unique_ptr<MemMap> mem_map,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) {
+ ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
+ std::unique_ptr<const DexFile> dex_file = OpenMemory(location,
+ location_checksum,
+ std::move(mem_map),
+ error_msg);
+ if (dex_file == nullptr) {
+ return nullptr;
+ }
+
+ if (verify && !DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ verify_checksum,
+ error_msg)) {
+ return nullptr;
+ }
return dex_file;
}
@@ -263,7 +292,7 @@
/*low_4gb*/false,
location,
error_msg));
- if (map.get() == nullptr) {
+ if (map == nullptr) {
DCHECK(!error_msg->empty());
return nullptr;
}
@@ -277,7 +306,9 @@
const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
- std::unique_ptr<const DexFile> dex_file(OpenMemory(location, dex_header->checksum_, map.release(),
+ std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
+ dex_header->checksum_,
+ std::move(map),
error_msg));
if (dex_file.get() == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
@@ -314,13 +345,13 @@
std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
std::string* error_msg) {
return OpenMemory(mem_map->Begin(),
mem_map->Size(),
location,
location_checksum,
- mem_map,
+ std::move(mem_map),
nullptr,
error_msg);
}
@@ -350,9 +381,11 @@
*error_code = ZipOpenErrorCode::kExtractToMemoryError;
return nullptr;
}
- std::unique_ptr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(),
- error_msg));
- if (dex_file.get() == nullptr) {
+ std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
+ zip_entry->GetCrc32(),
+ std::move(map),
+ error_msg));
+ if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
error_msg->c_str());
*error_code = ZipOpenErrorCode::kDexFileError;
@@ -437,14 +470,14 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
const OatDexFile* oat_dex_file,
std::string* error_msg) {
DCHECK(base != nullptr);
DCHECK_NE(size, 0U);
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
std::unique_ptr<DexFile> dex_file(
- new DexFile(base, size, location, location_checksum, mem_map, oat_dex_file));
+ new DexFile(base, size, location, location_checksum, std::move(mem_map), oat_dex_file));
if (!dex_file->Init(error_msg)) {
dex_file.reset();
}
@@ -454,13 +487,13 @@
DexFile::DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
const OatDexFile* oat_dex_file)
: begin_(base),
size_(size),
location_(location),
location_checksum_(location_checksum),
- mem_map_(mem_map),
+ mem_map_(std::move(mem_map)),
header_(reinterpret_cast<const Header*>(base)),
string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 767f921..1200416 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -422,10 +422,6 @@
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files);
- // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
- // If this function returns false, Open will not succeed. The inverse is not true, however.
- static bool MaybeDex(const char* filename);
-
// Opens .dex file, backed by existing memory
static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
const std::string& location,
@@ -435,6 +431,18 @@
bool verify_checksum,
std::string* error_msg);
+ // Opens .dex file that has been memory-mapped by the caller.
+ static std::unique_ptr<const DexFile> Open(const std::string& location,
+ uint32_t location_checkum,
+ std::unique_ptr<MemMap> mem_map,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
+ // If this function returns false, Open will not succeed. The inverse is not true, however.
+ static bool MaybeDex(const char* filename);
+
// Open all classesXXX.dex files from a zip archive.
static bool OpenFromZip(const ZipArchive& zip_archive,
const std::string& location,
@@ -1175,7 +1183,7 @@
// Opens a .dex file at the given address backed by a MemMap
static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
std::string* error_msg);
// Opens a .dex file at the given address, optionally backed by a MemMap
@@ -1183,14 +1191,14 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
const OatDexFile* oat_dex_file,
std::string* error_msg);
DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map,
+ std::unique_ptr<MemMap> mem_map,
const OatDexFile* oat_dex_file);
// Top-level initializer that calls other Init methods.
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 2704d8a..2328e3d 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -22,6 +22,7 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "dex_file-inl.h"
+#include "mem_map.h"
#include "os.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -61,7 +62,7 @@
255, 255, 255, 255
};
-static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+static inline std::vector<uint8_t> DecodeBase64(const char* src) {
std::vector<uint8_t> tmp;
uint32_t t = 0, y = 0;
int g = 3;
@@ -73,13 +74,11 @@
c = 0;
// prevent g < 0 which would potentially allow an overflow later
if (--g < 0) {
- *dst_size = 0;
- return nullptr;
+ return std::vector<uint8_t>();
}
} else if (g != 3) {
// we only allow = to be at the end
- *dst_size = 0;
- return nullptr;
+ return std::vector<uint8_t>();
}
t = (t << 6) | c;
if (++y == 4) {
@@ -94,17 +93,9 @@
}
}
if (y != 0) {
- *dst_size = 0;
- return nullptr;
+ return std::vector<uint8_t>();
}
- std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
- if (dst_size != nullptr) {
- *dst_size = tmp.size();
- } else {
- *dst_size = 0;
- }
- std::copy(tmp.begin(), tmp.end(), dst.get());
- return dst.release();
+ return tmp;
}
// Although this is the same content logically as the Nested test dex,
@@ -175,14 +166,13 @@
static void DecodeAndWriteDexFile(const char* base64, const char* location) {
// decode base64
CHECK(base64 != nullptr);
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
- CHECK(dex_bytes.get() != nullptr);
+ std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+ CHECK_NE(dex_bytes.size(), 0u);
// write to provided file
std::unique_ptr<File> file(OS::CreateEmptyFile(location));
CHECK(file.get() != nullptr);
- if (!file->WriteFully(dex_bytes.get(), length)) {
+ if (!file->WriteFully(dex_bytes.data(), dex_bytes.size())) {
PLOG(FATAL) << "Failed to write base64 as dex file";
}
if (file->FlushCloseOrErase() != 0) {
@@ -208,9 +198,67 @@
return dex_file;
}
+static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
+ const char* location,
+ uint32_t location_checksum) {
+ CHECK(base64 != nullptr);
+ std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+ CHECK_NE(dex_bytes.size(), 0u);
+
+ std::string error_message;
+ std::unique_ptr<MemMap> region(MemMap::MapAnonymous("test-region",
+ nullptr,
+ dex_bytes.size(),
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &error_message));
+ memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size());
+ std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+ location_checksum,
+ std::move(region),
+ /* verify */ true,
+ /* verify_checksum */ true,
+ &error_message));
+ CHECK(dex_file != nullptr) << error_message;
+ return dex_file;
+}
+
TEST_F(DexFileTest, Header) {
ScratchFile tmp;
std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
+ ASSERT_TRUE(raw != nullptr);
+
+ const DexFile::Header& header = raw->GetHeader();
+ // TODO: header.magic_
+ EXPECT_EQ(0x00d87910U, header.checksum_);
+ // TODO: header.signature_
+ EXPECT_EQ(904U, header.file_size_);
+ EXPECT_EQ(112U, header.header_size_);
+ EXPECT_EQ(0U, header.link_size_);
+ EXPECT_EQ(0U, header.link_off_);
+ EXPECT_EQ(15U, header.string_ids_size_);
+ EXPECT_EQ(112U, header.string_ids_off_);
+ EXPECT_EQ(7U, header.type_ids_size_);
+ EXPECT_EQ(172U, header.type_ids_off_);
+ EXPECT_EQ(2U, header.proto_ids_size_);
+ EXPECT_EQ(200U, header.proto_ids_off_);
+ EXPECT_EQ(1U, header.field_ids_size_);
+ EXPECT_EQ(224U, header.field_ids_off_);
+ EXPECT_EQ(3U, header.method_ids_size_);
+ EXPECT_EQ(232U, header.method_ids_off_);
+ EXPECT_EQ(2U, header.class_defs_size_);
+ EXPECT_EQ(256U, header.class_defs_off_);
+ EXPECT_EQ(584U, header.data_size_);
+ EXPECT_EQ(320U, header.data_off_);
+
+ EXPECT_EQ(header.checksum_, raw->GetLocationChecksum());
+}
+
+TEST_F(DexFileTest, HeaderInMemory) {
+ ScratchFile tmp;
+ std::unique_ptr<const DexFile> raw =
+ OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U);
ASSERT_TRUE(raw.get() != nullptr);
const DexFile::Header& header = raw->GetHeader();
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
new file mode 100644
index 0000000..08bf978
--- /dev/null
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
+
+#include <sstream>
+
+#include "class_linker.h"
+#include "common_throws.h"
+#include "dex_file.h"
+#include "jni_internal.h"
+#include "mem_map.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+
+namespace art {
+
+static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
+ if (end <= start) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Bad range");
+ return nullptr;
+ }
+
+ std::string error_message;
+ size_t length = static_cast<size_t>(end - start);
+ std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
+ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &error_message));
+ if (dex_mem_map == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ }
+ return dex_mem_map;
+}
+
+static jlong DexFileToCookie(const DexFile* dex_file) {
+ return reinterpret_cast<jlong>(dex_file);
+}
+
+static const DexFile* CookieToDexFile(jlong cookie) {
+ return reinterpret_cast<const DexFile*>(cookie);
+}
+
+static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
+ std::string location = StringPrintf("InMemoryDexClassLoader_DexData@%p-%p",
+ dex_mem_map->Begin(),
+ dex_mem_map->End());
+ std::string error_message;
+ std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+ 0,
+ std::move(dex_mem_map),
+ /* verify */ true,
+ /* verify_location */ true,
+ &error_message));
+ if (dex_file == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ return nullptr;
+ }
+
+ if (!dex_file->DisableWrite()) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Failed to make dex file read-only");
+ return nullptr;
+ }
+
+ return dex_file.release();
+}
+
+static jlong InMemoryDexClassLoader_DexData_initializeWithDirectBuffer(
+ JNIEnv* env, jclass, jobject buffer, jint start, jint end) {
+ uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+ if (base_address == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("dexFileBuffer not direct");
+ return 0;
+ }
+
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ size_t length = static_cast<size_t>(end - start);
+ memcpy(dex_mem_map->Begin(), base_address, length);
+ return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
+}
+
+static jlong InMemoryDexClassLoader_DexData_initializeWithArray(
+ JNIEnv* env, jclass, jbyteArray buffer, jint start, jint end) {
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
+ env->GetByteArrayRegion(buffer, start, end - start, destination);
+ return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
+}
+
+static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlong cookie) {
+ const DexFile* dex_file = CookieToDexFile(cookie);
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(env);
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ CHECK(class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr);
+ }
+ delete dex_file;
+}
+
+static jclass InMemoryDexClassLoader_DexData_findClass(
+ JNIEnv* env, jobject dexData, jstring name, jobject loader, jlong cookie) {
+ ScopedUtfChars scoped_class_name(env, name);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ const char* class_name = scoped_class_name.c_str();
+ const std::string descriptor(DotToDescriptor(class_name));
+ const char* class_descriptor = descriptor.c_str();
+ const size_t hash = ComputeModifiedUtf8Hash(class_descriptor);
+ const DexFile* dex_file = CookieToDexFile(cookie);
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(class_descriptor, hash);
+ if (dex_class_def != nullptr) {
+ ScopedObjectAccess soa(env);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> handle_scope(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ handle_scope.NewHandle(soa.Decode<mirror::ClassLoader*>(loader)));
+ class_linker->RegisterDexFile(*dex_file, class_loader.Get());
+ mirror::Class* result = class_linker->DefineClass(
+ soa.Self(), class_descriptor, hash, class_loader, *dex_file, *dex_class_def);
+ if (result != nullptr) {
+ // Ensure the class table has a strong reference to the
+ // InMemoryClassLoader/DexData instance now that a class has
+ // been loaded.
+ class_linker->InsertDexFileInToClassLoader(
+ soa.Decode<mirror::Object*>(dexData), class_loader.Get());
+ return soa.AddLocalReference<jclass>(result);
+ }
+ }
+
+ VLOG(class_linker) << "Failed to find dex_class_def " << class_name;
+ return nullptr;
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(InMemoryDexClassLoader_DexData,
+ initializeWithDirectBuffer,
+ "(Ljava/nio/ByteBuffer;II)J"),
+ NATIVE_METHOD(InMemoryDexClassLoader_DexData, initializeWithArray, "([BII)J"),
+ NATIVE_METHOD(InMemoryDexClassLoader_DexData, uninitialize, "(J)V"),
+ NATIVE_METHOD(InMemoryDexClassLoader_DexData,
+ findClass,
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
+};
+
+void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env) {
+ REGISTER_NATIVE_METHODS("dalvik/system/InMemoryDexClassLoader$DexData");
+}
+
+} // namespace art
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
new file mode 100644
index 0000000..f73d07a
--- /dev/null
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env);
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8ea31a1..a365a73 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -90,6 +90,7 @@
#include "mirror/throwable.h"
#include "monitor.h"
#include "native/dalvik_system_DexFile.h"
+#include "native/dalvik_system_InMemoryDexClassLoader_DexData.h"
#include "native/dalvik_system_VMDebug.h"
#include "native/dalvik_system_VMRuntime.h"
#include "native/dalvik_system_VMStack.h"
@@ -1389,6 +1390,7 @@
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_dalvik_system_DexFile(env);
+ register_dalvik_system_InMemoryDexClassLoader_DexData(env);
register_dalvik_system_VMDebug(env);
register_dalvik_system_VMRuntime(env);
register_dalvik_system_VMStack(env);