summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2018-11-22 14:57:28 +0000
committer Vladimir Marko <vmarko@google.com> 2018-12-06 14:43:07 +0000
commitd19085141ad9c71eae1b0ff585999ac8e27dadd4 (patch)
tree24284e84f5df4601bacc2a97c56af1bc5950c253
parent523940b40f9701504d6e007dd2af48f315038dc1 (diff)
Pass boot class path to ImageSpace::LoadBootImage.
When loading the boot image by ImageSpace, do not rely on the boot class path (BCP) recorded in the primary boot image oat file. Instead, check the BCP from image against the BCP provided by the Runtime, i.e. from -Xbootclasspath:_ or the environment variable BOOTCLASSPATH. For now, allow Runtime to extract the BCP from the primary boot image oat file when we have -Ximage:_ but no BCP. Also pass the boot class path components down to the OatFile so that we can open and use the original dex files when we do not have copies in the vdex files. This allows compiling app prebuilts when the boot dex files are uncompressed and aligned in the original jars. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: Pixel 2 XL boots. Test: m test-art-target-gtest Test: testrunner.py --target --optimizing Test: run-libcore-tests.sh --mode=device --variant=X64 Test: Manually create boot image jars with uncompressed and aligned dex files, compile a boot image with dex files not copied into vdex files and use this boot image for compiling a prebuilt services.{art,vdex,odex}. Bug: 119868597 Change-Id: If9411ddbecf6bb90bfa9233040931c5e833f6aee
-rw-r--r--cmdline/cmdline_parser_test.cc15
-rw-r--r--dex2oat/dex2oat.cc98
-rw-r--r--dex2oat/linker/image_test.h14
-rw-r--r--runtime/class_linker.cc23
-rw-r--r--runtime/gc/heap.cc6
-rw-r--r--runtime/gc/heap.h4
-rw-r--r--runtime/gc/space/image_space.cc246
-rw-r--r--runtime/gc/space/image_space.h19
-rw-r--r--runtime/interpreter/unstarted_runtime.cc11
-rw-r--r--runtime/jdwp/jdwp_handler.cc3
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc6
-rw-r--r--runtime/oat.h4
-rw-r--r--runtime/parsed_options.cc40
-rw-r--r--runtime/parsed_options_test.cc9
-rw-r--r--runtime/runtime.cc71
-rw-r--r--runtime/runtime.h13
-rw-r--r--runtime/runtime_options.def2
-rwxr-xr-xtools/run-libcore-tests.sh16
18 files changed, 273 insertions, 327 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 97daafadd8..101e5c42cd 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -63,6 +63,12 @@ namespace art {
return expected == actual;
}
+ template <char Separator>
+ bool UsuallyEquals(const std::vector<std::string>& expected,
+ const ParseStringList<Separator>& actual) {
+ return expected == static_cast<std::vector<std::string>>(actual);
+ }
+
// Try to use memcmp to compare simple plain-old-data structs.
//
// This should *not* generate false positives, but it can generate false negatives.
@@ -218,8 +224,13 @@ TEST_F(CmdlineParserTest, TestSimpleSuccesses) {
}
EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
- EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
- EXPECT_SINGLE_PARSE_VALUE("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello/world"}),
+ "-Xbootclasspath:/hello/world",
+ M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello", "/world"}),
+ "-Xbootclasspath:/hello:/world",
+ M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-classpath /hello/world", M::ClassPath);
EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index edd61897a9..9e0e61df6c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -103,6 +103,7 @@ namespace art {
using android::base::StringAppendV;
using android::base::StringPrintf;
+using gc::space::ImageSpace;
static constexpr size_t kDefaultMinDexFilesForSwap = 2;
static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
@@ -963,91 +964,24 @@ class Dex2Oat final {
}
void ExpandOatAndImageFilenames() {
- std::string base_oat = oat_filenames_[0];
- size_t last_oat_slash = base_oat.rfind('/');
- if (last_oat_slash == std::string::npos) {
- Usage("Unusable boot image oat filename %s", base_oat.c_str());
- }
- // We also need to honor path components that were encoded through '@'. Otherwise the loading
- // code won't be able to find the images.
- if (base_oat.find('@', last_oat_slash) != std::string::npos) {
- last_oat_slash = base_oat.rfind('@');
- }
- base_oat = base_oat.substr(0, last_oat_slash + 1);
-
- std::string base_img = image_filenames_[0];
- size_t last_img_slash = base_img.rfind('/');
- if (last_img_slash == std::string::npos) {
- Usage("Unusable boot image filename %s", base_img.c_str());
- }
- // We also need to honor path components that were encoded through '@'. Otherwise the loading
- // code won't be able to find the images.
- if (base_img.find('@', last_img_slash) != std::string::npos) {
- last_img_slash = base_img.rfind('@');
+ if (image_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
}
+ image_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, image_filenames_[0]);
- // Get the prefix, which is the primary image name (without path components). Strip the
- // extension.
- std::string prefix = base_img.substr(last_img_slash + 1);
- if (prefix.rfind('.') != std::string::npos) {
- prefix = prefix.substr(0, prefix.rfind('.'));
+ if (oat_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
}
- if (!prefix.empty()) {
- prefix = prefix + "-";
- }
-
- base_img = base_img.substr(0, last_img_slash + 1);
+ oat_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_filenames_[0]);
- std::string base_symbol_oat;
if (!oat_unstripped_.empty()) {
- base_symbol_oat = oat_unstripped_[0];
- size_t last_symbol_oat_slash = base_symbol_oat.rfind('/');
- if (last_symbol_oat_slash == std::string::npos) {
- Usage("Unusable boot image symbol filename %s", base_symbol_oat.c_str());
- }
- base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
- }
-
- // Now create the other names. Use a counted loop to skip the first one.
- for (size_t i = 1; i < dex_locations_.size(); ++i) {
- // TODO: Make everything properly std::string.
- std::string image_name = CreateMultiImageName(dex_locations_[i], prefix, ".art");
- char_backing_storage_.push_front(base_img + image_name);
- image_filenames_.push_back(char_backing_storage_.front().c_str());
-
- std::string oat_name = CreateMultiImageName(dex_locations_[i], prefix, ".oat");
- char_backing_storage_.push_front(base_oat + oat_name);
- oat_filenames_.push_back(char_backing_storage_.front().c_str());
-
- if (!base_symbol_oat.empty()) {
- char_backing_storage_.push_front(base_symbol_oat + oat_name);
- oat_unstripped_.push_back(char_backing_storage_.front().c_str());
+ if (oat_unstripped_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image symbol filename %s", oat_unstripped_[0].c_str());
}
+ oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_unstripped_[0]);
}
}
- // Modify the input string in the following way:
- // 0) Assume input is /a/b/c.d
- // 1) Strip the path -> c.d
- // 2) Inject prefix p -> pc.d
- // 3) Replace suffix with s if it's "jar" -> d == "jar" -> pc.s
- static std::string CreateMultiImageName(std::string in,
- const std::string& prefix,
- const char* replace_suffix) {
- size_t last_dex_slash = in.rfind('/');
- if (last_dex_slash != std::string::npos) {
- in = in.substr(last_dex_slash + 1);
- }
- if (!prefix.empty()) {
- in = prefix + in;
- }
- if (android::base::EndsWith(in, ".jar")) {
- in = in.substr(0, in.length() - strlen(".jar")) +
- (replace_suffix != nullptr ? replace_suffix : "");
- }
- return in;
- }
-
void InsertCompileOptions(int argc, char** argv) {
if (!avoid_storing_invocation_) {
std::ostringstream oss;
@@ -1496,11 +1430,8 @@ class Dex2Oat final {
if (IsBootImage()) {
// If we're compiling the boot image, store the boot classpath into the Key-Value store.
- // We need this for the multi-image case.
- key_value_store_->Put(OatHeader::kBootClassPathKey,
- gc::space::ImageSpace::GetMultiImageBootClassPath(dex_locations_,
- oat_filenames_,
- image_filenames_));
+ // We use this when loading the boot image.
+ key_value_store_->Put(OatHeader::kBootClassPathKey, android::base::Join(dex_locations_, ':'));
}
if (!IsBootImage()) {
@@ -1512,8 +1443,7 @@ class Dex2Oat final {
if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
- std::vector<gc::space::ImageSpace*> image_spaces =
- Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ std::vector<ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
boot_image_checksum_ = image_spaces[0]->GetImageHeader().GetImageChecksum();
} else {
boot_image_checksum_ = 0u;
@@ -1945,7 +1875,7 @@ class Dex2Oat final {
if (IsImage()) {
if (IsAppImage() && image_base_ == 0) {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
+ for (ImageSpace* image_space : heap->GetBootImageSpaces()) {
image_base_ = std::max(image_base_, RoundUp(
reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
kPageSize));
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 6ffcef12a7..41108448c4 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -169,10 +169,11 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
{
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
- for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
- std::string cur_location =
- android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
- out_helper.image_locations.push_back(ScratchFile(cur_location));
+ std::vector<std::string> image_locations =
+ gc::space::ImageSpace::ExpandMultiImageLocations(out_helper.dex_file_locations,
+ location.GetFilename() + ".art");
+ for (size_t i = 0u; i != class_path.size(); ++i) {
+ out_helper.image_locations.push_back(ScratchFile(image_locations[i]));
}
}
std::vector<std::string> image_filenames;
@@ -225,10 +226,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
t.NewTiming("WriteElf");
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kBootClassPathKey,
- gc::space::ImageSpace::GetMultiImageBootClassPath(
- out_helper.dex_file_locations,
- oat_filenames,
- image_filenames));
+ android::base::Join(out_helper.dex_file_locations, ':'));
std::vector<std::unique_ptr<ElfWriter>> elf_writers;
std::vector<std::unique_ptr<OatWriter>> oat_writers;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9b2e1a10e4..61966120ec 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1037,13 +1037,20 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) {
runtime->SetSentinel(heap->AllocNonMovableObject<true>(
self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
- for (gc::space::ImageSpace* image_space : spaces) {
+ const std::vector<std::string>& boot_class_path = runtime->GetBootClassPath();
+ if (boot_class_path.size() != spaces.size()) {
+ *error_msg = StringPrintf("Boot class path has %zu components but there are %zu image spaces.",
+ boot_class_path.size(),
+ spaces.size());
+ return false;
+ }
+ for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
// Boot class loader, use a null handle.
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!AddImageSpace(image_space,
+ if (!AddImageSpace(spaces[i],
ScopedNullHandle<mirror::ClassLoader>(),
- /*dex_elements=*/nullptr,
- /*dex_location=*/nullptr,
+ /*dex_elements=*/ nullptr,
+ /*dex_location=*/ boot_class_path[i].c_str(),
/*out*/&dex_files,
error_msg)) {
return false;
@@ -1981,13 +1988,7 @@ bool ClassLinker::AddImageSpace(
std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
// TODO: Only store qualified paths.
// If non qualified, qualify it.
- if (dex_file_location.find('/') == std::string::npos) {
- std::string dex_location_path = dex_location;
- const size_t pos = dex_location_path.find_last_of('/');
- CHECK_NE(pos, std::string::npos);
- dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/'
- dex_file_location = dex_location_path + dex_file_location;
- }
+ dex_file_location = OatFile::ResolveRelativeEncodedDexLocation(dex_location, dex_file_location);
std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
dex_file_location.c_str(),
error_msg);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 86135c1bc2..2f36d02eef 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -173,6 +173,8 @@ Heap::Heap(size_t initial_size,
double foreground_heap_growth_multiplier,
size_t capacity,
size_t non_moving_space_capacity,
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_file_name,
const InstructionSet image_instruction_set,
CollectorType foreground_collector_type,
@@ -350,7 +352,9 @@ Heap::Heap(size_t initial_size,
// Load image space(s).
std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces;
MemMap heap_reservation;
- if (space::ImageSpace::LoadBootImage(image_file_name,
+ if (space::ImageSpace::LoadBootImage(boot_class_path,
+ boot_class_path_locations,
+ image_file_name,
image_instruction_set,
heap_reservation_size,
&boot_image_spaces,
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 411a4469d9..8d81c11a43 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -174,7 +174,9 @@ class Heap {
double foreground_heap_growth_multiplier,
size_t capacity,
size_t non_moving_space_capacity,
- const std::string& original_image_file_name,
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
+ const std::string& image_file_name,
InstructionSet image_instruction_set,
CollectorType foreground_collector_type,
CollectorType background_collector_type,
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c772bdab18..d358f0acd5 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -103,9 +103,8 @@ static int32_t ChooseRelocationOffsetDelta() {
static bool GenerateImage(const std::string& image_filename,
InstructionSet image_isa,
std::string* error_msg) {
- const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
- std::vector<std::string> boot_class_path;
- Split(boot_class_path_string, ':', &boot_class_path);
+ Runtime* runtime = Runtime::Current();
+ const std::vector<std::string>& boot_class_path = runtime->GetBootClassPath();
if (boot_class_path.empty()) {
*error_msg = "Failed to generate image because no boot class path specified";
return false;
@@ -125,8 +124,11 @@ static bool GenerateImage(const std::string& image_filename,
image_option_string += image_filename;
arg_vector.push_back(image_option_string);
- for (size_t i = 0; i < boot_class_path.size(); i++) {
+ const std::vector<std::string>& boot_class_path_locations = runtime->GetBootClassPathLocations();
+ DCHECK_EQ(boot_class_path.size(), boot_class_path_locations.size());
+ for (size_t i = 0u; i < boot_class_path.size(); i++) {
arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]);
+ arg_vector.push_back(std::string("--dex-location=") + boot_class_path_locations[i]);
}
std::string oat_file_option_string("--oat-file=");
@@ -1185,8 +1187,13 @@ class ImageSpace::Loader {
class ImageSpace::BootImageLoader {
public:
- BootImageLoader(const std::string& image_location, InstructionSet image_isa)
- : image_location_(image_location),
+ BootImageLoader(const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
+ const std::string& image_location,
+ InstructionSet image_isa)
+ : boot_class_path_(boot_class_path),
+ boot_class_path_locations_(boot_class_path_locations),
+ image_location_(image_location),
image_isa_(image_isa),
is_zygote_(Runtime::Current()->IsZygote()),
has_system_(false),
@@ -1234,10 +1241,8 @@ class ImageSpace::BootImageLoader {
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
- std::vector<std::string> locations;
- if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) {
- return false;
- }
+ std::vector<std::string> locations =
+ ExpandMultiImageLocations(boot_class_path_locations_, image_location_);
uint32_t image_start;
uint32_t image_end;
if (!GetBootImageAddressRange(filename, &image_start, &image_end, error_msg)) {
@@ -1270,9 +1275,16 @@ class ImageSpace::BootImageLoader {
return false;
}
}
- for (std::unique_ptr<ImageSpace>& space : spaces) {
- static constexpr bool kValidateOatFile = false;
- if (!OpenOatFile(space.get(), kValidateOatFile, &logger, &image_reservation, error_msg)) {
+ for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
+ std::string expected_boot_class_path =
+ (i == 0u) ? android::base::Join(boot_class_path_locations_, ':') : std::string();
+ if (!OpenOatFile(spaces[i].get(),
+ boot_class_path_[i],
+ expected_boot_class_path,
+ /*validate_oat_file=*/ false,
+ &logger,
+ &image_reservation,
+ error_msg)) {
return false;
}
}
@@ -1301,10 +1313,8 @@ class ImageSpace::BootImageLoader {
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
DCHECK(DalvikCacheExists());
- std::vector<std::string> locations;
- if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) {
- return false;
- }
+ std::vector<std::string> locations =
+ ExpandMultiImageLocations(boot_class_path_locations_, image_location_);
uint32_t image_start;
uint32_t image_end;
if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, error_msg)) {
@@ -1346,8 +1356,16 @@ class ImageSpace::BootImageLoader {
return false;
}
}
- for (std::unique_ptr<ImageSpace>& space : spaces) {
- if (!OpenOatFile(space.get(), validate_oat_file, &logger, &image_reservation, error_msg)) {
+ for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
+ std::string expected_boot_class_path =
+ (i == 0u) ? android::base::Join(boot_class_path_locations_, ':') : std::string();
+ if (!OpenOatFile(spaces[i].get(),
+ boot_class_path_[i],
+ expected_boot_class_path,
+ validate_oat_file,
+ &logger,
+ &image_reservation,
+ error_msg)) {
return false;
}
}
@@ -1867,8 +1885,6 @@ class ImageSpace::BootImageLoader {
DCHECK(!spaces.empty());
ImageSpace* space = spaces[0].get();
const ImageHeader& image_header = space->GetImageHeader();
- // Use oat_file_non_owned_ from the `space` to set the runtime methods.
- runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet());
runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
runtime->SetImtUnimplementedMethod(
@@ -1932,6 +1948,8 @@ class ImageSpace::BootImageLoader {
}
bool OpenOatFile(ImageSpace* space,
+ const std::string& dex_filename,
+ const std::string& expected_boot_class_path,
bool validate_oat_file,
TimingLogger* logger,
/*inout*/MemMap* image_reservation,
@@ -1947,13 +1965,15 @@ class ImageSpace::BootImageLoader {
TimingLogger::ScopedTiming timing("OpenOatFile", logger);
std::string oat_filename =
ImageHeader::GetOatLocationFromImageLocation(space->GetImageFilename());
+ std::string oat_location =
+ ImageHeader::GetOatLocationFromImageLocation(space->GetImageLocation());
oat_file.reset(OatFile::Open(/*zip_fd=*/ -1,
oat_filename,
- oat_filename,
+ oat_location,
!Runtime::Current()->IsAotCompiler(),
/*low_4gb=*/ false,
- /*abs_dex_location=*/ nullptr,
+ /*abs_dex_location=*/ dex_filename.c_str(),
image_reservation,
error_msg));
if (oat_file == nullptr) {
@@ -1974,6 +1994,17 @@ class ImageSpace::BootImageLoader {
space->GetName());
return false;
}
+ const char* oat_boot_class_path =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ oat_boot_class_path = (oat_boot_class_path != nullptr) ? oat_boot_class_path : "";
+ if (expected_boot_class_path != oat_boot_class_path) {
+ *error_msg = StringPrintf("Failed to match oat boot class path %s to expected "
+ "boot class path %s in image %s",
+ oat_boot_class_path,
+ expected_boot_class_path.c_str(),
+ space->GetName());
+ return false;
+ }
ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin();
CHECK(image_header.GetOatDataBegin() != nullptr);
uint8_t* oat_data_begin = image_header.GetOatDataBegin() + relocation_diff;
@@ -1999,37 +2030,6 @@ class ImageSpace::BootImageLoader {
return true;
}
- // Extract boot class path from oat file associated with `image_filename`
- // and list all associated image locations.
- static bool GetBootClassPathImageLocations(const std::string& image_location,
- const std::string& image_filename,
- /*out*/ std::vector<std::string>* all_locations,
- /*out*/ std::string* error_msg) {
- std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_filename);
- std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
- oat_filename,
- oat_filename,
- /*executable=*/ false,
- /*low_4gb=*/ false,
- /*abs_dex_location=*/ nullptr,
- /*reservation=*/ nullptr,
- error_msg));
- if (oat_file == nullptr) {
- *error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s",
- oat_filename.c_str(),
- image_filename.c_str(),
- error_msg->c_str());
- return false;
- }
- const OatHeader& oat_header = oat_file->GetOatHeader();
- const char* boot_classpath = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
- all_locations->push_back(image_location);
- if (boot_classpath != nullptr && boot_classpath[0] != 0) {
- ExtractMultiImageLocations(image_location, boot_classpath, all_locations);
- }
- return true;
- }
-
bool GetBootImageAddressRange(const std::string& filename,
/*out*/uint32_t* start,
/*out*/uint32_t* end,
@@ -2096,6 +2096,8 @@ class ImageSpace::BootImageLoader {
return true;
}
+ const std::vector<std::string>& boot_class_path_;
+ const std::vector<std::string>& boot_class_path_locations_;
const std::string& image_location_;
InstructionSet image_isa_;
bool is_zygote_;
@@ -2143,6 +2145,8 @@ static bool CheckSpace(const std::string& cache_filename, std::string* error_msg
}
bool ImageSpace::LoadBootImage(
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_location,
const InstructionSet image_isa,
size_t extra_reservation_size,
@@ -2160,7 +2164,7 @@ bool ImageSpace::LoadBootImage(
return false;
}
- BootImageLoader loader(image_location, image_isa);
+ BootImageLoader loader(boot_class_path, boot_class_path_locations, image_location, image_isa);
// Step 0: Extra zygote work.
@@ -2321,57 +2325,6 @@ void ImageSpace::Dump(std::ostream& os) const {
<< ",name=\"" << GetName() << "\"]";
}
-std::string ImageSpace::GetMultiImageBootClassPath(
- const std::vector<std::string>& dex_locations,
- const std::vector<std::string>& oat_filenames,
- const std::vector<std::string>& image_filenames) {
- DCHECK_GT(oat_filenames.size(), 1u);
- // If the image filename was adapted (e.g., for our tests), we need to change this here,
- // too, but need to strip all path components (they will be re-established when loading).
- // For example, dex location
- // /system/framework/core-libart.art
- // with image name
- // out/target/product/taimen/dex_bootjars/system/framework/arm64/boot-core-libart.art
- // yields boot class path component
- // /system/framework/boot-core-libart.art .
- std::ostringstream bootcp_oss;
- bool first_bootcp = true;
- for (size_t i = 0; i < dex_locations.size(); ++i) {
- if (!first_bootcp) {
- bootcp_oss << ":";
- }
-
- std::string dex_loc = dex_locations[i];
- std::string image_filename = image_filenames[i];
-
- // Use the dex_loc path, but the image_filename name (without path elements).
- size_t dex_last_slash = dex_loc.rfind('/');
-
- // npos is max(size_t). That makes this a bit ugly.
- size_t image_last_slash = image_filename.rfind('/');
- size_t image_last_at = image_filename.rfind('@');
- size_t image_last_sep = (image_last_slash == std::string::npos)
- ? image_last_at
- : (image_last_at == std::string::npos)
- ? image_last_slash
- : std::max(image_last_slash, image_last_at);
- // Note: whenever image_last_sep == npos, +1 overflow means using the full string.
-
- if (dex_last_slash == std::string::npos) {
- dex_loc = image_filename.substr(image_last_sep + 1);
- } else {
- dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
- image_filename.substr(image_last_sep + 1);
- }
-
- // Image filenames already end with .art, no need to replace.
-
- bootcp_oss << dex_loc;
- first_bootcp = false;
- }
- return bootcp_oss.str();
-}
-
bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
const ArtDexFileLoader dex_file_loader;
for (const OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
@@ -2432,46 +2385,55 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg
return true;
}
-void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
- const std::string& boot_classpath,
- std::vector<std::string>* image_file_names) {
- DCHECK(image_file_names != nullptr);
+std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
+ const std::vector<std::string>& dex_locations,
+ const std::string& image_location) {
+ DCHECK(!dex_locations.empty());
+
+ // Find the path.
+ size_t last_slash = image_location.rfind('/');
+ CHECK_NE(last_slash, std::string::npos);
- std::vector<std::string> images;
- Split(boot_classpath, ':', &images);
+ // We also need to honor path components that were encoded through '@'. Otherwise the loading
+ // code won't be able to find the images.
+ if (image_location.find('@', last_slash) != std::string::npos) {
+ last_slash = image_location.rfind('@');
+ }
- // Add the rest into the list. We have to adjust locations, possibly:
- //
- // For example, image_file_name is /a/b/c/d/e.art
- // images[0] is f/c/d/e.art
- // ----------------------------------------------
- // images[1] is g/h/i/j.art -> /a/b/h/i/j.art
- const std::string& first_image = images[0];
- // Length of common suffix.
- size_t common = 0;
- while (common < input_image_file_name.size() &&
- common < first_image.size() &&
- *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) {
- ++common;
+ // Find the dot separating the primary image name from the extension.
+ size_t last_dot = image_location.rfind('.');
+ // Extract the extension and base (the path and primary image name).
+ std::string extension;
+ std::string base = image_location;
+ if (last_dot != std::string::npos && last_dot > last_slash) {
+ extension = image_location.substr(last_dot); // Including the dot.
+ base.resize(last_dot);
+ }
+ // For non-empty primary image name, add '-' to the `base`.
+ if (last_slash + 1u != base.size()) {
+ base += '-';
}
- // We want to replace the prefix of the input image with the prefix of the boot class path.
- // This handles the case where the image file contains @ separators.
- // Example image_file_name is oats/system@framework@boot.art
- // images[0] is .../arm/boot.art
- // means that the image name prefix will be oats/system@framework@
- // so that the other images are openable.
- const size_t old_prefix_length = first_image.size() - common;
- const std::string new_prefix = input_image_file_name.substr(
- 0,
- input_image_file_name.size() - common);
-
- // Apply pattern to images[1] .. images[n].
- for (size_t i = 1; i < images.size(); ++i) {
- const std::string& image = images[i];
- CHECK_GT(image.length(), old_prefix_length);
- std::string suffix = image.substr(old_prefix_length);
- image_file_names->push_back(new_prefix + suffix);
+
+ std::vector<std::string> locations;
+ locations.reserve(dex_locations.size());
+ locations.push_back(image_location);
+
+ // Now create the other names. Use a counted loop to skip the first one.
+ for (size_t i = 1u; i < dex_locations.size(); ++i) {
+ // Replace path with `base` (i.e. image path and prefix) and replace the original
+ // extension (if any) with `extension`.
+ std::string name = dex_locations[i];
+ size_t last_dex_slash = name.rfind('/');
+ if (last_dex_slash != std::string::npos) {
+ name = name.substr(last_dex_slash + 1);
+ }
+ size_t last_dex_dot = name.rfind('.');
+ if (last_dex_dot != std::string::npos) {
+ name.resize(last_dex_dot);
+ }
+ locations.push_back(base + name + extension);
}
+ return locations;
}
void ImageSpace::DumpSections(std::ostream& os) const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index aa45ed3952..05e7fa5cca 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -39,9 +39,11 @@ class ImageSpace : public MemMapSpace {
// Load boot image spaces from a primary image file for a specified instruction set.
//
// On successful return, the loaded spaces are added to boot_image_spaces (which must be
- // empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
- // oat file.
+ // empty on entry) and `extra_reservation` is set to the requested reservation located
+ // after the end of the last loaded oat file.
static bool LoadBootImage(
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_location,
const InstructionSet image_isa,
size_t extra_reservation_size,
@@ -122,15 +124,10 @@ class ImageSpace : public MemMapSpace {
bool* has_data,
bool *is_global_cache);
- // Use the input image filename to adapt the names in the given boot classpath to establish
- // complete locations for secondary images.
- static void ExtractMultiImageLocations(const std::string& input_image_file_name,
- const std::string& boot_classpath,
- std::vector<std::string>* image_filenames);
-
- static std::string GetMultiImageBootClassPath(const std::vector<std::string>& dex_locations,
- const std::vector<std::string>& oat_filenames,
- const std::vector<std::string>& image_filenames);
+ // Expand a single image location to multi-image locations based on the dex locations.
+ static std::vector<std::string> ExpandMultiImageLocations(
+ const std::vector<std::string>& dex_locations,
+ const std::string& image_location);
// Returns true if the dex checksums in the given oat file match the
// checksums of the original dex files on disk. This is intended to be used
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index e292a7612c..4fa7271ff4 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -571,12 +571,9 @@ static void GetResourceAsStream(Thread* self,
Runtime* runtime = Runtime::Current();
- std::vector<std::string> split;
- Split(runtime->GetBootClassPathString(), ':', &split);
- if (split.empty()) {
- AbortTransactionOrFail(self,
- "Boot classpath not set or split error:: %s",
- runtime->GetBootClassPathString().c_str());
+ const std::vector<std::string>& boot_class_path = Runtime::Current()->GetBootClassPath();
+ if (boot_class_path.empty()) {
+ AbortTransactionOrFail(self, "Boot classpath not set");
return;
}
@@ -584,7 +581,7 @@ static void GetResourceAsStream(Thread* self,
size_t map_size;
std::string last_error_msg; // Only store the last message (we could concatenate).
- for (const std::string& jar_file : split) {
+ for (const std::string& jar_file : boot_class_path) {
mem_map = FindAndExtractEntry(jar_file, resource_cstr, &map_size, &last_error_msg);
if (mem_map.IsValid()) {
break;
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index d31f166869..37365ff9b5 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -292,8 +292,7 @@ static JdwpError VM_ClassPaths(JdwpState*, Request*, ExpandBuf* pReply)
expandBufAddUtf8String(pReply, str);
}
- std::vector<std::string> boot_class_path;
- Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path);
+ std::vector<std::string> boot_class_path = Runtime::Current()->GetBootClassPath();
expandBufAdd4BE(pReply, boot_class_path.size());
for (const std::string& str : boot_class_path) {
expandBufAddUtf8String(pReply, str);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index e213dc79b8..3e5003ce13 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -24,7 +24,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version);
#include <limits.h>
#include "nativehelper/scoped_utf_chars.h"
-#include "android-base/stringprintf.h"
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "arch/instruction_set.h"
#include "art_method-inl.h"
@@ -222,7 +223,8 @@ static const char* DefaultToDot(const std::string& class_path) {
}
static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) {
- return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPathString()));
+ std::string boot_class_path = android::base::Join(Runtime::Current()->GetBootClassPath(), ':');
+ return env->NewStringUTF(DefaultToDot(boot_class_path));
}
static jstring VMRuntime_classPath(JNIEnv* env, jobject) {
diff --git a/runtime/oat.h b/runtime/oat.h
index ee46f426cf..b09c81e3ad 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Image checksums.
- static constexpr uint8_t kOatVersion[] = { '1', '6', '4', '\0' };
+ // Last oat version changed reason: Pass boot class path to LoadBootImage.
+ static constexpr uint8_t kOatVersion[] = { '1', '6', '5', '\0' };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 29b569050c..17ff3a244d 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -20,6 +20,7 @@
#include <sstream>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include "base/file_utils.h"
#include "base/macros.h"
@@ -78,7 +79,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define("-showversion")
.IntoKey(M::ShowVersion)
.Define("-Xbootclasspath:_")
- .WithType<std::string>()
+ .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by :
.IntoKey(M::BootClassPath)
.Define("-Xbootclasspath-locations:_")
.WithType<ParseStringList<':'>>() // std::vector<std::string>, split by :
@@ -513,7 +514,7 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
GetInstructionSetString(kRuntimeISA));
Exit(0);
} else if (args.Exists(M::BootClassPath)) {
- LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
+ LOG(INFO) << "setting boot class path to " << args.Get(M::BootClassPath)->Join();
}
if (args.GetOrDefault(M::Interpret)) {
@@ -525,8 +526,9 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
}
// Set a default boot class path if we didn't get an explicit one via command line.
- if (getenv("BOOTCLASSPATH") != nullptr) {
- args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
+ const char* env_bcp = getenv("BOOTCLASSPATH");
+ if (env_bcp != nullptr) {
+ args.SetIfMissing(M::BootClassPath, ParseStringList<':'>::Split(env_bcp));
}
// Set a default class path if we didn't get an explicit one via command line.
@@ -586,22 +588,20 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
}
- auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
- {
- auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
- auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
- if (args.Exists(M::BootClassPathLocations)) {
- size_t boot_class_path_count = ParseStringList<':'>::Split(boot_class_path).Size();
-
- if (boot_class_path_count != boot_class_path_locations.Size()) {
- Usage("The number of boot class path files does not match"
- " the number of boot class path locations given\n"
- " boot class path files (%zu): %s\n"
- " boot class path locations (%zu): %s\n",
- boot_class_path.size(), boot_class_path_string.c_str(),
- boot_class_path_locations.Size(), boot_class_path_locations.Join().c_str());
- return false;
- }
+ const ParseStringList<':'>* boot_class_path_locations = args.Get(M::BootClassPathLocations);
+ if (boot_class_path_locations != nullptr && boot_class_path_locations->Size() != 0u) {
+ const ParseStringList<':'>* boot_class_path = args.Get(M::BootClassPath);
+ if (boot_class_path == nullptr ||
+ boot_class_path_locations->Size() != boot_class_path->Size()) {
+ Usage("The number of boot class path files does not match"
+ " the number of boot class path locations given\n"
+ " boot class path files (%zu): %s\n"
+ " boot class path locations (%zu): %s\n",
+ (boot_class_path != nullptr) ? boot_class_path->Size() : 0u,
+ (boot_class_path != nullptr) ? boot_class_path->Join().c_str() : "<nil>",
+ boot_class_path_locations->Size(),
+ boot_class_path_locations->Join().c_str());
+ return false;
}
}
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 705cc6c5e2..cbb7b825d3 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -40,8 +40,7 @@ TEST_F(ParsedOptionsTest, ParsedOptions) {
boot_class_path += "-Xbootclasspath:";
bool first_dex_file = true;
- for (const std::string &dex_file_name :
- CommonRuntimeTest::GetLibCoreDexFileNames()) {
+ for (const std::string &dex_file_name : CommonRuntimeTest::GetLibCoreDexFileNames()) {
if (!first_dex_file) {
class_path += ":";
} else {
@@ -50,6 +49,8 @@ TEST_F(ParsedOptionsTest, ParsedOptions) {
class_path += dex_file_name;
}
boot_class_path += class_path;
+ std::vector<std::string> expected_boot_class_path;
+ Split(class_path, ':', &expected_boot_class_path);
RuntimeOptions options;
options.push_back(std::make_pair(boot_class_path.c_str(), nullptr));
@@ -78,9 +79,11 @@ TEST_F(ParsedOptionsTest, ParsedOptions) {
using Opt = RuntimeArgumentMap;
#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
+#define EXPECT_PARSED_EQ_AS_STRING_VECTOR(expected, actual_key) \
+ EXPECT_EQ(expected, static_cast<std::vector<std::string>>(map.GetOrDefault(actual_key)))
#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
- EXPECT_PARSED_EQ(class_path, Opt::BootClassPath);
+ EXPECT_PARSED_EQ_AS_STRING_VECTOR(expected_boot_class_path, Opt::BootClassPath);
EXPECT_PARSED_EQ(class_path, Opt::ClassPath);
EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
EXPECT_PARSED_EXISTS(Opt::CheckJni);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5323ceed37..83e3f546b3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1078,7 +1078,45 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold));
- boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+ image_location_ = runtime_options.GetOrDefault(Opt::Image);
+ SetInstructionSet(runtime_options.GetOrDefault(Opt::ImageInstructionSet));
+ boot_class_path_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+ boot_class_path_locations_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathLocations);
+ DCHECK(boot_class_path_locations_.empty() ||
+ boot_class_path_locations_.size() == boot_class_path_.size());
+ if (boot_class_path_.empty()) {
+ // Try to extract the boot class path from the system boot image.
+ if (image_location_.empty()) {
+ LOG(ERROR) << "Empty boot class path, cannot continue without image.";
+ return false;
+ }
+ std::string system_oat_filename = ImageHeader::GetOatLocationFromImageLocation(
+ GetSystemImageFilename(image_location_.c_str(), instruction_set_));
+ std::string system_oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location_);
+ std::string error_msg;
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
+ system_oat_filename,
+ system_oat_location,
+ /*executable=*/ false,
+ /*low_4gb=*/ false,
+ /*abs_dex_location=*/ nullptr,
+ /*reservation=*/ nullptr,
+ &error_msg));
+ if (oat_file == nullptr) {
+ LOG(ERROR) << "Could not open boot oat file for extracting boot class path: " << error_msg;
+ return false;
+ }
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ const char* oat_boot_class_path = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ if (oat_boot_class_path != nullptr) {
+ Split(oat_boot_class_path, ':', &boot_class_path_);
+ }
+ if (boot_class_path_.empty()) {
+ LOG(ERROR) << "Boot class path missing from boot image oat file " << oat_file->GetLocation();
+ return false;
+ }
+ }
+
class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
@@ -1104,7 +1142,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
}
}
image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
- image_location_ = runtime_options.GetOrDefault(Opt::Image);
max_spins_before_thin_lock_inflation_ =
runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
@@ -1173,8 +1210,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
foreground_heap_growth_multiplier,
runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
- runtime_options.GetOrDefault(Opt::Image),
- runtime_options.GetOrDefault(Opt::ImageInstructionSet),
+ GetBootClassPath(),
+ GetBootClassPathLocations(),
+ image_location_,
+ instruction_set_,
// Override the collector type to CC if the read barrier config.
kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
@@ -1374,16 +1413,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
image_space->VerifyImageAllocations();
}
}
- if (boot_class_path_string_.empty()) {
- // The bootclasspath is not explicitly specified: construct it from the loaded dex files.
- const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();
- std::vector<std::string> dex_locations;
- dex_locations.reserve(boot_class_path.size());
- for (const DexFile* dex_file : boot_class_path) {
- dex_locations.push_back(dex_file->GetLocation());
- }
- boot_class_path_string_ = android::base::Join(dex_locations, ':');
- }
{
ScopedTrace trace2("AddImageStringsToTable");
for (gc::space::ImageSpace* image_space : heap_->GetBootImageSpaces()) {
@@ -1396,24 +1425,12 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
DeoptimizeBootImage();
}
} else {
- std::vector<std::string> dex_filenames;
- Split(boot_class_path_string_, ':', &dex_filenames);
-
- std::vector<std::string> dex_locations;
- if (!runtime_options.Exists(Opt::BootClassPathLocations)) {
- dex_locations = dex_filenames;
- } else {
- dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);
- CHECK_EQ(dex_filenames.size(), dex_locations.size());
- }
-
std::vector<std::unique_ptr<const DexFile>> boot_class_path;
if (runtime_options.Exists(Opt::BootClassPathDexList)) {
boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList));
} else {
- OpenDexFiles(dex_filenames, dex_locations, &boot_class_path);
+ OpenDexFiles(GetBootClassPath(), GetBootClassPathLocations(), &boot_class_path);
}
- instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);
if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) {
LOG(ERROR) << "Could not initialize without image: " << error_msg;
return false;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1d58ad784c..64d33ac7a3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -242,8 +242,14 @@ class Runtime {
~Runtime();
- const std::string& GetBootClassPathString() const {
- return boot_class_path_string_;
+ const std::vector<std::string>& GetBootClassPath() const {
+ return boot_class_path_;
+ }
+
+ const std::vector<std::string>& GetBootClassPathLocations() const {
+ DCHECK(boot_class_path_locations_.empty() ||
+ boot_class_path_locations_.size() == boot_class_path_.size());
+ return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
}
const std::string& GetClassPathString() const {
@@ -859,7 +865,8 @@ class Runtime {
std::vector<std::string> image_compiler_options_;
std::string image_location_;
- std::string boot_class_path_string_;
+ std::vector<std::string> boot_class_path_;
+ std::vector<std::string> boot_class_path_locations_;
std::string class_path_string_;
std::vector<std::string> properties_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 5cec309453..2b2919eff4 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -37,7 +37,7 @@
RUNTIME_OPTIONS_KEY (Unit, Zygote)
RUNTIME_OPTIONS_KEY (Unit, Help)
RUNTIME_OPTIONS_KEY (Unit, ShowVersion)
-RUNTIME_OPTIONS_KEY (std::string, BootClassPath)
+RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPath) // std::vector<std::string>
RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations) // std::vector<std::string>
RUNTIME_OPTIONS_KEY (std::string, ClassPath)
RUNTIME_OPTIONS_KEY (std::string, Image)
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 2d39b2a9f8..63f1fce13f 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -46,6 +46,21 @@ function cparg {
done
}
+function boot_classpath_arg {
+ local dir="$1"
+ local suffix="$2"
+ shift 2
+ printf -- "--vm-arg -Xbootclasspath"
+ for var
+ do
+ printf -- ":${dir}/${var}${suffix}.jar";
+ done
+}
+
+# Note: This must match the TEST_CORE_JARS in Android.common_path.mk
+# because that's what we use for compiling the core.art image.
+BOOT_CLASSPATH_JARS="core-oj core-libart core-simple conscrypt okhttp bouncycastle"
+
DEPS="core-tests jsr166-tests mockito-target"
for lib in $DEPS
@@ -110,6 +125,7 @@ while true; do
if [[ "$1" == "--mode=device" ]]; then
device_mode=true
vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
+ vogar_args="$vogar_args $(boot_classpath_arg /system/framework -testdex $BOOT_CLASSPATH_JARS)"
shift
elif [[ "$1" == "--mode=host" ]]; then
# We explicitly give a wrong path for the image, to ensure vogar