diff options
| -rw-r--r-- | runtime/Android.bp | 1 | ||||
| -rw-r--r-- | runtime/dex_file.cc | 55 | ||||
| -rw-r--r-- | runtime/dex_file.h | 22 | ||||
| -rw-r--r-- | runtime/dex_file_test.cc | 86 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc | 184 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h | 28 | ||||
| -rw-r--r-- | runtime/runtime.cc | 2 |
7 files changed, 341 insertions, 37 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index c4c7384902..22d79cb737 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -135,6 +135,7 @@ cc_defaults { "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 90c678c1a8..53838fd817 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -222,6 +222,10 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, 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 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, 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 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, /*low_4gb*/false, location, error_msg)); - if (map.get() == nullptr) { + if (map == nullptr) { DCHECK(!error_msg->empty()); return nullptr; } @@ -277,7 +306,9 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, 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 @@ bool DexFile::OpenZip(int fd, 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 @@ std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, *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 @@ std::unique_ptr<const DexFile> DexFile::OpenMemory(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, 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 @@ std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base, 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 767f9217d8..1200416540 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -422,10 +422,6 @@ class DexFile { 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 @@ class DexFile { 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 @@ class DexFile { // 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 @@ class DexFile { 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 2704d8a010..2328e3ddbb 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 @@ static const uint8_t kBase64Map[256] = { 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 @@ static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) { 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 @@ static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) { } } 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 const char kRawDexZeroLength[] = 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 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, 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 0000000000..08bf978537 --- /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 0000000000..f73d07a618 --- /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 8ea31a1a76..a365a737d8 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 @@ jobject Runtime::GetSystemClassLoader() const { 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); |