From 7848da48a0a4241dedc1cc83ac4931e61575eb92 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 9 Apr 2015 11:15:04 -0700 Subject: [PATCH] ART: Store classpath information into oat file Store a "dependency list" of class-path dex-files into the key-value store of an oat file. The list is made up of dex locations and corresponding checksums. Add tests for encoding, decoding and checking the list. Bug: 19781184 Change-Id: Ie700dd37e6e086db599c95d329ac1f1d2ff0b758 --- build/Android.gtest.mk | 1 + dex2oat/dex2oat.cc | 5 +++ runtime/oat.h | 1 + runtime/oat_file.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ runtime/oat_file.h | 12 +++++++ runtime/oat_file_test.cc | 58 +++++++++++++++++++++++++++++++- 6 files changed, 163 insertions(+), 1 deletion(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index d9d09bcc6..7283710bf 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -67,6 +67,7 @@ ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested +ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY ART_GTEST_proxy_test_DEX_DEPS := Interfaces ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index eda7ec6f6..7e32b43e6 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1237,6 +1237,11 @@ class Dex2Oat FINAL { for (auto& class_path_file : class_path_files_) { class_path_files.push_back(class_path_file.get()); } + + // Store the classpath we have right now. + key_value_store_->Put(OatHeader::kClassPathKey, + OatFile::EncodeDexFileDependencies(class_path_files)); + // Then the dex files we'll compile. Thus we'll resolve the class-path first. class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); diff --git a/runtime/oat.h b/runtime/oat.h index de95fef55..a31e09a3c 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; + static constexpr const char* kClassPathKey = "classpath"; static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 81703b1b2..d3c4b4914 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include "base/bit_vector.h" @@ -592,4 +593,90 @@ bool OatFile::IsPic() const { // TODO: Check against oat_patches. b/18144996 } +static constexpr char kDexClassPathEncodingSeparator = '*'; + +std::string OatFile::EncodeDexFileDependencies(const std::vector& dex_files) { + std::ostringstream out; + + for (const DexFile* dex_file : dex_files) { + out << dex_file->GetLocation().c_str(); + out << kDexClassPathEncodingSeparator; + out << dex_file->GetLocationChecksum(); + out << kDexClassPathEncodingSeparator; + } + + return out.str(); +} + +bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) { + if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { + // No dependencies. + return true; + } + + // Assumption: this is not performance-critical. So it's OK to do this with a std::string and + // Split() instead of manual parsing of the combined char*. + std::vector split; + Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); + if (split.size() % 2 != 0) { + // Expected pairs of location and checksum. + *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies); + return false; + } + + for (auto it = split.begin(), end = split.end(); it != end; it += 2) { + std::string& location = *it; + std::string& checksum = *(it + 1); + int64_t converted = strtoll(checksum.c_str(), nullptr, 10); + if (converted == 0) { + // Conversion error. + *msg = StringPrintf("Conversion error for %s", checksum.c_str()); + return false; + } + + uint32_t dex_checksum; + std::string error_msg; + if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(), + &dex_checksum, + &error_msg)) { + if (converted != dex_checksum) { + *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u", + location.c_str(), converted, dex_checksum); + return false; + } + } else { + // Problem retrieving checksum. + // TODO: odex files? + *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(), + error_msg.c_str()); + return false; + } + } + + return true; +} + +bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies, + std::vector* locations) { + DCHECK(locations != nullptr); + if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { + return true; + } + + // Assumption: this is not performance-critical. So it's OK to do this with a std::string and + // Split() instead of manual parsing of the combined char*. + std::vector split; + Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); + if (split.size() % 2 != 0) { + // Expected pairs of location and checksum. + return false; + } + + for (auto it = split.begin(), end = split.end(); it != end; it += 2) { + locations->push_back(*it); + } + + return true; +} + } // namespace art diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 73a8c8e45..a5d5ae8a7 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -248,6 +248,18 @@ class OatFile FINAL { static std::string ResolveRelativeEncodedDexLocation( const char* abs_dex_location, const std::string& rel_dex_location); + // Create a dependency list (dex locations and checksums) for the given dex files. + static std::string EncodeDexFileDependencies(const std::vector& dex_files); + + // Check the given dependency list against their dex files - thus the name "Static," this does + // not check the class-loader environment, only whether there have been file updates. + static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg); + + // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic + // locations of multidex files. + static bool GetDexLocationsFromDependencies(const char* dex_dependencies, + std::vector* locations); + private: static void CheckLocation(const std::string& location); diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index f2213e987..a88553ca7 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -20,9 +20,15 @@ #include +#include "common_runtime_test.h" +#include "scoped_thread_state_change.h" + namespace art { -TEST(OatFileTest, ResolveRelativeEncodedDexLocation) { +class OatFileTest : public CommonRuntimeTest { +}; + +TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { EXPECT_EQ(std::string("/data/app/foo/base.apk"), OatFile::ResolveRelativeEncodedDexLocation( nullptr, "/data/app/foo/base.apk")); @@ -56,4 +62,54 @@ TEST(OatFileTest, ResolveRelativeEncodedDexLocation) { "/data/app/foo/base.apk", "o/base.apk")); } +static std::vector ToConstDexFiles( + const std::vector>& in) { + std::vector ret; + for (auto& d : in) { + ret.push_back(d.get()); + } + return ret; +} + +TEST_F(OatFileTest, DexFileDependencies) { + std::string error_msg; + + // No dependencies. + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg; + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg; + + // Ill-formed dependencies. + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg)); + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg)); + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg)); + + // Unsatisfiable dependency. + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg)); + + // Load some dex files to be able to do a real test. + ScopedObjectAccess soa(Thread::Current()); + + std::vector> dex_files1 = OpenTestDexFiles("Main"); + std::vector dex_files_const1 = ToConstDexFiles(dex_files1); + std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1); + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg)) + << error_msg << " " << encoding1; + std::vector split1; + EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1)); + ASSERT_EQ(split1.size(), 1U); + EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation()); + + std::vector> dex_files2 = OpenTestDexFiles("MultiDex"); + EXPECT_GT(dex_files2.size(), 1U); + std::vector dex_files_const2 = ToConstDexFiles(dex_files2); + std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2); + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg)) + << error_msg << " " << encoding2; + std::vector split2; + EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2)); + ASSERT_EQ(split2.size(), 2U); + EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation()); + EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation()); +} + } // namespace art -- 2.11.0