Single-image boot image extensions.
Allow boot image extensions to be compiled into a single
art/oat/vdex triplet for any number of input dex files.
Support argument --image-fd so that this can be done with
file descriptors. This is intended for creating a boot
image extension in memory when starting zygote.
Remove the possibility to specify multiple oat or image
files. It was unused and untested.
Test: New test in dex2oat_image_test
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: I78e5410642a5df14c0891d87f27eefcb02156764
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e23bedf..345a8a0 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -251,6 +251,9 @@
UsageError(" --oat-file=<file.oat>: specifies an oat output destination via a filename.");
UsageError(" Example: --oat-file=/system/framework/boot.oat");
UsageError("");
+ UsageError(" --oat-symbols=<file.oat>: specifies a symbolized oat output destination.");
+ UsageError(" Example: --oat-file=symbols/system/framework/boot.oat");
+ UsageError("");
UsageError(" --oat-fd=<number>: specifies the oat output destination via a file descriptor.");
UsageError(" Example: --oat-fd=6");
UsageError("");
@@ -278,6 +281,9 @@
UsageError(" --image=<file.art>: specifies an output image filename.");
UsageError(" Example: --image=/system/framework/boot.art");
UsageError("");
+ UsageError(" --image-fd=<number>: same as --image but accepts a file descriptor instead.");
+ UsageError(" Cannot be used together with --image.");
+ UsageError("");
UsageError(" --image-format=(uncompressed|lz4|lz4hc):");
UsageError(" Which format to store the image.");
UsageError(" Example: --image-format=lz4");
@@ -430,7 +436,11 @@
UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art");
UsageError("");
- UsageError(" --multi-image: obsolete, ignored");
+ UsageError(" --multi-image: specify that separate oat and image files be generated for ");
+ UsageError(" each input dex file; the default for boot image and boot image extension.");
+ UsageError("");
+ UsageError(" --single-image: specify that a single oat and image file be generated for ");
+ UsageError(" all input dex files; the default for app image.");
UsageError("");
UsageError(" --force-determinism: force the compiler to emit a deterministic output.");
UsageError("");
@@ -706,6 +716,9 @@
input_vdex_file_(nullptr),
dm_fd_(-1),
zip_fd_(-1),
+ image_fd_(-1),
+ have_multi_image_arg_(false),
+ multi_image_(false),
image_base_(0U),
image_storage_mode_(ImageHeader::kStorageModeUncompressed),
passes_to_run_filename_(nullptr),
@@ -814,7 +827,7 @@
}
DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
- if (!image_filenames_.empty()) {
+ if (!image_filenames_.empty() || image_fd_ != -1) {
if (!boot_image_filename_.empty()) {
compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImageExtension;
} else if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
@@ -825,11 +838,15 @@
}
if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
if (compiler_options_->IsBootImage() || compiler_options_->IsBootImageExtension()) {
- Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ Usage("Can't have both (--image or --image-fd) and (--app-image-fd or --app-image-file)");
}
compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
}
+ if (!image_filenames_.empty() && image_fd_ != -1) {
+ Usage("Can't have both --image and --image-fd");
+ }
+
if (oat_filenames_.empty() && oat_fd_ == -1) {
Usage("Output must be supplied with either --oat-file or --oat-fd");
}
@@ -851,6 +868,10 @@
"or with --oat-fd and --output-vdex-fd file descriptors");
}
+ if ((image_fd_ != -1) && (oat_fd_ == -1)) {
+ Usage("--image-fd must be used with --oat_fd and --output_vdex_fd");
+ }
+
if (!parser_options->oat_symbols.empty() && oat_fd_ != -1) {
Usage("--oat-symbols should not be used with --oat-fd");
}
@@ -924,6 +945,25 @@
}
}
+ if (have_multi_image_arg_) {
+ if (!IsImage()) {
+ Usage("--multi-image or --single-image specified for non-image compilation");
+ }
+ } else {
+ // Use the default, i.e. multi-image for boot image and boot image extension.
+ multi_image_ = IsBootImage() || IsBootImageExtension(); // Shall pass checks below.
+ }
+ if (IsBootImage() && !multi_image_) {
+ Usage("--single-image specified for primary boot image");
+ }
+ if (IsAppImage() && multi_image_) {
+ Usage("--multi-image specified for app image");
+ }
+
+ if (image_fd_ != -1 && multi_image_) {
+ Usage("--single-image not specified for --image-fd");
+ }
+
const bool have_profile_file = !profile_file_.empty();
const bool have_profile_fd = profile_file_fd_ != kInvalidFd;
if (have_profile_file && have_profile_fd) {
@@ -1015,24 +1055,36 @@
}
void ExpandOatAndImageFilenames() {
- if (image_filenames_[0].rfind('/') == std::string::npos) {
- Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
+ ArrayRef<const std::string> locations(dex_locations_);
+ if (!multi_image_) {
+ locations = locations.SubArray(/*pos=*/ 0u, /*length=*/ 1u);
}
- image_filenames_ = ImageSpace::ExpandMultiImageLocations(
- ArrayRef<const std::string>(dex_locations_), image_filenames_[0], IsBootImageExtension());
+ if (image_fd_ == -1) {
+ if (image_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
+ }
+ image_filenames_ = ImageSpace::ExpandMultiImageLocations(
+ locations, image_filenames_[0], IsBootImageExtension());
- if (oat_filenames_[0].rfind('/') == std::string::npos) {
- Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
+ if (oat_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
+ }
+ oat_filenames_ = ImageSpace::ExpandMultiImageLocations(
+ locations, oat_filenames_[0], IsBootImageExtension());
+ } else {
+ DCHECK(!multi_image_);
+ std::vector<std::string> oat_locations = ImageSpace::ExpandMultiImageLocations(
+ locations, oat_location_, IsBootImageExtension());
+ DCHECK_EQ(1u, oat_locations.size());
+ oat_location_ = oat_locations[0];
}
- oat_filenames_ = ImageSpace::ExpandMultiImageLocations(
- ArrayRef<const std::string>(dex_locations_), oat_filenames_[0], IsBootImageExtension());
if (!oat_unstripped_.empty()) {
if (oat_unstripped_[0].rfind('/') == std::string::npos) {
Usage("Unusable boot image symbol filename %s", oat_unstripped_[0].c_str());
}
oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(
- ArrayRef<const std::string>(dex_locations_), oat_unstripped_[0], IsBootImageExtension());
+ locations, oat_unstripped_[0], IsBootImageExtension());
}
}
@@ -1111,6 +1163,15 @@
}
}
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<std::string>& key,
+ std::vector<std::string>* out) {
+ DCHECK(out->empty());
+ if (map.Exists(key)) {
+ out->push_back(*map.Get(key));
+ }
+ }
+
// Parse the arguments from the command line. In case of an unrecognized option or impossible
// values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
// returns, arguments have been successfully parsed.
@@ -1138,10 +1199,11 @@
AssignIfExists(args, M::CompactDexLevel, &compact_dex_level_);
AssignIfExists(args, M::DexFiles, &dex_filenames_);
AssignIfExists(args, M::DexLocations, &dex_locations_);
- AssignIfExists(args, M::OatFiles, &oat_filenames_);
+ AssignIfExists(args, M::OatFile, &oat_filenames_);
AssignIfExists(args, M::OatSymbols, &parser_options->oat_symbols);
AssignTrueIfExists(args, M::Strip, &strip_);
- AssignIfExists(args, M::ImageFilenames, &image_filenames_);
+ AssignIfExists(args, M::ImageFilename, &image_filenames_);
+ AssignIfExists(args, M::ImageFd, &image_fd_);
AssignIfExists(args, M::ZipFd, &zip_fd_);
AssignIfExists(args, M::ZipLocation, &zip_location_);
AssignIfExists(args, M::InputVdexFd, &input_vdex_fd_);
@@ -1198,6 +1260,9 @@
}
AssignIfExists(args, M::CopyDexFiles, ©_dex_files_);
+ AssignTrueIfExists(args, M::MultiImage, &have_multi_image_arg_);
+ AssignIfExists(args, M::MultiImage, &multi_image_);
+
if (args.Exists(M::ForceDeterminism)) {
force_determinism_ = true;
}
@@ -1266,8 +1331,9 @@
// Prune non-existent dex files now so that we don't create empty oat files for multi-image.
PruneNonExistentDexFiles();
- // Expand oat and image filenames for multi image.
- if ((IsBootImage() || IsBootImageExtension()) && image_filenames_.size() == 1) {
+ // Expand oat and image filenames for boot image and boot image extension.
+ // This is mostly for multi-image but single-image also needs some processing.
+ if (IsBootImage() || IsBootImageExtension()) {
ExpandOatAndImageFilenames();
}
@@ -2612,13 +2678,21 @@
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!IsBootImage() && !IsBootImageExtension()) {
- CHECK(image_filenames_.empty());
- image_filenames_.push_back(app_image_file_name_);
+ if (IsAppImage()) {
+ DCHECK(image_filenames_.empty());
+ if (app_image_fd_ != -1) {
+ image_filenames_.push_back(StringPrintf("FileDescriptor[%d]", app_image_fd_));
+ } else {
+ image_filenames_.push_back(app_image_file_name_);
+ }
}
- if (!image_writer_->Write(app_image_fd_,
+ if (image_fd_ != -1) {
+ DCHECK(image_filenames_.empty());
+ image_filenames_.push_back(StringPrintf("FileDescriptor[%d]", image_fd_));
+ }
+ if (!image_writer_->Write(IsAppImage() ? app_image_fd_ : image_fd_,
image_filenames_,
- oat_filenames_)) {
+ IsAppImage() ? 1u : dex_locations_.size())) {
LOG(ERROR) << "Failure during image file creation";
return false;
}
@@ -2781,6 +2855,9 @@
std::string boot_image_filename_;
std::vector<const char*> runtime_args_;
std::vector<std::string> image_filenames_;
+ int image_fd_;
+ bool have_multi_image_arg_;
+ bool multi_image_;
uintptr_t image_base_;
ImageHeader::StorageMode image_storage_mode_;
const char* passes_to_run_filename_;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index f03f02c..a80db55 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -198,7 +198,8 @@
bool CompileBootImage(const std::vector<std::string>& extra_args,
const std::string& image_file_name_prefix,
ArrayRef<const std::string> dex_files,
- std::string* error_msg) {
+ std::string* error_msg,
+ const std::string& use_fd_prefix = "") {
Runtime* const runtime = Runtime::Current();
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
@@ -219,9 +220,22 @@
argv.push_back("--host");
}
- argv.push_back("--image=" + image_file_name_prefix + ".art");
- argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
- argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
+ std::unique_ptr<File> art_file;
+ std::unique_ptr<File> vdex_file;
+ std::unique_ptr<File> oat_file;
+ if (!use_fd_prefix.empty()) {
+ art_file.reset(OS::CreateEmptyFile((use_fd_prefix + ".art").c_str()));
+ vdex_file.reset(OS::CreateEmptyFile((use_fd_prefix + ".vdex").c_str()));
+ oat_file.reset(OS::CreateEmptyFile((use_fd_prefix + ".oat").c_str()));
+ argv.push_back("--image-fd=" + std::to_string(art_file->Fd()));
+ argv.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd()));
+ argv.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
+ argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
+ } else {
+ argv.push_back("--image=" + image_file_name_prefix + ".art");
+ argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
+ argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
+ }
std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
@@ -232,7 +246,17 @@
argv.push_back("--android-root=" + std::string(android_root));
argv.insert(argv.end(), extra_args.begin(), extra_args.end());
- return RunDex2Oat(argv, error_msg);
+ bool result = RunDex2Oat(argv, error_msg);
+ if (art_file != nullptr) {
+ CHECK_EQ(0, art_file->FlushClose());
+ }
+ if (vdex_file != nullptr) {
+ CHECK_EQ(0, vdex_file->FlushClose());
+ }
+ if (oat_file != nullptr) {
+ CHECK_EQ(0, oat_file->FlushClose());
+ }
+ return result;
}
bool RunDex2Oat(const std::vector<std::string>& args, std::string* error_msg) {
@@ -277,6 +301,20 @@
dex_file = new_location;
}
}
+
+ bool CompareFiles(const std::string& filename1, const std::string& filename2) {
+ std::unique_ptr<File> file1(OS::OpenFileForReading(filename1.c_str()));
+ std::unique_ptr<File> file2(OS::OpenFileForReading(filename2.c_str()));
+ // Did we open the files?
+ if (file1 == nullptr || file2 == nullptr) {
+ return false;
+ }
+ // Are they non-empty and the same length?
+ if (file1->GetLength() <= 0 || file2->GetLength() != file1->GetLength()) {
+ return false;
+ }
+ return file1->Compare(file2.get()) == 0;
+ }
};
TEST_F(Dex2oatImageTest, TestModesAndFilters) {
@@ -601,4 +639,142 @@
ASSERT_EQ(0, rmdir_result);
}
+TEST_F(Dex2oatImageTest, TestExtensionSingleImage) {
+ std::string error_msg;
+ MemMap reservation = ReserveCoreImageAddressSpace(&error_msg);
+ ASSERT_TRUE(reservation.IsValid()) << error_msg;
+
+ ScratchFile scratch;
+ std::string scratch_dir = scratch.GetFilename() + "-d";
+ int mkdir_result = mkdir(scratch_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ scratch_dir += '/';
+ std::string image_dir = scratch_dir + GetInstructionSetString(kRuntimeISA);
+ mkdir_result = mkdir(image_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ std::string filename_prefix = image_dir + "/core";
+
+ // Copy the libcore dex files to a custom dir inside `scratch_dir` so that we do not
+ // accidentally load pre-compiled core images from their original directory based on BCP paths.
+ std::string jar_dir = scratch_dir + "jars";
+ mkdir_result = mkdir(jar_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ jar_dir += '/';
+ std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
+ CopyDexFiles(jar_dir, &libcore_dex_files);
+
+ // Create a smaller profile compared to the TestExtension test.
+ ScratchFile profile_file;
+ GenerateProfile(libcore_dex_files,
+ profile_file.GetFile(),
+ /*method_frequency=*/ 5u,
+ /*type_frequency=*/ 4u);
+ std::vector<std::string> extra_args;
+ extra_args.push_back("--profile-file=" + profile_file.GetFilename());
+
+ ArrayRef<const std::string> full_bcp(libcore_dex_files);
+ size_t total_dex_files = full_bcp.size();
+ ASSERT_GE(total_dex_files, 5u); // 3 for "head", at least 2 for "tail".
+
+ // The primary image must contain at least core-oj and core-libart to initialize the runtime
+ // and we also need the core-icu4j if we want to compile these with full profile.
+ ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
+ ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
+ ASSERT_NE(std::string::npos, full_bcp[2].find("core-icu4j"));
+ ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 3u);
+ ArrayRef<const std::string> tail_dex_files = full_bcp.SubArray(/*pos=*/ 3u);
+
+ // Prepare the "head" and "tail" names and locations.
+ std::string base_name = "core.art";
+ std::string base_location = scratch_dir + base_name;
+ CHECK_GE(tail_dex_files.size(), 2u);
+ std::vector<std::string> expanded_tail = gc::space::ImageSpace::ExpandMultiImageLocations(
+ tail_dex_files.SubArray(/*pos=*/ 0u, /*length=*/ 1u),
+ base_location,
+ /*boot_image_extension=*/ true);
+ CHECK_EQ(1u, expanded_tail.size());
+ std::string tail_location = expanded_tail[0];
+ size_t tail_slash_pos = tail_location.rfind('/');
+ ASSERT_NE(std::string::npos, tail_slash_pos);
+ std::string tail_name = tail_location.substr(tail_slash_pos + 1u);
+
+ // Compile the "head", i.e. the primary boot image.
+ extra_args.push_back(android::base::StringPrintf("--base=0x%08x", kBaseAddress));
+ bool head_ok = CompileBootImage(extra_args, filename_prefix, head_dex_files, &error_msg);
+ ASSERT_TRUE(head_ok) << error_msg;
+ extra_args.pop_back();
+
+ // Compile the "tail" against the primary boot image.
+ std::string full_bcp_string = android::base::Join(full_bcp, ':');
+ AddRuntimeArg(extra_args, "-Xbootclasspath:" + full_bcp_string);
+ AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + full_bcp_string);
+ extra_args.push_back("--boot-image=" + base_location);
+ extra_args.push_back("--single-image");
+ extra_args.push_back("--avoid-storing-invocation"); // For comparison below.
+ error_msg.clear();
+ bool tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
+ ASSERT_TRUE(tail_ok) << error_msg;
+
+ reservation = MemMap::Invalid(); // Free the reserved memory for loading images.
+
+ // Try to load the boot image with different image locations.
+ std::vector<std::string> boot_class_path = libcore_dex_files;
+ std::vector<std::unique_ptr<gc::space::ImageSpace>> boot_image_spaces;
+ bool relocate = false;
+ MemMap extra_reservation;
+ auto load = [&](const std::string& image_location) {
+ boot_image_spaces.clear();
+ extra_reservation = MemMap::Invalid();
+ ScopedObjectAccess soa(Thread::Current());
+ return gc::space::ImageSpace::LoadBootImage(/*boot_class_path=*/ boot_class_path,
+ /*boot_class_path_locations=*/ libcore_dex_files,
+ image_location,
+ kRuntimeISA,
+ gc::space::ImageSpaceLoadingOrder::kSystemFirst,
+ relocate,
+ /*executable=*/ true,
+ /*is_zygote=*/ false,
+ /*extra_reservation_size=*/ 0u,
+ &boot_image_spaces,
+ &extra_reservation);
+ };
+
+ for (bool r : { false, true }) {
+ relocate = r;
+
+ // Load the primary and first extension with full path.
+ bool load_ok = load(base_location + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(head_dex_files.size() + 1u, boot_image_spaces.size());
+
+ // Load the primary with full path and extension with a specified search path.
+ load_ok = load(base_location + ':' + scratch_dir + '*');
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(head_dex_files.size() + 1u, boot_image_spaces.size());
+ }
+
+ // Recompile using file descriptors and compare contents.
+ std::vector<std::string> expanded_oat_filename = gc::space::ImageSpace::ExpandMultiImageLocations(
+ tail_dex_files.SubArray(/*pos=*/ 0u, /*length=*/ 1u),
+ filename_prefix,
+ /*boot_image_extension=*/ true);
+ CHECK_EQ(1u, expanded_oat_filename.size());
+ std::string extension_filename_prefix = expanded_oat_filename[0];
+ std::string filename_prefix2 = extension_filename_prefix + "2";
+ error_msg.clear();
+ tail_ok = CompileBootImage(extra_args,
+ filename_prefix,
+ tail_dex_files,
+ &error_msg,
+ /*use_fd_prefix=*/ filename_prefix2);
+ ASSERT_TRUE(tail_ok) << error_msg;
+ EXPECT_TRUE(CompareFiles(extension_filename_prefix + ".art", filename_prefix2 + ".art"));
+ EXPECT_TRUE(CompareFiles(extension_filename_prefix + ".vdex", filename_prefix2 + ".vdex"));
+ EXPECT_TRUE(CompareFiles(extension_filename_prefix + ".oat", filename_prefix2 + ".oat"));
+
+ ClearDirectory(scratch_dir.c_str());
+ int rmdir_result = rmdir(scratch_dir.c_str());
+ ASSERT_EQ(0, rmdir_result);
+}
+
} // namespace art
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 29a288d..58944ff 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -93,10 +93,10 @@
.WithType<std::string>()
.IntoKey(M::DmFile)
.Define("--oat-file=_")
- .WithType<std::vector<std::string>>().AppendValues()
- .IntoKey(M::OatFiles)
+ .WithType<std::string>()
+ .IntoKey(M::OatFile)
.Define("--oat-symbols=_")
- .WithType<std::vector<std::string>>().AppendValues()
+ .WithType<std::string>()
.IntoKey(M::OatSymbols)
.Define("--strip")
.IntoKey(M::Strip)
@@ -111,8 +111,11 @@
static void AddImageMappings(Builder& builder) {
builder.
Define("--image=_")
- .WithType<std::vector<std::string>>().AppendValues()
- .IntoKey(M::ImageFilenames)
+ .WithType<std::string>()
+ .IntoKey(M::ImageFilename)
+ .Define("--image-fd=_")
+ .WithType<int>()
+ .IntoKey(M::ImageFd)
.Define("--base=_")
.WithType<std::string>()
.IntoKey(M::Base)
@@ -122,7 +125,8 @@
.Define("--app-image-fd=_")
.WithType<int>()
.IntoKey(M::AppImageFileFd)
- .Define("--multi-image")
+ .Define({"--multi-image", "--single-image"})
+ .WithValues({true, false})
.IntoKey(M::MultiImage)
.Define("--dirty-image-objects=_")
.WithType<std::string>()
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index ad28f3a..f48806c 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -45,8 +45,8 @@
DEX2OAT_OPTIONS_KEY (std::string, OutputVdex)
DEX2OAT_OPTIONS_KEY (int, DmFd)
DEX2OAT_OPTIONS_KEY (std::string, DmFile)
-DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatFiles)
-DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatSymbols)
+DEX2OAT_OPTIONS_KEY (std::string, OatFile)
+DEX2OAT_OPTIONS_KEY (std::string, OatSymbols)
DEX2OAT_OPTIONS_KEY (Unit, Strip)
DEX2OAT_OPTIONS_KEY (int, OatFd)
DEX2OAT_OPTIONS_KEY (std::string, OatLocation)
@@ -54,7 +54,8 @@
DEX2OAT_OPTIONS_KEY (int, WatchdogTimeout)
DEX2OAT_OPTIONS_KEY (unsigned int, Threads)
DEX2OAT_OPTIONS_KEY (std::vector<std::int32_t>, CpuSet)
-DEX2OAT_OPTIONS_KEY (std::vector<std::string>, ImageFilenames)
+DEX2OAT_OPTIONS_KEY (std::string, ImageFilename)
+DEX2OAT_OPTIONS_KEY (int, ImageFd)
DEX2OAT_OPTIONS_KEY (ImageHeader::StorageMode, ImageFormat)
DEX2OAT_OPTIONS_KEY (std::string, Passes)
DEX2OAT_OPTIONS_KEY (std::string, Base) // TODO: Hex string parsing.
@@ -79,7 +80,7 @@
DEX2OAT_OPTIONS_KEY (unsigned int, VeryLargeAppThreshold)
DEX2OAT_OPTIONS_KEY (std::string, AppImageFile)
DEX2OAT_OPTIONS_KEY (int, AppImageFileFd)
-DEX2OAT_OPTIONS_KEY (Unit, MultiImage)
+DEX2OAT_OPTIONS_KEY (bool, MultiImage)
DEX2OAT_OPTIONS_KEY (std::string, NoInlineFrom)
DEX2OAT_OPTIONS_KEY (Unit, ForceDeterminism)
DEX2OAT_OPTIONS_KEY (std::string, ClasspathDir)
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 69e144c..ef296fc 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -345,7 +345,7 @@
bool success_image = writer->Write(kInvalidFd,
image_filenames,
- oat_filenames);
+ image_filenames.size());
ASSERT_TRUE(success_image);
for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) {
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 3d998c9..0facc85 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -401,21 +401,21 @@
bool ImageWriter::Write(int image_fd,
const std::vector<std::string>& image_filenames,
- const std::vector<std::string>& oat_filenames) {
+ size_t component_count) {
// If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or
// oat_filenames.
CHECK(!image_filenames.empty());
if (image_fd != kInvalidFd) {
CHECK_EQ(image_filenames.size(), 1u);
}
- CHECK(!oat_filenames.empty());
- CHECK_EQ(image_filenames.size(), oat_filenames.size());
+ DCHECK(!oat_filenames_.empty());
+ CHECK_EQ(image_filenames.size(), oat_filenames_.size());
Thread* const self = Thread::Current();
{
ScopedObjectAccess soa(self);
- for (size_t i = 0; i < oat_filenames.size(); ++i) {
- CreateHeader(i);
+ for (size_t i = 0; i < oat_filenames_.size(); ++i) {
+ CreateHeader(i, component_count);
CopyAndFixupNativeData(i);
}
}
@@ -444,15 +444,12 @@
ImageInfo& image_info = GetImageInfo(i);
ImageFileGuard image_file;
if (image_fd != kInvalidFd) {
- if (image_filename.empty()) {
- image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage));
- // Empty the file in case it already exists.
- if (image_file != nullptr) {
- TEMP_FAILURE_RETRY(image_file->SetLength(0));
- TEMP_FAILURE_RETRY(image_file->Flush());
- }
- } else {
- LOG(ERROR) << "image fd " << image_fd << " name " << image_filename;
+ // Ignore image_filename, it is supplied only for better diagnostic.
+ image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage));
+ // Empty the file in case it already exists.
+ if (image_file != nullptr) {
+ TEMP_FAILURE_RETRY(image_file->SetLength(0));
+ TEMP_FAILURE_RETRY(image_file->Flush());
}
} else {
image_file.reset(OS::CreateEmptyFile(image_filename.c_str()));
@@ -2640,7 +2637,7 @@
return make_pair(metadata_section.End(), std::move(sections));
}
-void ImageWriter::CreateHeader(size_t oat_index) {
+void ImageWriter::CreateHeader(size_t oat_index, size_t component_count) {
ImageInfo& image_info = GetImageInfo(oat_index);
const uint8_t* oat_file_begin = image_info.oat_file_begin_;
const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_;
@@ -2648,18 +2645,23 @@
uint32_t image_reservation_size = image_info.image_size_;
DCHECK_ALIGNED(image_reservation_size, kPageSize);
- uint32_t component_count = 1u;
- if (!compiler_options_.IsAppImage()) {
+ uint32_t current_component_count = 1u;
+ if (compiler_options_.IsAppImage()) {
+ DCHECK_EQ(oat_index, 0u);
+ DCHECK_EQ(component_count, current_component_count);
+ } else {
+ DCHECK(image_infos_.size() == 1u || image_infos_.size() == component_count)
+ << image_infos_.size() << " " << component_count;
if (oat_index == 0u) {
const ImageInfo& last_info = image_infos_.back();
const uint8_t* end = last_info.oat_file_begin_ + last_info.oat_loaded_size_;
DCHECK_ALIGNED(image_info.image_begin_, kPageSize);
image_reservation_size =
dchecked_integral_cast<uint32_t>(RoundUp(end - image_info.image_begin_, kPageSize));
- component_count = image_infos_.size();
+ current_component_count = component_count;
} else {
image_reservation_size = 0u;
- component_count = 0u;
+ current_component_count = 0u;
}
}
@@ -2669,13 +2671,13 @@
if (oat_index == 0u) {
const std::vector<gc::space::ImageSpace*>& image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
- boot_image_components = dchecked_integral_cast<uint32_t>(image_spaces.size());
- DCHECK_EQ(boot_image_components == 0u, compiler_options_.IsBootImage());
- for (uint32_t i = 0; i != boot_image_components; ) {
+ DCHECK_EQ(image_spaces.empty(), compiler_options_.IsBootImage());
+ for (size_t i = 0u, size = image_spaces.size(); i != size; ) {
const ImageHeader& header = image_spaces[i]->GetImageHeader();
+ boot_image_components += header.GetComponentCount();
boot_image_checksums ^= header.GetImageChecksum();
- DCHECK_LE(header.GetComponentCount(), boot_image_components - i);
- i += header.GetComponentCount();
+ DCHECK_LE(header.GetImageSpaceCount(), size - i);
+ i += header.GetImageSpaceCount();
}
}
@@ -2709,7 +2711,7 @@
// image.
new (image_info.image_.Begin()) ImageHeader(
image_reservation_size,
- component_count,
+ current_component_count,
PointerToLowMemUInt32(image_info.image_begin_),
image_end,
sections.data(),
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 22b7739..811b5c3 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -144,7 +144,7 @@
// the names in oat_filenames.
bool Write(int image_fd,
const std::vector<std::string>& image_filenames,
- const std::vector<std::string>& oat_filenames)
+ size_t component_count)
REQUIRES(!Locks::mutator_lock_);
uintptr_t GetOatDataBegin(size_t oat_index) {
@@ -470,7 +470,7 @@
// Lays out where the image objects will be at runtime.
void CalculateNewObjectOffsets()
REQUIRES_SHARED(Locks::mutator_lock_);
- void CreateHeader(size_t oat_index)
+ void CreateHeader(size_t oat_index, size_t component_count)
REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<mirror::ObjectArray<mirror::Object>> CollectDexCaches(Thread* self, size_t oat_index) const
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8d94bea..eea3084 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -178,12 +178,12 @@
for (size_t i = 0u, num_spaces = image_spaces.size(); i != num_spaces; ) {
const ImageHeader& image_header = image_spaces[i]->GetImageHeader();
uint32_t reservation_size = image_header.GetImageReservationSize();
- uint32_t component_count = image_header.GetComponentCount();
+ uint32_t image_count = image_header.GetImageSpaceCount();
- CHECK_NE(component_count, 0u);
- CHECK_LE(component_count, num_spaces - i);
+ CHECK_NE(image_count, 0u);
+ CHECK_LE(image_count, num_spaces - i);
CHECK_NE(reservation_size, 0u);
- for (size_t j = 1u; j != image_header.GetComponentCount(); ++j) {
+ for (size_t j = 1u; j != image_count; ++j) {
CHECK_EQ(image_spaces[i + j]->GetImageHeader().GetComponentCount(), 0u);
CHECK_EQ(image_spaces[i + j]->GetImageHeader().GetImageReservationSize(), 0u);
}
@@ -193,7 +193,7 @@
// Check contiguous layout of images and oat files.
const uint8_t* current_heap = image_spaces[i]->Begin();
const uint8_t* current_oat = image_spaces[i]->GetImageHeader().GetOatFileBegin();
- for (size_t j = 0u; j != image_header.GetComponentCount(); ++j) {
+ for (size_t j = 0u; j != image_count; ++j) {
const ImageHeader& current_header = image_spaces[i + j]->GetImageHeader();
CHECK_EQ(current_heap, image_spaces[i + j]->Begin());
CHECK_EQ(current_oat, current_header.GetOatFileBegin());
@@ -207,7 +207,7 @@
CHECK_EQ(reservation_size, static_cast<size_t>(current_oat - image_spaces[i]->Begin()));
boot_image_size += reservation_size;
- i += component_count;
+ i += image_count;
}
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c4f1c2e..de4f93f 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -966,8 +966,9 @@
}
uint32_t checksum = 0u;
size_t chunk_count = 0u;
+ size_t space_pos = 0u;
for (size_t component_count = 0u; component_count != boot_image_component_count; ) {
- const ImageHeader& current_header = image_spaces[component_count]->GetImageHeader();
+ const ImageHeader& current_header = image_spaces[space_pos]->GetImageHeader();
if (current_header.GetComponentCount() > boot_image_component_count - component_count) {
*error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, "
"%u is between %zu and %zu",
@@ -980,6 +981,7 @@
component_count += current_header.GetComponentCount();
checksum ^= current_header.GetImageChecksum();
chunk_count += 1u;
+ space_pos += current_header.GetImageSpaceCount();
}
if (image_header.GetBootImageChecksum() != checksum) {
*error_msg = StringPrintf("Boot image checksum mismatch (0x%x != 0x%x) in image %s",
@@ -1481,10 +1483,12 @@
std::string base_location;
std::string base_filename;
size_t start_index;
- size_t component_count;
- size_t reservation_size;
+ uint32_t component_count;
+ uint32_t image_space_count;
+ uint32_t reservation_size;
uint32_t checksum;
uint32_t boot_image_component_count;
+ uint32_t boot_image_space_count;
uint32_t boot_image_checksum;
};
@@ -1568,6 +1572,7 @@
bool ValidateBootImageChecksum(const std::string& actual_filename,
const ImageHeader& header,
+ /*out*/uint32_t* boot_image_space_count,
/*out*/std::string* error_msg);
bool ReadHeader(const std::string& base_location,
@@ -1750,6 +1755,7 @@
bool ImageSpace::BootImageLayout::ValidateBootImageChecksum(const std::string& actual_filename,
const ImageHeader& header,
+ /*out*/uint32_t* boot_image_space_count,
/*out*/std::string* error_msg) {
uint32_t boot_image_component_count = header.GetBootImageComponentCount();
if (chunks_.empty() != (boot_image_component_count == 0u)) {
@@ -1761,6 +1767,7 @@
}
uint32_t component_count = 0u;
uint32_t composite_checksum = 0u;
+ *boot_image_space_count = 0u;
for (const ImageChunk& chunk : chunks_) {
if (component_count == boot_image_component_count) {
break; // Hit the component count.
@@ -1770,7 +1777,7 @@
}
if (chunk.component_count > boot_image_component_count - component_count) {
*error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, "
- "%u is between %u and %zu",
+ "%u is between %u and %u",
actual_filename.c_str(),
boot_image_component_count,
component_count,
@@ -1779,6 +1786,7 @@
}
component_count += chunk.component_count;
composite_checksum ^= chunk.checksum;
+ *boot_image_space_count += chunk.image_space_count;
}
DCHECK_LE(component_count, boot_image_component_count);
if (component_count != boot_image_component_count) {
@@ -1830,7 +1838,8 @@
allowed_reservation_size);
return false;
}
- if (!ValidateBootImageChecksum(actual_filename, header, error_msg)) {
+ uint32_t boot_image_space_count;
+ if (!ValidateBootImageChecksum(actual_filename, header, &boot_image_space_count, error_msg)) {
return false;
}
@@ -1842,9 +1851,11 @@
chunk.base_filename = base_filename;
chunk.start_index = bcp_index;
chunk.component_count = header.GetComponentCount();
+ chunk.image_space_count = header.GetImageSpaceCount();
chunk.reservation_size = header.GetImageReservationSize();
chunk.checksum = header.GetImageChecksum();
chunk.boot_image_component_count = header.GetBootImageComponentCount();
+ chunk.boot_image_space_count = boot_image_space_count;
chunk.boot_image_checksum = header.GetBootImageChecksum();
chunks_.push_back(std::move(chunk));
next_bcp_index_ = bcp_index + header.GetComponentCount();
@@ -2308,22 +2319,22 @@
spaces.front()->Begin(),
spaces.back()->End() - spaces.front()->Begin()));
const ImageHeader& base_header = spaces[0]->GetImageHeader();
- size_t base_component_count = base_header.GetComponentCount();
- DCHECK_LE(base_component_count, spaces.size());
+ size_t base_image_space_count = base_header.GetImageSpaceCount();
+ DCHECK_LE(base_image_space_count, spaces.size());
DoRelocateSpaces<kPointerSize, /*kExtension=*/ false>(
- spaces.SubArray(/*pos=*/ 0u, base_component_count),
+ spaces.SubArray(/*pos=*/ 0u, base_image_space_count),
base_diff64,
&patched_objects);
- for (size_t i = base_component_count, size = spaces.size(); i != size; ) {
+ for (size_t i = base_image_space_count, size = spaces.size(); i != size; ) {
const ImageHeader& ext_header = spaces[i]->GetImageHeader();
- size_t ext_component_count = ext_header.GetComponentCount();
- DCHECK_LE(ext_component_count, size - i);
+ size_t ext_image_space_count = ext_header.GetImageSpaceCount();
+ DCHECK_LE(ext_image_space_count, size - i);
DoRelocateSpaces<kPointerSize, /*kExtension=*/ true>(
- spaces.SubArray(/*pos=*/ i, ext_component_count),
+ spaces.SubArray(/*pos=*/ i, ext_image_space_count),
base_diff64,
&patched_objects);
- i += ext_component_count;
+ i += ext_image_space_count;
}
}
@@ -2741,7 +2752,7 @@
}
ArrayRef<const std::string> requested_bcp_locations =
ArrayRef<const std::string>(boot_class_path_locations_).SubArray(
- chunk.start_index, chunk.component_count);
+ chunk.start_index, chunk.image_space_count);
std::vector<std::string> locations =
ExpandMultiImageLocations(requested_bcp_locations, chunk.base_location, is_extension);
std::vector<std::string> filenames =
@@ -2761,14 +2772,18 @@
}
const ImageHeader& header = space->GetImageHeader();
if (i == 0 && (chunk.checksum != header.GetImageChecksum() ||
+ chunk.image_space_count != header.GetImageSpaceCount() ||
chunk.boot_image_component_count != header.GetBootImageComponentCount() ||
chunk.boot_image_checksum != header.GetBootImageChecksum())) {
*error_msg = StringPrintf("Image header modified since previously read from %s; "
"checksum: 0x%08x -> 0x%08x,"
+ "image_space_count: %u -> %u"
"boot_image_component_count: %u -> %u, "
"boot_image_checksum: 0x%08x -> 0x%08x",
space->GetImageFilename().c_str(),
chunk.checksum,
+ chunk.image_space_count,
+ header.GetImageSpaceCount(),
header.GetImageChecksum(),
chunk.boot_image_component_count,
header.GetBootImageComponentCount(),
@@ -2782,9 +2797,10 @@
ArrayRef<const std::unique_ptr<ImageSpace>>(*spaces).SubArray(
/*pos=*/ 0u, chunk.boot_image_component_count);
for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
- ImageSpace* space = (*spaces)[spaces->size() - chunk.component_count + i].get();
+ ImageSpace* space = (*spaces)[spaces->size() - chunk.image_space_count + i].get();
+ size_t bcp_chunk_size = (chunk.image_space_count == 1u) ? chunk.component_count : 1u;
if (!OpenOatFile(space,
- boot_class_path_.SubArray(/*pos=*/ chunk.start_index + i, /*length=*/ 1u),
+ boot_class_path_.SubArray(/*pos=*/ chunk.start_index + i, bcp_chunk_size),
validate_oat_file,
dependencies,
logger,
@@ -3220,15 +3236,16 @@
DCHECK_EQ(main_space->oat_file_non_owned_->GetOatDexFiles()[0]->GetDexFileLocation(),
boot_class_path[bcp_pos]->GetLocation());
const ImageHeader& current_header = main_space->GetImageHeader();
- uint32_t component_count = current_header.GetComponentCount();
- DCHECK_NE(component_count, 0u);
- DCHECK_LE(component_count, image_spaces.size() - image_pos);
+ uint32_t image_space_count = current_header.GetImageSpaceCount();
+ DCHECK_NE(image_space_count, 0u);
+ DCHECK_LE(image_space_count, image_spaces.size() - image_pos);
if (image_pos != 0u) {
boot_image_checksum += ':';
}
+ uint32_t component_count = current_header.GetComponentCount();
AppendImageChecksum(component_count, current_header.GetImageChecksum(), &boot_image_checksum);
- for (size_t component_index = 0; component_index != component_count; ++component_index) {
- const ImageSpace* space = image_spaces[image_pos + component_index];
+ for (size_t space_index = 0; space_index != image_space_count; ++space_index) {
+ const ImageSpace* space = image_spaces[image_pos + space_index];
const OatFile* oat_file = space->oat_file_non_owned_;
size_t num_dex_files = oat_file->GetOatDexFiles().size();
if (kIsDebugBuild) {
@@ -3241,7 +3258,7 @@
}
bcp_pos += num_dex_files;
}
- image_pos += component_count;
+ image_pos += image_space_count;
}
ArrayRef<const DexFile* const> boot_class_path_tail =
@@ -3426,13 +3443,15 @@
}
// Verify image checksums.
+ size_t bcp_pos = 0u;
size_t image_pos = 0u;
while (image_pos != num_image_spaces && StartsWith(oat_checksums, "i")) {
// Verify the current image checksum.
const ImageHeader& current_header = image_spaces[image_pos]->GetImageHeader();
+ uint32_t image_space_count = current_header.GetImageSpaceCount();
+ DCHECK_NE(image_space_count, 0u);
+ DCHECK_LE(image_space_count, image_spaces.size() - image_pos);
uint32_t component_count = current_header.GetComponentCount();
- DCHECK_NE(component_count, 0u);
- DCHECK_LE(component_count, image_spaces.size() - image_pos);
uint32_t checksum = current_header.GetImageChecksum();
if (!CheckAndRemoveImageChecksum(component_count, checksum, &oat_checksums, error_msg)) {
DCHECK(!error_msg->empty());
@@ -3440,21 +3459,29 @@
}
if (kIsDebugBuild) {
- for (size_t component_index = 0; component_index != component_count; ++component_index) {
- const OatFile* oat_file = image_spaces[image_pos + component_index]->oat_file_non_owned_;
+ for (size_t space_index = 0; space_index != image_space_count; ++space_index) {
+ const OatFile* oat_file = image_spaces[image_pos + space_index]->oat_file_non_owned_;
size_t num_dex_files = oat_file->GetOatDexFiles().size();
CHECK_NE(num_dex_files, 0u);
const std::string main_location = oat_file->GetOatDexFiles()[0]->GetDexFileLocation();
- CHECK_EQ(main_location, boot_class_path_locations[image_pos + component_index]);
+ CHECK_EQ(main_location, boot_class_path_locations[bcp_pos + space_index]);
CHECK(!DexFileLoader::IsMultiDexLocation(main_location.c_str()));
+ size_t num_base_locations = 1u;
for (size_t i = 1u; i != num_dex_files; ++i) {
- CHECK(DexFileLoader::IsMultiDexLocation(
- oat_file->GetOatDexFiles()[i]->GetDexFileLocation().c_str()));
+ if (DexFileLoader::IsMultiDexLocation(
+ oat_file->GetOatDexFiles()[i]->GetDexFileLocation().c_str())) {
+ CHECK_EQ(image_space_count, 1u); // We can find base locations only for --single-image.
+ ++num_base_locations;
+ }
+ }
+ if (image_space_count == 1u) {
+ CHECK_EQ(num_base_locations, component_count);
}
}
}
- image_pos += component_count;
+ image_pos += image_space_count;
+ bcp_pos += component_count;
if (!StartsWith(oat_checksums, ":")) {
// Check that we've reached the end of checksums and BCP.
diff --git a/runtime/image.cc b/runtime/image.cc
index 07fcc8b..782c0c6 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,7 +29,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '8', '4', '\0' }; // FP16 gt/ge/lt/le intrinsic
+const uint8_t ImageHeader::kImageVersion[] = { '0', '8', '5', '\0' }; // Single-image.
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
@@ -104,6 +104,14 @@
return image_reservation_size_ == RoundUp(image_size_, kPageSize);
}
+uint32_t ImageHeader::GetImageSpaceCount() const {
+ DCHECK(!IsAppImage());
+ DCHECK_NE(component_count_, 0u); // Must be the header for the first component.
+ // For images compiled with --single-image, there is only one oat file. To detect
+ // that, check whether the reservation ends at the end of the first oat file.
+ return (image_begin_ + image_reservation_size_ == oat_file_end_) ? 1u : component_count_;
+}
+
bool ImageHeader::IsValid() const {
if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
return false;
diff --git a/runtime/image.h b/runtime/image.h
index 12950a3..896a83b 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -366,6 +366,8 @@
bool IsAppImage() const;
+ uint32_t GetImageSpaceCount() const;
+
// Visit mirror::Objects in the section starting at base.
// TODO: Delete base parameter if it is always equal to GetImageBegin.
void VisitObjects(ObjectVisitor* visitor,