summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dex2oat/dex2oat.cc18
-rw-r--r--dex2oat/dex2oat_test.cc11
-rw-r--r--runtime/class_loader_context.cc31
-rw-r--r--runtime/class_loader_context.h31
-rw-r--r--runtime/class_loader_context_test.cc14
-rw-r--r--runtime/oat_file_assistant.cc31
-rw-r--r--runtime/oat_file_assistant.h11
-rw-r--r--runtime/oat_file_assistant_test.cc69
-rw-r--r--runtime/oat_file_manager.cc46
-rw-r--r--runtime/oat_file_manager.h17
-rw-r--r--tools/dexfuzz/src/dexfuzz/executors/Executor.java2
11 files changed, 200 insertions, 81 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e0ad64946e..6fbfdef4a2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1308,7 +1308,7 @@ class Dex2Oat FINAL {
} else if (option.starts_with("--class-loader-context=")) {
class_loader_context_ = ClassLoaderContext::Create(
option.substr(strlen("--class-loader-context=")).data());
- if (class_loader_context_== nullptr) {
+ if (class_loader_context_ == nullptr) {
Usage("Option --class-loader-context has an incorrect format: %s", option.data());
}
} else if (option.starts_with("--dirty-image-objects=")) {
@@ -1576,20 +1576,12 @@ class Dex2Oat FINAL {
}
// Open dex files for class path.
+
if (class_loader_context_ == nullptr) {
- // TODO(calin): Temporary workaround while we transition to use
- // --class-loader-context instead of --runtime-arg -cp
- if (runtime_->GetClassPathString().empty()) {
- class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
- new ClassLoaderContext());
- } else {
- std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
- ? OatFile::kSpecialSharedLibrary
- : "PCL[" + runtime_->GetClassPathString() + "]";
- class_loader_context_ = ClassLoaderContext::Create(spec);
- }
+ // If no context was specified use the default one (which is an empty PathClassLoader).
+ class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default());
}
- CHECK(class_loader_context_ != nullptr);
+
DCHECK_EQ(oat_writers_.size(), 1u);
// Note: Ideally we would reject context where the source dex files are also
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 32877a8cd6..12bceb3c1f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -134,7 +134,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
}
}
- // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+ // Check the input compiler filter against the generated oat file's filter. May be overridden
// in subclasses when equality is not expected.
virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
EXPECT_EQ(expected, actual);
@@ -153,14 +153,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- std::string class_path = runtime->GetClassPathString();
- if (class_path == "") {
- class_path = OatFile::kSpecialSharedLibrary;
- }
- argv.push_back(class_path);
+
if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 92d0f8d5ae..e7051b35d8 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -68,6 +68,10 @@ ClassLoaderContext::~ClassLoaderContext() {
}
}
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() {
+ return Create("");
+}
+
std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
if (result->Parse(spec)) {
@@ -263,7 +267,16 @@ bool ClassLoaderContext::RemoveLocationsFromClassPaths(
return removed_locations;
}
+std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const {
+ return EncodeContext(base_dir, /*for_dex2oat*/ true);
+}
+
std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
+ return EncodeContext(base_dir, /*for_dex2oat*/ false);
+}
+
+std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
+ bool for_dex2oat) const {
CheckDexFilesOpened("EncodeContextForOatFile");
if (special_shared_library_) {
return OatFile::kSpecialSharedLibrary;
@@ -286,8 +299,17 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_
}
out << GetClassLoaderTypeName(info.type);
out << kClassLoaderOpeningMark;
+ std::set<std::string> seen_locations;
for (size_t k = 0; k < info.opened_dex_files.size(); k++) {
const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k];
+ if (for_dex2oat) {
+ // dex2oat only needs the base location. It cannot accept multidex locations.
+ // So ensure we only add each file once.
+ bool new_insert = seen_locations.insert(dex_file->GetBaseLocation()).second;
+ if (!new_insert) {
+ continue;
+ }
+ }
const std::string& location = dex_file->GetLocation();
if (k > 0) {
out << kClasspathSeparator;
@@ -298,8 +320,11 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_
} else {
out << dex_file->GetLocation().c_str();
}
- out << kDexFileChecksumSeparator;
- out << dex_file->GetLocationChecksum();
+ // dex2oat does not need the checksums.
+ if (!for_dex2oat) {
+ out << kDexFileChecksumSeparator;
+ out << dex_file->GetLocationChecksum();
+ }
}
out << kClassLoaderClosingMark;
}
@@ -593,7 +618,7 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoa
}
}
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) {
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
ClassLoaderContext expected_context;
if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
LOG(WARNING) << "Invalid class loader context: " << context_spec;
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 37dd02b07f..9afa880da4 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -34,9 +34,6 @@ class OatFile;
// Utility class which holds the class loader context used during compilation/verification.
class ClassLoaderContext {
public:
- // Creates an empty context (with no class loaders).
- ClassLoaderContext();
-
~ClassLoaderContext();
// Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -82,9 +79,16 @@ class ClassLoaderContext {
// (so that it can be read and verified at runtime against the actual class
// loader hierarchy).
// Should only be called if OpenDexFiles() returned true.
- // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum".
+ // E.g. if the context is PCL[a.dex:b.dex] this will return
+ // "PCL[a.dex*a_checksum*b.dex*a_checksum]".
std::string EncodeContextForOatFile(const std::string& base_dir) const;
+ // Encodes the context as a string suitable to be passed to dex2oat.
+ // This is the same as EncodeContextForOatFile but without adding the checksums
+ // and only adding each dex files once (no multidex).
+ // Should only be called if OpenDexFiles() returned true.
+ std::string EncodeContextForDex2oat(const std::string& base_dir) const;
+
// Flattens the opened dex files into the given vector.
// Should only be called if OpenDexFiles() returned true.
std::vector<const DexFile*> FlattenOpenedDexFiles() const;
@@ -94,7 +98,7 @@ class ClassLoaderContext {
// - the number and type of the class loaders from the chain matches
// - the class loader from the same position have the same classpath
// (the order and checksum of the dex files matches)
- bool VerifyClassLoaderContextMatch(const std::string& context_spec);
+ bool VerifyClassLoaderContextMatch(const std::string& context_spec) const;
// Creates the class loader context from the given string.
// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
@@ -119,6 +123,10 @@ class ClassLoaderContext {
static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader,
jobjectArray dex_elements);
+ // Returns the default class loader context to be used when none is specified.
+ // This will return a context with a single and empty PathClassLoader.
+ static std::unique_ptr<ClassLoaderContext> Default();
+
private:
enum ClassLoaderType {
kInvalidClassLoader = 0,
@@ -144,6 +152,9 @@ class ClassLoaderContext {
explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
};
+ // Creates an empty context (with no class loaders).
+ ClassLoaderContext();
+
// Constructs an empty context.
// `owns_the_dex_files` specifies whether or not the context will own the opened dex files
// present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
@@ -173,7 +184,15 @@ class ClassLoaderContext {
bool AddInfoToContextFromClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Encodes the context as a string suitable to be passed to dex2oat or to be added to the
+ // oat file as the class path key.
+ // If for_dex2oat is true, the encoding adds each file once (i.e. it does not add multidex
+ // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex)
+ // together with their checksums.
+ // Should only be called if OpenDexFiles() returned true.
+ std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const;
// Extracts the class loader type from the given spec.
// Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 2b85188e38..d4688c1904 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -455,6 +455,20 @@ TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
}
+TEST_F(ClassLoaderContextTest, EncodeForDex2oat) {
+ std::string dex1_name = GetTestDexFileName("Main");
+ std::string dex2_name = GetTestDexFileName("MultiDex");
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+ std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
+ std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MultiDex");
+ std::string encoding = context->EncodeContextForDex2oat("");
+ std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]";
+ ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat(""));
+}
+
// TODO(calin) add a test which creates the context for a class loader together with dex_elements.
TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
// The chain is
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index dae41c1b67..3794f51cb7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -37,6 +37,7 @@
#include "scoped_thread_state_change-inl.h"
#include "utils.h"
#include "vdex_file.h"
+#include "class_loader_context.h"
namespace art {
@@ -225,13 +226,25 @@ bool OatFileAssistant::IsUpToDate() {
}
OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) {
+OatFileAssistant::MakeUpToDate(bool profile_changed,
+ const std::string& class_loader_context,
+ std::string* error_msg) {
CompilerFilter::Filter target;
if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
return kUpdateNotAttempted;
}
OatFileInfo& info = GetBestInfo();
+ // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291.
+ // This is actually not trivial in the current logic as it will interact with the collision
+ // check:
+ // - currently, if the context does not match but we have no collisions we still accept the
+ // oat file.
+ // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make
+ // the oat code up to date the collision check becomes useless.
+ // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
+ // loaded in other processes). So it boils down to how far do we want to complicate
+ // the logic in order to enable the use of oat files. Maybe its time to try simplify it.
switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) {
case kNoDexOptNeeded:
return kUpdateSucceeded;
@@ -243,7 +256,7 @@ OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) {
case kDex2OatForBootImage:
case kDex2OatForRelocation:
case kDex2OatForFilter:
- return GenerateOatFileNoChecks(info, target, error_msg);
+ return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg);
}
UNREACHABLE();
}
@@ -628,7 +641,10 @@ static bool PrepareOdexDirectories(const std::string& dex_location,
}
OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
- OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg) {
+ OatFileAssistant::OatFileInfo& info,
+ CompilerFilter::Filter filter,
+ const std::string& class_loader_context,
+ std::string* error_msg) {
CHECK(error_msg != nullptr);
Runtime* runtime = Runtime::Current();
@@ -704,6 +720,7 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe
args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
args.push_back("--oat-location=" + oat_file_name);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--class-loader-context=" + class_loader_context);
if (!Dex2Oat(args, error_msg)) {
// Manually delete the oat and vdex files. This ensures there is no garbage
@@ -743,14 +760,6 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- std::string class_path = runtime->GetClassPathString();
- if (class_path == "") {
- class_path = OatFile::kSpecialSharedLibrary;
- }
- argv.push_back(class_path);
if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 320aa4f860..5eec943689 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -185,12 +185,17 @@ class OatFileAssistant {
// profile_changed should be true to indicate the profile has recently
// changed for this dex location.
//
+ // If the dex files need to be made up to date, class_loader_context will be
+ // passed to dex2oat.
+ //
// Returns the result of attempting to update the code.
//
// If the result is not kUpdateSucceeded, the value of error_msg will be set
// to a string describing why there was a failure or the update was not
// attempted. error_msg must not be null.
- ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, std::string* error_msg);
+ ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
+ const std::string& class_loader_context,
+ std::string* error_msg);
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
@@ -389,7 +394,8 @@ class OatFileAssistant {
};
// Generate the oat file for the given info from the dex file using the
- // current runtime compiler options and the specified filter.
+ // current runtime compiler options, the specified filter and class loader
+ // context.
// This does not check the current status before attempting to generate the
// oat file.
//
@@ -398,6 +404,7 @@ class OatFileAssistant {
// attempted. error_msg must not be null.
ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
CompilerFilter::Filter target,
+ const std::string& class_loader_context,
std::string* error_msg);
// Return info for the best oat file.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c59dafcb38..e048177fa4 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -27,8 +27,10 @@
#include "art_field-inl.h"
#include "class_linker-inl.h"
+#include "class_loader_context.h"
#include "common_runtime_test.h"
#include "dexopt_test.h"
+#include "oat_file.h"
#include "oat_file_manager.h"
#include "os.h"
#include "scoped_thread_state_change-inl.h"
@@ -37,6 +39,8 @@
namespace art {
+static const std::string kSpecialSharedLibrary = "&";
+
class OatFileAssistantTest : public DexoptTest {};
class OatFileAssistantNoDex2OatTest : public DexoptTest {
@@ -116,7 +120,8 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) {
// Trying to make the oat file up to date should not fail or crash.
std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, oat_file_assistant.MakeUpToDate(false, &error_msg));
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -769,7 +774,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -949,7 +955,7 @@ TEST_F(OatFileAssistantTest, GenNoDex) {
// We should get kUpdateSucceeded from MakeUpToDate since there's nothing
// that can be done in this situation.
ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
// Verify it didn't create an oat in the default location (dalvik-cache).
OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
@@ -1028,7 +1034,7 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) {
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
@@ -1175,7 +1181,8 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) {
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
@@ -1183,7 +1190,8 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) {
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg))
+ << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
@@ -1191,7 +1199,7 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) {
Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
}
TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
@@ -1251,7 +1259,8 @@ TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) {
OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(default_filter));
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -1259,6 +1268,50 @@ TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) {
EXPECT_EQ(default_filter, oat_file->GetCompilerFilter());
}
+TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(default_filter));
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ EXPECT_EQ(kSpecialSharedLibrary,
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ std::string context_str = "PCL[" + context_location + "]";
+ int status = oat_file_assistant.MakeUpToDate(false, context_str, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(default_filter));
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create(context_str);
+ context->OpenDexFiles(kRuntimeISA, "");
+ EXPECT_EQ(context->EncodeContextForOatFile(""),
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index e950fca862..5baf59c665 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -329,12 +329,14 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
// Check for class-def collisions in dex files.
//
-// This first walks the class loader chain, getting all the dex files from the class loader. If
-// the class loader is null or one of the class loaders in the chain is unsupported, we collect
-// dex files from all open non-boot oat files to be safe.
+// This first walks the class loader chain present in the given context, getting all the dex files
+// from the class loader.
//
-// This first checks whether the shared libraries are in the expected order and the oat files
-// have the expected checksums. If so, we exit early. Otherwise, we do the collision check.
+// If the context is null (which means the initial class loader was null or unsupported)
+// this returns false. b/37777332.
+//
+// This first checks whether all class loaders in the context have the same type and
+// classpath. If so, we exit early. Otherwise, we do the collision check.
//
// The collision check works by maintaining a heap with one class from each dex file, sorted by the
// class descriptor. Then a dex-file/class pair is continually removed from the heap and compared
@@ -342,23 +344,11 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
// the two elements agree on whether their dex file was from an already-loaded oat-file or the
// new oat file. Any disagreement indicates a collision.
bool OatFileManager::HasCollisions(const OatFile* oat_file,
- jobject class_loader,
- jobjectArray dex_elements,
+ const ClassLoaderContext* context,
std::string* error_msg /*out*/) const {
DCHECK(oat_file != nullptr);
DCHECK(error_msg != nullptr);
- // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
- // directly with DexFile APIs instead of using class loaders.
- if (class_loader == nullptr) {
- LOG(WARNING) << "Opening an oat file without a class loader. "
- << "Are you using the deprecated DexFile APIs?";
- return false;
- }
-
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
-
// The context might be null if there are unrecognized class loaders in the chain or they
// don't meet sensible sanity conditions. In this case we assume that the app knows what it's
// doing and accept the oat file.
@@ -406,6 +396,17 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
+ std::unique_ptr<ClassLoaderContext> context;
+ // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
+ // directly with DexFile APIs instead of using class loaders.
+ if (class_loader == nullptr) {
+ LOG(WARNING) << "Opening an oat file without a class loader. "
+ << "Are you using the deprecated DexFile APIs?";
+ context = nullptr;
+ } else {
+ context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+ }
+
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
!runtime->IsAotCompiler());
@@ -425,7 +426,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
// Update the oat file on disk if we can, based on the --compiler-filter
// option derived from the current runtime options.
// This may fail, but that's okay. Best effort is all that matters here.
- switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
+
+ const std::string& dex2oat_context = context == nullptr
+ ? OatFile::kSpecialSharedLibrary
+ : context->EncodeContextForDex2oat(/*base_dir*/ "");
+ switch (oat_file_assistant.MakeUpToDate(
+ /*profile_changed*/false, dex2oat_context, /*out*/ &error_msg)) {
case OatFileAssistant::kUpdateFailed:
LOG(WARNING) << error_msg;
break;
@@ -451,7 +457,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
// Take the file only if it has no collisions, or we must take it because of preopting.
bool accept_oat_file =
- !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
+ !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
if (!accept_oat_file) {
// Failed the collision check. Print warning.
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 05a5f5b7b9..4523494c5c 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -35,6 +35,7 @@ class ImageSpace;
} // namespace space
} // namespace gc
+class ClassLoaderContext;
class DexFile;
class OatFile;
@@ -105,15 +106,17 @@ class OatFileManager {
void DumpForSigQuit(std::ostream& os);
private:
- // Check that the shared libraries in the given oat file match those in the given class loader and
- // dex elements. If the class loader is null or we do not support one of the class loaders in the
- // chain, compare against all non-boot oat files instead. If the shared libraries are not ok,
- // check for duplicate class definitions of the given oat file against the oat files (either from
- // the class loader and dex elements if possible or all non-boot oat files otherwise).
+ // Check that the class loader context of the given oat file matches the given context.
+ // This will perform a check that all class loaders in the chain have the same type and
+ // classpath.
+ // If the context is null (which means the initial class loader was null or unsupported)
+ // this returns false.
+ // If the context does not validate the method will check for duplicate class definitions of
+ // the given oat file against the oat files (either from the class loaders if possible or all
+ // non-boot oat files otherwise).
// Return true if there are any class definition collisions in the oat_file.
bool HasCollisions(const OatFile* oat_file,
- jobject class_loader,
- jobjectArray dex_elements,
+ const ClassLoaderContext* context,
/*out*/ std::string* error_msg) const
REQUIRES(!Locks::oat_file_manager_lock_);
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
index 074672d0ff..0367a83059 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -114,8 +114,6 @@ public abstract class Executor {
commandBuilder.append("--oat-file=output.oat ");
commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" ");
- commandBuilder.append("--runtime-arg -classpath ");
- commandBuilder.append("--runtime-arg ").append(programName).append(" ");
commandBuilder.append("--dex-file=").append(programName).append(" ");
commandBuilder.append("--compiler-filter=quicken --runtime-arg -Xnorelocate ");