Revert^4 "Boot image extension."
This reverts commit f5c5eb30fc71e0c305d678bd3c1c995a5c36d508.
Removed wrong check for no dex files. Dex files can also be
specified with zip-location and file descriptors. Added a
regression test to dex2oat_test.
Fixed the image checksums validation to use the appropriate
part of BCP rather then full BCP. This fixes errorneous
rejection of oat files compiled against partial BCP.
Changed dex2oat_image_test to use ART_BASE_ADDRESS to try
and avoid failures when reserving space for loading the
compiled images.
Test: Additional tests in dex2oat_{,image_}_test
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 119800099
Bug: 143492855
Bug: 144001974
Change-Id: I062cbecd0020f5c24353eb75643ea5905cb6f4fd
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 671bdb2..5aff4cf 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -100,6 +100,7 @@
# Remove dex2oat artifacts for boot image extensions (workaround for broken dependencies).
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
+$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index c29cd29..7c3e791 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -63,6 +63,7 @@
$$(error found $(1) expected interpreter, interp-ac, or optimizing)
endif
+ core_image_location := $(HOST_OUT_JAVA_LIBRARIES)/core$$(core_infix)$(CORE_IMG_SUFFIX)
core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
@@ -76,18 +77,46 @@
HOST_CORE_OAT_OUTS += $$(core_oat_name)
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-$$(core_image_name): $$(HOST_CORE_IMG_DEX_LOCATIONS) $$(core_dex2oat_dependency)
+# In addition to the primary core image containing HOST_CORE_IMG_DEX_FILES,
+# also build a boot image extension for the remaining HOST_CORE_DEX_FILES.
+$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@"
@mkdir -p $$(dir $$@)
- $$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ $$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
--runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
$$(addprefix --dex-file=,$$(HOST_CORE_IMG_DEX_FILES)) \
$$(addprefix --dex-location=,$$(HOST_CORE_IMG_DEX_LOCATIONS)) \
--oat-file=$$(PRIVATE_CORE_OAT_NAME) \
- --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(2)ART_HOST_ARCH) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) \
+ --instruction-set=$$($(2)ART_HOST_ARCH) \
+ $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
+ --host --android-root=$$(HOST_OUT) \
+ --generate-debug-info --generate-build-id \
+ --runtime-arg -XX:SlowDebug=true \
+ --no-inline-from=core-oj-hostdex.jar \
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
+ ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+ --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
+ $$(HOST_CORE_DEX_FILES))) \
+ --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
+ $$(HOST_CORE_DEX_LOCATIONS))) \
+ $$(addprefix --dex-file=, \
+ $$(filter-out $$(HOST_CORE_IMG_DEX_FILES),$$(HOST_CORE_DEX_FILES))) \
+ $$(addprefix --dex-location=, \
+ $$(filter-out $$(HOST_CORE_IMG_DEX_LOCATIONS),$$(HOST_CORE_DEX_LOCATIONS))) \
+ --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --instruction-set=$$($(2)ART_HOST_ARCH) \
$$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
--host --android-root=$$(HOST_OUT) \
--generate-debug-info --generate-build-id \
@@ -149,6 +178,7 @@
$$(error found $(1) expected interpreter, interp-ac, or optimizing)
endif
+ core_image_location := $(ART_TARGET_TEST_OUT)/core$$(core_infix)$(CORE_IMG_SUFFIX)
core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
@@ -166,24 +196,53 @@
TARGET_CORE_OAT_OUTS += $$(core_oat_name)
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-$$(core_image_name): $$(TARGET_CORE_IMG_DEX_FILES) $$(core_dex2oat_dependency)
+# In addition to the primary core image containing TARGET_CORE_IMG_DEX_FILES,
+# also build a boot image extension for the remaining TARGET_CORE_DEX_FILES.
+$$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
@echo "target dex2oat: $$@"
@mkdir -p $$(dir $$@)
- $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ $$(hide) $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
--runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
$$(addprefix --dex-file=,$$(TARGET_CORE_IMG_DEX_FILES)) \
$$(addprefix --dex-location=,$$(TARGET_CORE_IMG_DEX_LOCATIONS)) \
--oat-file=$$(PRIVATE_CORE_OAT_NAME) \
- --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(2)TARGET_ARCH) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$$($(2)TARGET_ARCH) \
--instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
--instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
--android-root=$$(PRODUCT_OUT)/system \
--generate-debug-info --generate-build-id \
--runtime-arg -XX:SlowDebug=true \
- $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
+ $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+ --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
+ $$(TARGET_CORE_DEX_FILES))) \
+ --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
+ $$(TARGET_CORE_DEX_LOCATIONS))) \
+ $$(addprefix --dex-file=, \
+ $$(filter-out $$(TARGET_CORE_IMG_DEX_FILES),$$(TARGET_CORE_DEX_FILES))) \
+ $$(addprefix --dex-location=, \
+ $$(filter-out $$(TARGET_CORE_IMG_DEX_LOCATIONS),$$(TARGET_CORE_DEX_LOCATIONS))) \
+ --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --instruction-set=$$($(2)TARGET_ARCH) \
+ --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
+ --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
+ --android-root=$$(PRODUCT_OUT)/system \
+ --generate-debug-info --generate-build-id \
+ --runtime-arg -XX:SlowDebug=true \
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) || \
+ (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8e642e5..dc0dd67 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -63,6 +63,7 @@
#include "debug/method_debug_info.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
+#include "dex/dex_file_loader.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
#include "dex2oat_options.h"
@@ -280,6 +281,14 @@
UsageError(" Do not include the arch as part of the name, it is added automatically.");
UsageError(" Example: --boot-image=/system/framework/boot.art");
UsageError(" (specifies /system/framework/<arch>/boot.art as the image file)");
+ UsageError(" Example: --boot-image=boot.art:boot-framework.art");
+ UsageError(" (specifies <bcp-path1>/<arch>/boot.art as the image file and");
+ UsageError(" <bcp-path2>/<arch>/boot-framework.art as the image extension file");
+ UsageError(" with paths taken from corresponding boot class path components)");
+ UsageError(" Example: --boot-image=/apex/com.android.art/boot.art:/system/framework/*:*");
+ UsageError(" (specifies /apex/com.android.art/<arch>/boot.art as the image");
+ UsageError(" file and search for extensions in /framework/system and boot");
+ UsageError(" class path components' paths)");
UsageError(" Default: $ANDROID_ROOT/system/framework/boot.art");
UsageError("");
UsageError(" --android-root=<path>: used to locate libraries for portable linking.");
@@ -752,16 +761,31 @@
void ProcessOptions(ParserOptions* parser_options) {
compiler_options_->compile_pic_ = true; // All AOT compilation is PIC.
+
+ if (android_root_.empty()) {
+ const char* android_root_env_var = getenv("ANDROID_ROOT");
+ if (android_root_env_var == nullptr) {
+ Usage("--android-root unspecified and ANDROID_ROOT not set");
+ }
+ android_root_ += android_root_env_var;
+ }
+
+ if (!parser_options->boot_image_filename.empty()) {
+ boot_image_filename_ = parser_options->boot_image_filename;
+ }
+
DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
if (!image_filenames_.empty()) {
- if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
+ if (!boot_image_filename_.empty()) {
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImageExtension;
+ } else if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
compiler_options_->image_type_ = CompilerOptions::ImageType::kApexBootImage;
} else {
compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
}
}
if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
- if (compiler_options_->IsBootImage()) {
+ if (compiler_options_->IsBootImage() || compiler_options_->IsBootImageExtension()) {
Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
@@ -818,19 +842,9 @@
Usage("--oat-file arguments do not match --image arguments");
}
- if (android_root_.empty()) {
- const char* android_root_env_var = getenv("ANDROID_ROOT");
- if (android_root_env_var == nullptr) {
- Usage("--android-root unspecified and ANDROID_ROOT not set");
- }
- android_root_ += android_root_env_var;
- }
-
- if (!IsBootImage() && parser_options->boot_image_filename.empty()) {
- parser_options->boot_image_filename = GetDefaultBootImageLocation(android_root_);
- }
- if (!parser_options->boot_image_filename.empty()) {
- boot_image_filename_ = parser_options->boot_image_filename;
+ if (!IsBootImage() && boot_image_filename_.empty()) {
+ DCHECK(!IsBootImageExtension());
+ boot_image_filename_ = GetDefaultBootImageLocation(android_root_);
}
if (dex_filenames_.empty() && zip_fd_ == -1) {
@@ -937,8 +951,8 @@
// Fill some values into the key-value store for the oat header.
key_value_store_.reset(new SafeMap<std::string, std::string>());
- // Automatically force determinism for the boot image in a host build.
- if (!kIsTargetBuild && IsBootImage()) {
+ // Automatically force determinism for the boot image and boot image extensions in a host build.
+ if (!kIsTargetBuild && (IsBootImage() || IsBootImageExtension())) {
force_determinism_ = true;
}
compiler_options_->force_determinism_ = force_determinism_;
@@ -961,18 +975,21 @@
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]);
+ image_filenames_ = ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(dex_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());
}
- oat_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_filenames_[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(dex_locations_, oat_unstripped_[0]);
+ oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(dex_locations_), oat_unstripped_[0], IsBootImageExtension());
}
}
@@ -1206,7 +1223,7 @@
PruneNonExistentDexFiles();
// Expand oat and image filenames for multi image.
- if (IsBootImage() && image_filenames_.size() == 1) {
+ if ((IsBootImage() || IsBootImageExtension()) && image_filenames_.size() == 1) {
ExpandOatAndImageFilenames();
}
@@ -1431,12 +1448,13 @@
return dex2oat::ReturnCode::kOther;
}
- // Verification results are null since we don't know if we will need them yet as the compler
+ // Verification results are null since we don't know if we will need them yet as the compiler
// filter may change.
callbacks_.reset(new QuickCompilerCallbacks(
- IsBootImage() ?
- CompilerCallbacks::CallbackMode::kCompileBootImage :
- CompilerCallbacks::CallbackMode::kCompileApp));
+ // For class verification purposes, boot image extension is the same as boot image.
+ (IsBootImage() || IsBootImageExtension())
+ ? CompilerCallbacks::CallbackMode::kCompileBootImage
+ : CompilerCallbacks::CallbackMode::kCompileApp));
RuntimeArgumentMap runtime_options;
if (!PrepareRuntimeOptions(&runtime_options, callbacks_.get())) {
@@ -1489,7 +1507,7 @@
// store which is used for determining whether the oat file is up to date,
// together with the boot class path locations and checksums stored below.
CompilerFilter::Filter original_compiler_filter = compiler_options_->GetCompilerFilter();
- if (!IsBootImage() && IsVeryLarge(dex_files)) {
+ if (!IsBootImage() && !IsBootImageExtension() && IsVeryLarge(dex_files)) {
// Disable app image to make sure dex2oat unloading is enabled.
compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
@@ -1512,14 +1530,64 @@
callbacks_->SetVerificationResults(verification_results_.get());
}
- if (IsBootImage()) {
- // For boot image, pass opened dex files to the Runtime::Create().
+ if (IsBootImage() || IsBootImageExtension()) {
+ // For boot image or boot image extension, pass opened dex files to the Runtime::Create().
// Note: Runtime acquires ownership of these dex files.
runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
}
if (!CreateRuntime(std::move(runtime_options))) {
return dex2oat::ReturnCode::kCreateRuntime;
}
+ ArrayRef<const DexFile* const> bcp_dex_files(runtime_->GetClassLinker()->GetBootClassPath());
+ if (IsBootImage() || IsBootImageExtension()) {
+ // Check boot class path dex files and, if compiling an extension, the images it depends on.
+ if ((IsBootImage() && bcp_dex_files.size() != dex_files.size()) ||
+ (IsBootImageExtension() && bcp_dex_files.size() <= dex_files.size())) {
+ LOG(ERROR) << "Unexpected number of boot class path dex files for boot image or extension, "
+ << bcp_dex_files.size() << (IsBootImage() ? " != " : " <= ") << dex_files.size();
+ return dex2oat::ReturnCode::kOther;
+ }
+ if (!std::equal(dex_files.begin(), dex_files.end(), bcp_dex_files.end() - dex_files.size())) {
+ LOG(ERROR) << "Boot class path dex files do not end with the compiled dex files.";
+ return dex2oat::ReturnCode::kOther;
+ }
+ size_t bcp_df_pos = 0u;
+ size_t bcp_df_end = bcp_dex_files.size();
+ for (const std::string& bcp_location : runtime_->GetBootClassPathLocations()) {
+ if (bcp_df_pos == bcp_df_end || bcp_dex_files[bcp_df_pos]->GetLocation() != bcp_location) {
+ LOG(ERROR) << "Missing dex file for boot class component " << bcp_location;
+ return dex2oat::ReturnCode::kOther;
+ }
+ CHECK(!DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str()));
+ ++bcp_df_pos;
+ while (bcp_df_pos != bcp_df_end &&
+ DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str())) {
+ ++bcp_df_pos;
+ }
+ }
+ if (bcp_df_pos != bcp_df_end) {
+ LOG(ERROR) << "Unexpected dex file in boot class path "
+ << bcp_dex_files[bcp_df_pos]->GetLocation();
+ return dex2oat::ReturnCode::kOther;
+ }
+ auto lacks_image = [](const DexFile* df) {
+ if (kIsDebugBuild && df->GetOatDexFile() != nullptr) {
+ const OatFile* oat_file = df->GetOatDexFile()->GetOatFile();
+ CHECK(oat_file != nullptr);
+ const auto& image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ CHECK(std::any_of(image_spaces.begin(),
+ image_spaces.end(),
+ [=](const ImageSpace* space) {
+ return oat_file == space->GetOatFile();
+ }));
+ }
+ return df->GetOatDexFile() == nullptr;
+ };
+ if (std::any_of(bcp_dex_files.begin(), bcp_dex_files.end() - dex_files.size(), lacks_image)) {
+ LOG(ERROR) << "Missing required boot image(s) for boot image extension.";
+ return dex2oat::ReturnCode::kOther;
+ }
+ }
if (!compilation_reason_.empty()) {
key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
@@ -1529,17 +1597,32 @@
// If we're compiling the boot image, store the boot classpath into the Key-Value store.
// We use this when loading the boot image.
key_value_store_->Put(OatHeader::kBootClassPathKey, android::base::Join(dex_locations_, ':'));
- }
-
- if (!IsBootImage()) {
+ } else if (IsBootImageExtension()) {
+ // Validate the boot class path and record the dependency on the loaded boot images.
+ TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
+ Runtime* runtime = Runtime::Current();
+ std::string full_bcp = android::base::Join(runtime->GetBootClassPathLocations(), ':');
+ std::string extension_part = ":" + android::base::Join(dex_locations_, ':');
+ if (!android::base::EndsWith(full_bcp, extension_part)) {
+ LOG(ERROR) << "Full boot class path does not end with extension parts, full: " << full_bcp
+ << ", extension: " << extension_part.substr(1u);
+ return dex2oat::ReturnCode::kOther;
+ }
+ std::string bcp_dependency = full_bcp.substr(0u, full_bcp.size() - extension_part.size());
+ key_value_store_->Put(OatHeader::kBootClassPathKey, bcp_dependency);
+ ArrayRef<const DexFile* const> bcp_dex_files_dependency =
+ bcp_dex_files.SubArray(/*pos=*/ 0u, bcp_dex_files.size() - dex_files.size());
+ ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
+ key_value_store_->Put(
+ OatHeader::kBootClassPathChecksumsKey,
+ gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files_dependency));
+ } else {
if (CompilerFilter::DependsOnImageChecksum(original_compiler_filter)) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
Runtime* runtime = Runtime::Current();
key_value_store_->Put(OatHeader::kBootClassPathKey,
android::base::Join(runtime->GetBootClassPathLocations(), ':'));
ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
- ArrayRef<const DexFile* const> bcp_dex_files(
- runtime->GetClassLinker()->GetBootClassPath());
key_value_store_->Put(
OatHeader::kBootClassPathChecksumsKey,
gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files));
@@ -1607,7 +1690,7 @@
CHECK(driver_ == nullptr);
// If we use a swap file, ensure we are above the threshold to make it necessary.
if (swap_fd_ != -1) {
- if (!UseSwap(IsBootImage(), dex_files)) {
+ if (!UseSwap(IsBootImage() || IsBootImageExtension(), dex_files)) {
close(swap_fd_);
swap_fd_ = -1;
VLOG(compiler) << "Decided to run without swap.";
@@ -1624,7 +1707,7 @@
Thread* self = Thread::Current();
WellKnownClasses::Init(self->GetJniEnv());
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
SaveDexInput();
@@ -1713,7 +1796,7 @@
if (!no_inline_filters.empty()) {
std::vector<const DexFile*> class_path_files;
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
// The class loader context is used only for apps.
class_path_files = class_loader_context_->FlattenOpenedDexFiles();
}
@@ -1758,7 +1841,7 @@
compiler_kind_,
thread_count_,
swap_fd_));
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles());
}
@@ -1806,7 +1889,7 @@
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
jobject class_loader = nullptr;
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
class_loader =
class_loader_context_->CreateClassLoader(compiler_options_->dex_files_for_oat_file_);
callbacks_->SetDexFiles(&dex_files);
@@ -1950,7 +2033,7 @@
{
TimingLogger::ScopedTiming t2("dex2oat Write VDEX", timings_);
- DCHECK(IsBootImage() || oat_files_.size() == 1u);
+ DCHECK(IsBootImage() || IsBootImageExtension() || oat_files_.size() == 1u);
verifier::VerifierDeps* verifier_deps = callbacks_->GetVerifierDeps();
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
File* vdex_file = vdex_files_[i].get();
@@ -2174,7 +2257,7 @@
}
bool IsImage() const {
- return IsAppImage() || IsBootImage();
+ return IsAppImage() || IsBootImage() || IsBootImageExtension();
}
bool IsAppImage() const {
@@ -2185,6 +2268,10 @@
return compiler_options_->IsBootImage();
}
+ bool IsBootImageExtension() const {
+ return compiler_options_->IsBootImageExtension();
+ }
+
bool IsHost() const {
return is_host_;
}
@@ -2389,7 +2476,7 @@
bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options,
QuickCompilerCallbacks* callbacks) {
RuntimeOptions raw_options;
- if (boot_image_filename_.empty()) {
+ if (IsBootImage()) {
std::string boot_class_path = "-Xbootclasspath:";
boot_class_path += android::base::Join(dex_filenames_, ':');
raw_options.push_back(std::make_pair(boot_class_path, nullptr));
@@ -2479,7 +2566,7 @@
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
CHECK(image_filenames_.empty());
image_filenames_.push_back(app_image_file_name_);
}
@@ -2862,7 +2949,10 @@
// 3) Compiling with --host
// 4) Compiling on the host (not a target build)
// Otherwise, print a stripped command line.
- if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) {
+ if (kIsDebugBuild ||
+ dex2oat->IsBootImage() || dex2oat->IsBootImageExtension() ||
+ dex2oat->IsHost() ||
+ !kIsTargetBuild) {
LOG(INFO) << CommandLine();
} else {
LOG(INFO) << StrippedCommandLine();
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index d0d5081..1a0a9c8 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -14,31 +14,44 @@
* limitations under the License.
*/
+#include <fstream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
+#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "common_runtime_test.h"
+#include "base/array_ref.h"
#include "base/file_utils.h"
#include "base/macros.h"
+#include "base/mem_map.h"
+#include "base/string_view_cpp20.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
#include "dex/method_reference.h"
+#include "gc/space/image_space.h"
#include "profile/profile_compilation_info.h"
#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
namespace art {
+// A suitable address for loading the core images.
+constexpr uint32_t kBaseAddress = ART_BASE_ADDRESS;
+
struct ImageSizes {
size_t art_size = 0;
size_t oat_size = 0;
@@ -132,8 +145,12 @@
scratch_dir.pop_back();
}
CHECK(!scratch_dir.empty()) << "No directory " << scratch.GetFilename();
+ std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
+ ArrayRef<const std::string> dex_files(libcore_dex_files);
+ std::vector<std::string> local_extra_args = extra_args;
+ local_extra_args.push_back(android::base::StringPrintf("--base=0x%08x", kBaseAddress));
std::string error_msg;
- if (!CompileBootImage(extra_args, scratch.GetFilename(), &error_msg)) {
+ if (!CompileBootImage(local_extra_args, scratch.GetFilename(), dex_files, &error_msg)) {
LOG(ERROR) << "Failed to compile image " << scratch.GetFilename() << error_msg;
}
std::string art_file = scratch.GetFilename() + ".art";
@@ -151,19 +168,19 @@
scratch.Close();
// Clear image files since we compile the image multiple times and don't want to leave any
// artifacts behind.
- ClearDirectory(scratch_dir.c_str(), /*recursive*/ false);
+ ClearDirectory(scratch_dir.c_str(), /*recursive=*/ false);
return ret;
}
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) {
Runtime* const runtime = Runtime::Current();
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
AddRuntimeArg(argv, "-Xms64m");
AddRuntimeArg(argv, "-Xmx64m");
- std::vector<std::string> dex_files = GetLibCoreDexFileNames();
for (const std::string& dex_file : dex_files) {
argv.push_back("--dex-file=" + dex_file);
argv.push_back("--dex-location=" + dex_file);
@@ -182,7 +199,6 @@
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");
- argv.push_back("--base=0x60000000");
std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
@@ -265,4 +281,266 @@
}
}
+TEST_F(Dex2oatImageTest, TestExtension) {
+ constexpr size_t kReservationSize = 256 * MB; // This should be enough for the compiled images.
+ // Extend to both directions for maximum relocation difference.
+ static_assert(ART_BASE_ADDRESS_MIN_DELTA < 0);
+ static_assert(ART_BASE_ADDRESS_MAX_DELTA > 0);
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MIN_DELTA));
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MAX_DELTA));
+ constexpr size_t kExtra = ART_BASE_ADDRESS_MAX_DELTA - ART_BASE_ADDRESS_MIN_DELTA;
+ uint32_t min_relocated_address = kBaseAddress + ART_BASE_ADDRESS_MIN_DELTA;
+ std::string error_msg;
+ MemMap reservation = MemMap::MapAnonymous("Reservation",
+ reinterpret_cast<uint8_t*>(min_relocated_address),
+ kReservationSize + kExtra,
+ PROT_NONE,
+ /*low_4gb=*/ true,
+ /*reuse=*/ false,
+ /*reservation=*/ nullptr,
+ &error_msg);
+ ASSERT_TRUE(reservation.IsValid());
+
+ 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();
+ for (std::string& dex_file : libcore_dex_files) {
+ size_t slash_pos = dex_file.rfind('/');
+ ASSERT_NE(std::string::npos, slash_pos);
+ std::string new_location = jar_dir + dex_file.substr(slash_pos + 1u);
+ std::ifstream src_stream(dex_file, std::ios::binary);
+ std::ofstream dst_stream(new_location, std::ios::binary);
+ dst_stream << src_stream.rdbuf();
+ dex_file = new_location;
+ }
+
+ ArrayRef<const std::string> full_bcp(libcore_dex_files);
+ size_t total_dex_files = full_bcp.size();
+ ASSERT_GE(total_dex_files, 4u); // 2 for "head", 1 for "tail", at least one for "mid", see below.
+
+ // The primary image must contain at least core-oj and core-libart to initialize the runtime.
+ ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
+ ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
+ ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 2u);
+ // Middle part is everything else except for conscrypt.
+ ASSERT_NE(std::string::npos, full_bcp[full_bcp.size() - 1u].find("conscrypt"));
+ ArrayRef<const std::string> mid_bcp =
+ full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ total_dex_files - 1u);
+ ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 2u);
+ // Tail is just the conscrypt.
+ ArrayRef<const std::string> tail_dex_files =
+ full_bcp.SubArray(/*pos=*/ total_dex_files - 1u, /*length=*/ 1u);
+
+ // Prepare the "head", "mid" and "tail" names and locations.
+ std::string base_name = "core.art";
+ std::string base_location = scratch_dir + base_name;
+ std::vector<std::string> expanded_mid = gc::space::ImageSpace::ExpandMultiImageLocations(
+ mid_dex_files.SubArray(/*pos=*/ 0u, /*length=*/ 1u),
+ base_location,
+ /*boot_image_extension=*/ true);
+ CHECK_EQ(1u, expanded_mid.size());
+ std::string mid_location = expanded_mid[0];
+ size_t mid_slash_pos = mid_location.rfind('/');
+ ASSERT_NE(std::string::npos, mid_slash_pos);
+ std::string mid_name = mid_location.substr(mid_slash_pos + 1u);
+ CHECK_EQ(1u, tail_dex_files.size());
+ std::vector<std::string> expanded_tail = gc::space::ImageSpace::ExpandMultiImageLocations(
+ tail_dex_files, 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.
+ std::string base = android::base::StringPrintf("--base=0x%08x", kBaseAddress);
+ bool head_ok = CompileBootImage({base}, filename_prefix, head_dex_files, &error_msg);
+ ASSERT_TRUE(head_ok) << error_msg;
+
+ // Compile the "mid", i.e. the first extension.
+ std::string mid_bcp_string = android::base::Join(mid_bcp, ':');
+ std::vector<std::string> extra_args;
+ AddRuntimeArg(extra_args, "-Xbootclasspath:" + mid_bcp_string);
+ AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + mid_bcp_string);
+ extra_args.push_back("--boot-image=" + base_location);
+ bool mid_ok = CompileBootImage(extra_args, filename_prefix, mid_dex_files, &error_msg);
+ ASSERT_TRUE(mid_ok) << error_msg;
+
+ // Try to compile the "tail" without specifying the "mid" extension. This shall fail.
+ std::string full_bcp_string = android::base::Join(full_bcp, ':');
+ extra_args.clear();
+ AddRuntimeArg(extra_args, "-Xbootclasspath:" + full_bcp_string);
+ AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + full_bcp_string);
+ extra_args.push_back("--boot-image=" + base_location);
+ bool tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
+ ASSERT_FALSE(tail_ok) << error_msg;
+
+ // Now compile the tail against both "head" and "mid".
+ CHECK(StartsWith(extra_args.back(), "--boot-image="));
+ extra_args.back() = "--boot-image=" + base_location + ':' + mid_location;
+ 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 primary image with full path.
+ bool load_ok = load(base_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_FALSE(extra_reservation.IsValid());
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+
+ // Fail to load primary image with just the name.
+ load_ok = load(base_name);
+ ASSERT_FALSE(load_ok);
+
+ // Fail to load primary image with a search path.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
+
+ // Load the primary and first extension with full path.
+ load_ok = load(base_location + ':' + mid_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to load first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+
+ // Load all the libcore images with full paths.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary and first extension with full paths, fail to load second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to load first extension without full path,
+ // fail to load second extension because it depends on the first.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+
+ // Load the primary with full path and extensions with a specified search path.
+ load_ok = load(base_location + ':' + scratch_dir + '*');
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to find extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+ }
+
+ // Now copy the libcore dex files to the `scratch_dir` and retry loading the boot image
+ // with BCP in the scratch_dir so that the images can be found based on BCP paths.
+ for (std::string& bcp_component : boot_class_path) {
+ size_t slash_pos = bcp_component.rfind('/');
+ ASSERT_NE(std::string::npos, slash_pos);
+ std::string new_location = scratch_dir + bcp_component.substr(slash_pos + 1u);
+ std::ifstream src_stream(bcp_component, std::ios::binary);
+ std::ofstream dst_stream(new_location, std::ios::binary);
+ dst_stream << src_stream.rdbuf();
+ bcp_component = new_location;
+ }
+
+ for (bool r : { false, true}) {
+ relocate = r;
+
+ // Loading the primary image with just the name now succeeds.
+ bool load_ok = load(base_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+
+ // Loading the primary image with a search path still fails.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
+
+ // Load the primary and first extension without paths.
+ load_ok = load(base_name + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and the first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size()); // Including the extension.
+
+ // Load all the libcore images without paths.
+ load_ok = load(base_name + ':' + mid_name + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary and first extension with full paths and second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path, first extension without path,
+ // and second extension with full path.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size()); // Including both extensions.
+
+ // Load the primary with full path and find both extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Fail to load any images with invalid image locations (named component after search paths).
+ load_ok = load(base_location + ":*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ load_ok = load(base_location + ':' + scratch_dir + "*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ }
+
+ 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_test.cc b/dex2oat/dex2oat_test.cc
index 81366e4..bd439b7 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -32,6 +32,7 @@
#include "arch/instruction_set_features.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
+#include "base/string_view_cpp20.h"
#include "base/utils.h"
#include "dex/art_dex_file_loader.h"
#include "dex/base64_test_util.h"
@@ -111,13 +112,15 @@
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args = {},
bool expect_success = true,
- bool use_fd = false) WARN_UNUSED {
+ bool use_fd = false,
+ bool use_zip_fd = false) WARN_UNUSED {
return GenerateOdexForTest(dex_location,
odex_location,
filter,
extra_args,
expect_success,
use_fd,
+ use_zip_fd,
[](const OatFile&) {});
}
@@ -131,9 +134,22 @@
const std::vector<std::string>& extra_args,
bool expect_success,
bool use_fd,
+ bool use_zip_fd,
T check_oat) WARN_UNUSED {
+ std::vector<std::string> dex_locations;
+ if (use_zip_fd) {
+ std::string loc_arg = "--zip-location=" + dex_location;
+ CHECK(std::any_of(extra_args.begin(),
+ extra_args.end(),
+ [&](const std::string& s) { return s == loc_arg; }));
+ CHECK(std::any_of(extra_args.begin(),
+ extra_args.end(),
+ [](const std::string& s) { return StartsWith(s, "--zip-fd="); }));
+ } else {
+ dex_locations.push_back(dex_location);
+ }
std::string error_msg;
- int status = GenerateOdexForTestWithStatus({dex_location},
+ int status = GenerateOdexForTestWithStatus(dex_locations,
odex_location,
filter,
&error_msg,
@@ -1082,7 +1098,8 @@
CompilerFilter::kQuicken,
extra_args,
expected_success,
- /*use_fd*/ false,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
check_oat));
}
@@ -1602,8 +1619,9 @@
base_oat_name,
CompilerFilter::Filter::kSpeed,
{ "--deduplicate-code=false" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&no_dedupe_size](const OatFile& o) {
no_dedupe_size = o.Size();
}));
@@ -1613,8 +1631,9 @@
base_oat_name,
CompilerFilter::Filter::kSpeed,
{ "--deduplicate-code=true" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&dedupe_size](const OatFile& o) {
dedupe_size = o.Size();
}));
@@ -1630,8 +1649,9 @@
base_oat_name,
CompilerFilter::Filter::kQuicken,
{ },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(!o.ContainsDexCode());
}));
@@ -1750,8 +1770,9 @@
oat_filename,
CompilerFilter::Filter::kVerify,
{ },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(o.ContainsDexCode());
}));
@@ -1882,8 +1903,9 @@
odex_location,
CompilerFilter::Filter::kVerify,
{ "--copy-dex-files=false" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
{
// Check the vdex doesn't have dex.
@@ -1944,8 +1966,9 @@
// target.
"--runtime-arg",
"-Xuse-stderr-logger" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(o.ContainsDexCode());
}));
@@ -2131,8 +2154,9 @@
odex_location,
CompilerFilter::Filter::kSpeedProfile,
{ "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
@@ -2155,6 +2179,24 @@
EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u);
}
+TEST_F(Dex2oatTest, ZipFd) {
+ std::string zip_location = GetTestDexFileName("MainUncompressed");
+ std::unique_ptr<File> dex_file(OS::OpenFileForReading(zip_location.c_str()));
+ std::vector<std::string> extra_args{
+ StringPrintf("--zip-fd=%d", dex_file->Fd()),
+ "--zip-location=" + zip_location,
+ };
+ std::string out_dir = GetScratchDir();
+ const std::string base_oat_name = out_dir + "/base.oat";
+ ASSERT_TRUE(GenerateOdexForTest(zip_location,
+ base_oat_name,
+ CompilerFilter::Filter::kQuicken,
+ extra_args,
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ true));
+}
+
TEST_F(Dex2oatTest, AppImageResolveStrings) {
using Hotness = ProfileCompilationInfo::MethodHotness;
// Create a profile with the startup method marked.
@@ -2225,8 +2267,9 @@
{ "--app-image-file=" + app_image_location,
"--resolve-startup-const-strings=true",
"--profile-file=" + profile_file.GetFilename()},
- /* expect_success= */ true,
- /* use_fd= */ false,
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
@@ -2338,8 +2381,9 @@
odex_location,
CompilerFilter::Filter::kQuicken,
{ "--class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&](const OatFile& oat_file) {
EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_;
EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_;
@@ -2350,8 +2394,9 @@
CompilerFilter::Filter::kQuicken,
{ "--class-loader-context=" + valid_context,
"--stored-class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&](const OatFile& oat_file) {
EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_;
}));
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9e5da27..fa08627 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -171,8 +171,9 @@
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
std::vector<std::string> image_locations =
- gc::space::ImageSpace::ExpandMultiImageLocations(out_helper.dex_file_locations,
- location.GetFilename() + ".art");
+ gc::space::ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(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]));
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 760acc7..8b8ac18 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -65,6 +65,10 @@
using android::base::StringAppendF;
using android::base::StringPrintf;
+// We do not allow the boot image and extensions to take more than 1GiB. They are
+// supposed to be much smaller and allocating more that this would likely fail anyway.
+static constexpr size_t kMaxTotalImageReservationSize = 1 * GB;
+
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
@@ -1371,6 +1375,531 @@
}
};
+static void AppendImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/std::string* checksums) {
+ static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
+ StringAppendF(checksums, "i;%u/%08x", component_count, checksum);
+}
+
+static bool CheckAndRemoveImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ std::string image_checksum;
+ AppendImageChecksum(component_count, checksum, &image_checksum);
+ if (!StartsWith(*oat_checksums, image_checksum)) {
+ *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+ std::string(*oat_checksums).c_str(),
+ image_checksum.c_str());
+ return false;
+ }
+ oat_checksums->remove_prefix(image_checksum.size());
+ return true;
+}
+
+// Helper class to find the primary boot image and boot image extensions
+// and determine the boot image layout.
+class ImageSpace::BootImageLayout {
+ public:
+ // Description of a "chunk" of the boot image, i.e. either primary boot image
+ // or a boot image extension, used in conjunction with the boot class path to
+ // load boot image components.
+ struct ImageChunk {
+ std::string base_location;
+ std::string base_filename;
+ size_t start_index;
+ size_t component_count;
+ size_t reservation_size;
+ uint32_t checksum;
+ };
+
+ BootImageLayout(const std::string& image_location, ArrayRef<const std::string> boot_class_path)
+ : image_location_(image_location),
+ boot_class_path_(boot_class_path) {}
+
+ std::string GetPrimaryImageLocation();
+
+ bool LoadFromSystem(InstructionSet image_isa, /*out*/std::string* error_msg) {
+ return LoadOrValidateFromSystem(image_isa, /*oat_checksums=*/ nullptr, error_msg);
+ }
+
+ bool ValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ return LoadOrValidateFromSystem(image_isa, oat_checksums, error_msg);
+ }
+
+ bool LoadFromDalvikCache(const std::string& dalvik_cache, /*out*/std::string* error_msg) {
+ return LoadOrValidateFromDalvikCache(dalvik_cache, /*oat_checksums=*/ nullptr, error_msg);
+ }
+
+ bool ValidateFromDalvikCache(const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ return LoadOrValidateFromDalvikCache(dalvik_cache, oat_checksums, error_msg);
+ }
+
+ ArrayRef<const ImageChunk> GetChunks() const {
+ return ArrayRef<const ImageChunk>(chunks_);
+ }
+
+ uint32_t GetBaseAddress() const {
+ return base_address_;
+ }
+
+ size_t GetNextBcpIndex() const {
+ return next_bcp_index_;
+ }
+
+ size_t GetTotalComponentCount() const {
+ return total_component_count_;
+ }
+
+ size_t GetTotalReservationSize() const {
+ return total_reservation_size_;
+ }
+
+ private:
+ std::string ExpandLocationImpl(const std::string& location,
+ size_t bcp_index,
+ bool boot_image_extension) {
+ std::vector<std::string> expanded = ExpandMultiImageLocations(
+ ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
+ location,
+ boot_image_extension);
+ DCHECK_EQ(expanded.size(), 1u);
+ return expanded[0];
+ }
+
+ std::string ExpandLocation(const std::string& location, size_t bcp_index) {
+ if (bcp_index == 0u) {
+ DCHECK_EQ(location, ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ false));
+ return location;
+ } else {
+ return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ true);
+ }
+ }
+
+ bool VerifyImageLocation(const std::vector<std::string>& components,
+ /*out*/size_t* named_components_count,
+ /*out*/std::string* error_msg);
+
+ bool MatchNamedComponents(
+ ArrayRef<const std::string> named_components,
+ /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
+ /*out*/std::string* error_msg);
+
+ bool ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t bcp_component_count,
+ /*out*/std::string* error_msg);
+
+ bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ template <typename FilenameFn>
+ bool LoadOrValidate(FilenameFn&& filename_fn,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ bool LoadOrValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ bool LoadOrValidateFromDalvikCache(const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ const std::string& image_location_;
+ ArrayRef<const std::string> boot_class_path_;
+
+ std::vector<ImageChunk> chunks_;
+ uint32_t base_address_ = 0u;
+ size_t next_bcp_index_ = 0u;
+ size_t total_component_count_ = 0u;
+ size_t total_reservation_size_ = 0u;
+};
+
+std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
+ size_t location_start = 0u;
+ size_t location_end = image_location_.find(':');
+ while (location_end == location_start) {
+ ++location_start;
+ location_end = image_location_.find(location_start, ':');
+ }
+ std::string location = (location_end == std::string::npos)
+ ? image_location_.substr(location_start)
+ : image_location_.substr(location_start, location_end - location_start);
+ if (image_location_.find('/') == std::string::npos) {
+ // No path, so use the path from the first boot class path component.
+ size_t slash_pos = boot_class_path_.empty()
+ ? std::string::npos
+ : boot_class_path_[0].rfind('/');
+ if (slash_pos == std::string::npos) {
+ return std::string();
+ }
+ location.insert(0u, boot_class_path_[0].substr(0u, slash_pos + 1u));
+ }
+ return location;
+}
+
+bool ImageSpace::BootImageLayout::VerifyImageLocation(
+ const std::vector<std::string>& components,
+ /*out*/size_t* named_components_count,
+ /*out*/std::string* error_msg) {
+ DCHECK(named_components_count != nullptr);
+
+ // Validate boot class path. Require a path and non-empty name in each component.
+ for (const std::string& bcp_component : boot_class_path_) {
+ size_t bcp_slash_pos = bcp_component.rfind('/');
+ if (bcp_slash_pos == std::string::npos || bcp_slash_pos == bcp_component.size() - 1u) {
+ *error_msg = StringPrintf("Invalid boot class path component: %s", bcp_component.c_str());
+ return false;
+ }
+ }
+
+ // Validate the format of image location components.
+ size_t components_size = components.size();
+ if (components_size == 0u) {
+ *error_msg = "Empty image location.";
+ return false;
+ }
+ size_t wildcards_start = components_size; // No wildcards.
+ for (size_t i = 0; i != components_size; ++i) {
+ const std::string& component = components[i];
+ DCHECK(!component.empty()); // Guaranteed by Split().
+ size_t wildcard_pos = component.find('*');
+ if (wildcard_pos == std::string::npos) {
+ if (wildcards_start != components.size()) {
+ *error_msg =
+ StringPrintf("Image component without wildcard after component with wildcard: %s",
+ component.c_str());
+ return false;
+ }
+ if (component.back() == '/') {
+ *error_msg = StringPrintf("Image component ends with path separator: %s",
+ component.c_str());
+ return false;
+ }
+ } else {
+ if (wildcards_start == components_size) {
+ wildcards_start = i;
+ }
+ // Wildcard must be the last character.
+ if (wildcard_pos != component.size() - 1u) {
+ *error_msg = StringPrintf("Unsupported wildcard (*) position in %s", component.c_str());
+ return false;
+ }
+ // And it must be either plain wildcard or preceded by a path separator.
+ if (component.size() != 1u && component[wildcard_pos - 1u] != '/') {
+ *error_msg = StringPrintf("Non-plain wildcard (*) not preceded by path separator '/': %s",
+ component.c_str());
+ return false;
+ }
+ if (i == 0) {
+ *error_msg = StringPrintf("Primary component contains wildcard (*): %s", component.c_str());
+ return false;
+ }
+ }
+ }
+
+ *named_components_count = wildcards_start;
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::MatchNamedComponents(
+ ArrayRef<const std::string> named_components,
+ /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
+ /*out*/std::string* error_msg) {
+ DCHECK(!named_components.empty());
+ DCHECK(base_locations_and_bcp_indexes->empty());
+ base_locations_and_bcp_indexes->reserve(named_components.size());
+ size_t bcp_component_count = boot_class_path_.size();
+ size_t bcp_pos = 0;
+ std::string base_name;
+ for (size_t i = 0, size = named_components.size(); i != size; ++i) {
+ const std::string& component = named_components[i];
+ size_t slash_pos = component.rfind('/');
+ std::string base_location;
+ if (i == 0u) {
+ // The primary boot image name is taken as provided. It forms the base
+ // for expanding the extension filenames.
+ if (slash_pos != std::string::npos) {
+ base_name = component.substr(slash_pos + 1u);
+ base_location = component;
+ } else {
+ base_name = component;
+ size_t bcp_slash_pos = boot_class_path_[0u].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ base_location = boot_class_path_[0u].substr(0u, bcp_slash_pos + 1u) + component;
+ }
+ } else {
+ std::string to_match;
+ if (slash_pos != std::string::npos) {
+ // If we have the full path, we just need to match the filename to the BCP component.
+ base_location = component.substr(0u, slash_pos + 1u) + base_name;
+ to_match = component;
+ }
+ while (true) {
+ if (slash_pos == std::string::npos) {
+ // If we do not have a full path, we need to update the path based on the BCP location.
+ size_t bcp_slash_pos = boot_class_path_[bcp_pos].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ std::string path = boot_class_path_[bcp_pos].substr(0u, bcp_slash_pos + 1u);
+ to_match = path + component;
+ base_location = path + base_name;
+ }
+ if (ExpandLocation(base_location, bcp_pos) == to_match) {
+ break;
+ }
+ ++bcp_pos;
+ if (bcp_pos == bcp_component_count) {
+ *error_msg = StringPrintf("Image component %s does not match a boot class path component",
+ component.c_str());
+ return false;
+ }
+ }
+ }
+ base_locations_and_bcp_indexes->emplace_back(base_location, bcp_pos);
+ ++bcp_pos;
+ }
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t bcp_component_count,
+ /*out*/std::string* error_msg) {
+ DCHECK_LE(next_bcp_index_, bcp_index);
+ DCHECK_LT(bcp_index, bcp_component_count);
+ size_t allowed_component_count = bcp_component_count - bcp_index;
+ DCHECK_LE(total_reservation_size_, kMaxTotalImageReservationSize);
+ size_t allowed_reservation_size = kMaxTotalImageReservationSize - total_reservation_size_;
+
+ std::string actual_filename = ExpandLocation(base_filename, bcp_index);
+ ImageHeader header;
+ if (!ReadSpecificImageHeader(actual_filename.c_str(), &header, error_msg)) {
+ return false;
+ }
+ if (header.GetComponentCount() == 0u ||
+ header.GetComponentCount() > allowed_component_count) {
+ *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
+ "expected non-zero and <= %zu",
+ actual_filename.c_str(),
+ header.GetComponentCount(),
+ allowed_component_count);
+ return false;
+ }
+ if (header.GetImageReservationSize() > allowed_reservation_size) {
+ *error_msg = StringPrintf("Reservation size too big in %s: %u > %zu",
+ actual_filename.c_str(),
+ header.GetImageReservationSize(),
+ allowed_reservation_size);
+ return false;
+ }
+
+ if (chunks_.empty()) {
+ base_address_ = reinterpret_cast32<uint32_t>(header.GetImageBegin());
+ }
+ ImageChunk chunk;
+ chunk.base_location = base_location;
+ chunk.base_filename = base_filename;
+ chunk.start_index = bcp_index;
+ chunk.component_count = header.GetComponentCount();
+ chunk.reservation_size = header.GetImageReservationSize();
+ chunk.checksum = header.GetImageChecksum();
+ chunks_.push_back(std::move(chunk));
+ next_bcp_index_ = bcp_index + header.GetComponentCount();
+ total_component_count_ += header.GetComponentCount();
+ total_reservation_size_ += header.GetImageReservationSize();
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::CheckAndRemoveLastChunkChecksum(
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ DCHECK(!chunks_.empty());
+ const ImageChunk& chunk = chunks_.back();
+ size_t component_count = chunk.component_count;
+ size_t checksum = chunk.checksum;
+ if (!CheckAndRemoveImageChecksum(component_count, checksum, oat_checksums, error_msg)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ if (oat_checksums->empty()) {
+ if (next_bcp_index_ != boot_class_path_.size()) {
+ *error_msg = StringPrintf("Checksum too short, missing %zu components.",
+ boot_class_path_.size() - next_bcp_index_);
+ return false;
+ }
+ return true;
+ }
+ if (!StartsWith(*oat_checksums, ":")) {
+ *error_msg = StringPrintf("Missing ':' separator at start of %s",
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ oat_checksums->remove_prefix(1u);
+ if (oat_checksums->empty()) {
+ *error_msg = "Missing checksums after the ':' separator.";
+ return false;
+ }
+ return true;
+}
+
+template <typename FilenameFn>
+bool ImageSpace::BootImageLayout::LoadOrValidate(FilenameFn&& filename_fn,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(GetChunks().empty());
+ DCHECK_EQ(GetBaseAddress(), 0u);
+ bool validate = (oat_checksums != nullptr);
+ static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
+ DCHECK(!validate || StartsWith(*oat_checksums, "i"));
+
+ std::vector<std::string> components;
+ Split(image_location_, ':', &components);
+ size_t named_components_count = 0u;
+ if (!VerifyImageLocation(components, &named_components_count, error_msg)) {
+ return false;
+ }
+
+ ArrayRef<const std::string> named_components =
+ ArrayRef<const std::string>(components).SubArray(/*pos=*/ 0u, named_components_count);
+
+ std::vector<std::pair<std::string, size_t>> base_locations_and_bcp_indexes;
+ if (!MatchNamedComponents(named_components, &base_locations_and_bcp_indexes, error_msg)) {
+ return false;
+ }
+
+ // Load the image headers of named components.
+ DCHECK_EQ(base_locations_and_bcp_indexes.size(), named_components.size());
+ const size_t bcp_component_count = boot_class_path_.size();
+ size_t bcp_pos = 0u;
+ for (size_t i = 0, size = named_components.size(); i != size; ++i) {
+ const std::string& base_location = base_locations_and_bcp_indexes[i].first;
+ size_t bcp_index = base_locations_and_bcp_indexes[i].second;
+ if (bcp_index < bcp_pos) {
+ DCHECK_NE(i, 0u);
+ LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
+ continue;
+ }
+ if (validate && bcp_index > bcp_pos) {
+ *error_msg = StringPrintf("End of contiguous boot class path images, remaining checksum: %s",
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ std::string local_error_msg;
+ std::string* err_msg = (i == 0 || validate) ? error_msg : &local_error_msg;
+ std::string base_filename;
+ if (!filename_fn(base_location, &base_filename, err_msg) ||
+ !ReadHeader(base_location, base_filename, bcp_index, bcp_component_count, err_msg)) {
+ if (i == 0u || validate) {
+ return false;
+ }
+ VLOG(image) << "Error reading named image component header for " << base_location
+ << ", error: " << local_error_msg;
+ bcp_pos = bcp_index + 1u; // Skip at least this component.
+ DCHECK_GT(bcp_pos, GetNextBcpIndex());
+ continue;
+ }
+ if (validate) {
+ if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
+ return false;
+ }
+ if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
+ return true; // Let the caller deal with the dex file checksums if any.
+ }
+ }
+ bcp_pos = GetNextBcpIndex();
+ }
+
+ // Look for remaining components if there are any wildcard specifications.
+ ArrayRef<const std::string> search_paths =
+ ArrayRef<const std::string>(components).SubArray(/*pos=*/ named_components_count);
+ if (!search_paths.empty()) {
+ const std::string& primary_base_location = base_locations_and_bcp_indexes[0].first;
+ size_t base_slash_pos = primary_base_location.rfind('/');
+ DCHECK_NE(base_slash_pos, std::string::npos);
+ std::string base_name = primary_base_location.substr(base_slash_pos + 1u);
+ DCHECK(!base_name.empty());
+ while (bcp_pos != bcp_component_count) {
+ const std::string& bcp_component = boot_class_path_[bcp_pos];
+ bool found = false;
+ for (const std::string& path : search_paths) {
+ std::string base_location;
+ if (path.size() == 1u) {
+ DCHECK_EQ(path, "*");
+ size_t slash_pos = bcp_component.rfind('/');
+ DCHECK_NE(slash_pos, std::string::npos);
+ base_location = bcp_component.substr(0u, slash_pos + 1u) + base_name;
+ } else {
+ DCHECK(EndsWith(path, "/*"));
+ base_location = path.substr(0u, path.size() - 1u) + base_name;
+ }
+ std::string err_msg; // Ignored.
+ std::string base_filename;
+ if (filename_fn(base_location, &base_filename, &err_msg) &&
+ ReadHeader(base_location, base_filename, bcp_pos, bcp_component_count, &err_msg)) {
+ VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
+ bcp_pos = GetNextBcpIndex();
+ found = true;
+ if (validate) {
+ if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
+ return false;
+ }
+ if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
+ return true; // Let the caller deal with the dex file checksums if any.
+ }
+ }
+ break;
+ }
+ }
+ if (!found) {
+ if (validate) {
+ *error_msg = StringPrintf("Missing extension for %s, remaining checksum: %s",
+ bcp_component.c_str(),
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ ++bcp_pos;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ auto filename_fn = [image_isa](const std::string& location,
+ /*out*/std::string* filename,
+ /*out*/std::string* err_msg ATTRIBUTE_UNUSED) {
+ *filename = GetSystemImageFilename(location.c_str(), image_isa);
+ return true;
+ };
+ return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+}
+
+bool ImageSpace::BootImageLayout::LoadOrValidateFromDalvikCache(
+ const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ auto filename_fn = [&dalvik_cache](const std::string& location,
+ /*out*/std::string* filename,
+ /*out*/std::string* err_msg) {
+ return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), filename, err_msg);
+ };
+ return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+}
+
class ImageSpace::BootImageLoader {
public:
BootImageLoader(const std::vector<std::string>& boot_class_path,
@@ -1398,8 +1927,10 @@
bool IsZygote() const { return is_zygote_; }
void FindImageFiles() {
+ BootImageLayout layout(image_location_, boot_class_path_);
+ std::string image_location = layout.GetPrimaryImageLocation();
std::string system_filename;
- bool found_image = FindImageFilenameImpl(image_location_.c_str(),
+ bool found_image = FindImageFilenameImpl(image_location.c_str(),
image_isa_,
&has_system_,
&system_filename,
@@ -1428,124 +1959,115 @@
bool LoadFromSystem(bool validate_oat_file,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
- /*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_);
-
- if (!LoadFromFile(filename,
- validate_oat_file,
- extra_reservation_size,
- &logger,
- boot_image_spaces,
- extra_reservation,
- error_msg)) {
- return false;
- }
-
- if (VLOG_IS_ON(image)) {
- LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
- << boot_image_spaces->front();
- logger.Dump(LOG_STREAM(INFO));
- }
- return true;
- }
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
bool LoadFromDalvikCache(
bool validate_oat_file,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
- /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
- TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
- DCHECK(DalvikCacheExists());
-
- if (!LoadFromFile(cache_filename_,
- validate_oat_file,
- extra_reservation_size,
- &logger,
- boot_image_spaces,
- extra_reservation,
- error_msg)) {
- return false;
- }
-
- if (VLOG_IS_ON(image)) {
- LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
- << boot_image_spaces->front();
- logger.Dump(LOG_STREAM(INFO));
- }
- return true;
- }
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- bool LoadFromFile(
- const std::string& filename,
+ bool LoadImage(
+ const BootImageLayout& layout,
bool validate_oat_file,
size_t extra_reservation_size,
TimingLogger* logger,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
- ImageHeader system_hdr;
- if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr, error_msg)) {
- return false;
- }
- if (system_hdr.GetComponentCount() == 0u ||
- system_hdr.GetComponentCount() > boot_class_path_.size()) {
- *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
- "expected non-zero and <= %zu",
- filename.c_str(),
- system_hdr.GetComponentCount(),
- boot_class_path_.size());
- return false;
- }
- MemMap image_reservation;
- MemMap local_extra_reservation;
- if (!ReserveBootImageMemory(system_hdr.GetImageReservationSize(),
- reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()),
- extra_reservation_size,
- &image_reservation,
- &local_extra_reservation,
- error_msg)) {
+ ArrayRef<const BootImageLayout::ImageChunk> chunks = layout.GetChunks();
+ DCHECK(!chunks.empty());
+ const uint32_t base_address = layout.GetBaseAddress();
+ const size_t image_component_count = layout.GetTotalComponentCount();
+ const size_t image_reservation_size = layout.GetTotalReservationSize();
+
+ DCHECK_LE(image_reservation_size, kMaxTotalImageReservationSize);
+ static_assert(kMaxTotalImageReservationSize < std::numeric_limits<uint32_t>::max());
+ if (extra_reservation_size > std::numeric_limits<uint32_t>::max() - image_reservation_size) {
+ // Since the `image_reservation_size` is limited to kMaxTotalImageReservationSize,
+ // the `extra_reservation_size` would have to be really excessive to fail this check.
+ *error_msg = StringPrintf("Excessive extra reservation size: %zu", extra_reservation_size);
return false;
}
- ArrayRef<const std::string> provided_locations(boot_class_path_locations_.data(),
- system_hdr.GetComponentCount());
- std::vector<std::string> locations =
- ExpandMultiImageLocations(provided_locations, image_location_);
- std::vector<std::string> filenames =
- ExpandMultiImageLocations(provided_locations, filename);
- DCHECK_EQ(locations.size(), filenames.size());
+ // Reserve address space. If relocating, choose a random address for ALSR.
+ uint8_t* addr = reinterpret_cast<uint8_t*>(
+ relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : base_address);
+ MemMap image_reservation =
+ ReserveBootImageMemory(addr, image_reservation_size + extra_reservation_size, error_msg);
+ if (!image_reservation.IsValid()) {
+ return false;
+ }
+
+ // Load components.
std::vector<std::unique_ptr<ImageSpace>> spaces;
- spaces.reserve(locations.size());
- for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
- spaces.push_back(Load(locations[i], filenames[i], logger, &image_reservation, error_msg));
- const ImageSpace* space = spaces.back().get();
- if (space == nullptr) {
- return false;
+ spaces.reserve(image_component_count);
+ size_t max_image_space_dependencies = 0u;
+ for (size_t i = 0, num_chunks = chunks.size(); i != num_chunks; ++i) {
+ const BootImageLayout::ImageChunk& chunk = chunks[i];
+ std::string extension_error_msg;
+ uint8_t* old_reservation_begin = image_reservation.Begin();
+ size_t old_reservation_size = image_reservation.Size();
+ DCHECK_LE(chunk.reservation_size, old_reservation_size);
+ if (!LoadComponents(chunk,
+ validate_oat_file,
+ max_image_space_dependencies,
+ logger,
+ &spaces,
+ &image_reservation,
+ (i == 0) ? error_msg : &extension_error_msg)) {
+ // Failed to load the chunk. If this is the primary boot image, report the error.
+ if (i == 0) {
+ return false;
+ }
+ // For extension, shrink the reservation (and remap if needed, see below).
+ size_t new_reservation_size = old_reservation_size - chunk.reservation_size;
+ if (new_reservation_size == 0u) {
+ DCHECK_EQ(extra_reservation_size, 0u);
+ DCHECK_EQ(i + 1u, num_chunks);
+ image_reservation.Reset();
+ } else if (old_reservation_begin != image_reservation.Begin()) {
+ // Part of the image reservation has been used and then unmapped when
+ // rollling back the partial boot image extension load. Try to remap
+ // the image reservation. As this should be running single-threaded,
+ // the address range should still be available to mmap().
+ image_reservation.Reset();
+ std::string remap_error_msg;
+ image_reservation = ReserveBootImageMemory(old_reservation_begin,
+ new_reservation_size,
+ &remap_error_msg);
+ if (!image_reservation.IsValid()) {
+ *error_msg = StringPrintf("Failed to remap boot image reservation after failing "
+ "to load boot image extension (%s: %s): %s",
+ boot_class_path_locations_[chunk.start_index].c_str(),
+ extension_error_msg.c_str(),
+ remap_error_msg.c_str());
+ return false;
+ }
+ } else {
+ DCHECK_EQ(old_reservation_size, image_reservation.Size());
+ image_reservation.SetSize(new_reservation_size);
+ }
+ LOG(ERROR) << "Failed to load boot image extension "
+ << boot_class_path_locations_[chunk.start_index] << ": " << extension_error_msg;
}
- uint32_t expected_component_count = (i == 0u) ? system_hdr.GetComponentCount() : 0u;
- uint32_t expected_reservation_size = (i == 0u) ? system_hdr.GetImageReservationSize() : 0u;
- if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
- !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
- return false;
+ // Update `max_image_space_dependencies` if all previous BCP components
+ // were covered and loading the current chunk succeeded.
+ if (max_image_space_dependencies == chunk.start_index &&
+ spaces.size() == chunk.start_index + chunk.component_count) {
+ max_image_space_dependencies = chunk.start_index + chunk.component_count;
}
}
- for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
- if (!OpenOatFile(spaces[i].get(),
- boot_class_path_[i],
- validate_oat_file,
- /*available_dependencies=*/ ArrayRef<const std::unique_ptr<ImageSpace>>(),
- logger,
- &image_reservation,
- error_msg)) {
- return false;
- }
- }
- if (!CheckReservationExhausted(image_reservation, error_msg)) {
+
+ MemMap local_extra_reservation;
+ if (!RemapExtraReservation(extra_reservation_size,
+ &image_reservation,
+ &local_extra_reservation,
+ error_msg)) {
return false;
}
@@ -1705,6 +2227,7 @@
simple_relocate_visitor);
// Retrieve the Class.class, Method.class and Constructor.class needed in the loops below.
+ ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots;
ObjPtr<mirror::Class> class_class;
ObjPtr<mirror::Class> method_class;
ObjPtr<mirror::Class> constructor_class;
@@ -1719,9 +2242,8 @@
kExtension ? source_size - image_size : image_size);
int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
- ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
- image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+ class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
+ image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
if (kExtension) {
DCHECK(patched_objects->Test(class_roots.Ptr()));
class_class = GetClassRoot<mirror::Class, kWithoutReadBarrier>(class_roots);
@@ -1853,6 +2375,12 @@
pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment);
}
}
+ if (kIsDebugBuild && !kExtension) {
+ // We used just Test() instead of Set() above but we need to use Set()
+ // for class roots to satisfy a DCHECK() for extensions.
+ DCHECK(!patched_objects->Test(class_roots.Ptr()));
+ patched_objects->Set(class_roots.Ptr());
+ }
}
void MaybeRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces,
@@ -2033,37 +2561,117 @@
return true;
}
- bool ReserveBootImageMemory(uint32_t reservation_size,
- uint32_t image_start,
- size_t extra_reservation_size,
- /*out*/MemMap* image_reservation,
- /*out*/MemMap* extra_reservation,
- /*out*/std::string* error_msg) {
+ bool LoadComponents(const BootImageLayout::ImageChunk& chunk,
+ bool validate_oat_file,
+ size_t max_image_space_dependencies,
+ TimingLogger* logger,
+ /*inout*/std::vector<std::unique_ptr<ImageSpace>>* spaces,
+ /*inout*/MemMap* image_reservation,
+ /*out*/std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Make sure we destroy the spaces we created if we're returning an error.
+ // Note that this can unmap part of the original `image_reservation`.
+ class Guard {
+ public:
+ explicit Guard(std::vector<std::unique_ptr<ImageSpace>>* spaces_in)
+ : spaces_(spaces_in), committed_(spaces_->size()) {}
+ void Commit() {
+ DCHECK_LT(committed_, spaces_->size());
+ committed_ = spaces_->size();
+ }
+ ~Guard() {
+ DCHECK_LE(committed_, spaces_->size());
+ spaces_->resize(committed_);
+ }
+ private:
+ std::vector<std::unique_ptr<ImageSpace>>* const spaces_;
+ size_t committed_;
+ };
+ Guard guard(spaces);
+
+ bool is_extension = (chunk.start_index != 0u);
+ DCHECK_NE(spaces->empty(), is_extension);
+ ArrayRef<const std::string> requested_bcp_locations =
+ ArrayRef<const std::string>(boot_class_path_locations_).SubArray(
+ chunk.start_index, chunk.component_count);
+ std::vector<std::string> locations =
+ ExpandMultiImageLocations(requested_bcp_locations, chunk.base_location, is_extension);
+ std::vector<std::string> filenames =
+ ExpandMultiImageLocations(requested_bcp_locations, chunk.base_filename, is_extension);
+ DCHECK_EQ(locations.size(), filenames.size());
+ for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+ spaces->push_back(Load(locations[i], filenames[i], logger, image_reservation, error_msg));
+ const ImageSpace* space = spaces->back().get();
+ if (space == nullptr) {
+ return false;
+ }
+ uint32_t expected_component_count = (i == 0u) ? chunk.component_count : 0u;
+ uint32_t expected_reservation_size = (i == 0u) ? chunk.reservation_size : 0u;
+ if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
+ !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
+ return false;
+ }
+ if (i == 0 && chunk.checksum != space->GetImageHeader().GetImageChecksum()) {
+ *error_msg = StringPrintf("Image checksum modified since previously read from %s, "
+ "received %u, expected %u",
+ space->GetImageFilename().c_str(),
+ space->GetImageHeader().GetImageChecksum(),
+ chunk.checksum);
+ return false;
+ }
+ }
+ ArrayRef<const std::unique_ptr<ImageSpace>> available_dependencies =
+ ArrayRef<const std::unique_ptr<ImageSpace>>(*spaces).SubArray(/*pos=*/ 0u,
+ max_image_space_dependencies);
+ for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+ ImageSpace* space = (*spaces)[spaces->size() - chunk.component_count + i].get();
+ if (!OpenOatFile(space,
+ boot_class_path_[chunk.start_index + i],
+ validate_oat_file,
+ available_dependencies,
+ logger,
+ image_reservation,
+ error_msg)) {
+ return false;
+ }
+ }
+
+ guard.Commit();
+ return true;
+ }
+
+ MemMap ReserveBootImageMemory(uint8_t* addr,
+ uint32_t reservation_size,
+ /*out*/std::string* error_msg) {
DCHECK_ALIGNED(reservation_size, kPageSize);
- DCHECK_ALIGNED(image_start, kPageSize);
- DCHECK(!image_reservation->IsValid());
- DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size);
- size_t total_size = reservation_size + extra_reservation_size;
- // If relocating, choose a random address for ALSR.
- uint32_t addr = relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : image_start;
- *image_reservation =
- MemMap::MapAnonymous("Boot image reservation",
- reinterpret_cast32<uint8_t*>(addr),
- total_size,
- PROT_NONE,
- /*low_4gb=*/ true,
- /*reuse=*/ false,
- /*reservation=*/ nullptr,
- error_msg);
- if (!image_reservation->IsValid()) {
+ DCHECK_ALIGNED(addr, kPageSize);
+ return MemMap::MapAnonymous("Boot image reservation",
+ addr,
+ reservation_size,
+ PROT_NONE,
+ /*low_4gb=*/ true,
+ /*reuse=*/ false,
+ /*reservation=*/ nullptr,
+ error_msg);
+ }
+
+ bool RemapExtraReservation(size_t extra_reservation_size,
+ /*inout*/MemMap* image_reservation,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ DCHECK_ALIGNED(extra_reservation_size, kPageSize);
+ DCHECK(!extra_reservation->IsValid());
+ size_t expected_size = image_reservation->IsValid() ? image_reservation->Size() : 0u;
+ if (extra_reservation_size != expected_size) {
+ *error_msg = StringPrintf("Image reservation mismatch after loading boot image: %zu != %zu",
+ extra_reservation_size,
+ expected_size);
return false;
}
- DCHECK(!extra_reservation->IsValid());
if (extra_reservation_size != 0u) {
- DCHECK_ALIGNED(extra_reservation_size, kPageSize);
- DCHECK_LT(extra_reservation_size, image_reservation->Size());
- uint8_t* split = image_reservation->End() - extra_reservation_size;
- *extra_reservation = image_reservation->RemapAtEnd(split,
+ DCHECK(image_reservation->IsValid());
+ DCHECK_EQ(extra_reservation_size, image_reservation->Size());
+ *extra_reservation = image_reservation->RemapAtEnd(image_reservation->Begin(),
"Boot image extra reservation",
PROT_NONE,
error_msg);
@@ -2071,27 +2679,17 @@
return false;
}
}
-
+ DCHECK(!image_reservation->IsValid());
return true;
}
- bool CheckReservationExhausted(const MemMap& image_reservation, /*out*/std::string* error_msg) {
- if (image_reservation.IsValid()) {
- *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p",
- image_reservation.Begin(),
- image_reservation.End());
- return false;
- }
- 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 relocate_;
- bool executable_;
- bool is_zygote_;
+ const ArrayRef<const std::string> boot_class_path_;
+ const ArrayRef<const std::string> boot_class_path_locations_;
+ const std::string image_location_;
+ const InstructionSet image_isa_;
+ const bool relocate_;
+ const bool executable_;
+ const bool is_zygote_;
bool has_system_;
bool has_cache_;
bool is_global_cache_;
@@ -2100,6 +2698,68 @@
std::string cache_filename_;
};
+bool ImageSpace::BootImageLoader::LoadFromSystem(
+ bool validate_oat_file,
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+
+ BootImageLayout layout(image_location_, boot_class_path_);
+ if (!layout.LoadFromSystem(image_isa_, error_msg)) {
+ return false;
+ }
+
+ if (!LoadImage(layout,
+ validate_oat_file,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
+ if (VLOG_IS_ON(image)) {
+ LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
+ << boot_image_spaces->front();
+ logger.Dump(LOG_STREAM(INFO));
+ }
+ return true;
+}
+
+bool ImageSpace::BootImageLoader::LoadFromDalvikCache(
+ bool validate_oat_file,
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+ DCHECK(DalvikCacheExists());
+
+ BootImageLayout layout(image_location_, boot_class_path_);
+ if (!layout.LoadFromDalvikCache(dalvik_cache_, error_msg)) {
+ return false;
+ }
+ if (!LoadImage(layout,
+ validate_oat_file,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
+ if (VLOG_IS_ON(image)) {
+ LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
+ << boot_image_spaces->front();
+ logger.Dump(LOG_STREAM(INFO));
+ }
+ return true;
+}
+
static constexpr uint64_t kLowSpaceValue = 50 * MB;
static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
@@ -2145,7 +2805,7 @@
bool executable,
bool is_zygote,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) {
ScopedTrace trace(__FUNCTION__);
@@ -2388,11 +3048,6 @@
return true;
}
-static void AppendImageChecksum(const ImageHeader& header, /*inout*/std::string* checksums) {
- static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
- StringAppendF(checksums, "i;%u/%08x", header.GetComponentCount(), header.GetImageChecksum());
-}
-
std::string ImageSpace::GetBootClassPathChecksums(
ArrayRef<ImageSpace* const> image_spaces,
ArrayRef<const DexFile* const> boot_class_path) {
@@ -2413,7 +3068,7 @@
if (image_pos != 0u) {
boot_image_checksum += ':';
}
- AppendImageChecksum(current_header, &boot_image_checksum);
+ 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];
const OatFile* oat_file = space->oat_file_non_owned_;
@@ -2436,10 +3091,10 @@
DCHECK(boot_class_path_tail.empty() ||
!DexFileLoader::IsMultiDexLocation(boot_class_path_tail.front()->GetLocation().c_str()));
for (const DexFile* dex_file : boot_class_path_tail) {
- if (!boot_image_checksum.empty()) {
- boot_image_checksum += ':';
- }
if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
+ if (!boot_image_checksum.empty()) {
+ boot_image_checksum += ':';
+ }
boot_image_checksum += kDexFileChecksumPrefix;
}
StringAppendF(&boot_image_checksum, "/%08x", dex_file->GetLocationChecksum());
@@ -2479,21 +3134,6 @@
return component_count;
}
-static bool CheckAndRemoveImageChecksum(const ImageHeader& header,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
- std::string image_checksum;
- AppendImageChecksum(header, &image_checksum);
- if (!StartsWith(*oat_checksums, image_checksum)) {
- *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
- std::string(*oat_checksums).c_str(),
- image_checksum.c_str());
- return false;
- }
- oat_checksums->remove_prefix(image_checksum.size());
- return true;
-}
-
bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
std::string_view oat_boot_class_path,
const std::string& image_location,
@@ -2515,75 +3155,40 @@
return false;
}
- bool load_extensions = false; // TODO: Boot image extensions.
- const std::string& actual_image_location = image_location; // TODO: Boot image extensions.
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename;
- bool has_cache = false;
- bool dalvik_cache_exists = false;
- bool is_global_cache = false;
- if (!FindImageFilename(actual_image_location.c_str(),
- image_isa,
- &system_filename,
- &has_system,
- &cache_filename,
- &dalvik_cache_exists,
- &has_cache,
- &is_global_cache)) {
- *error_msg = StringPrintf("Unable to find image file for %s and %s",
- image_location.c_str(),
- GetInstructionSetString(image_isa));
- return false;
- }
-
- DCHECK(has_system || has_cache);
- const std::string& filename = (order == ImageSpaceLoadingOrder::kSystemFirst)
- ? (has_system ? system_filename : cache_filename)
- : (has_cache ? cache_filename : system_filename);
-
size_t bcp_pos = 0u;
- while (StartsWith(oat_checksums, "i")) {
- const std::string& current_filename = filename;
- if (bcp_pos != 0u) {
- if (!load_extensions) {
- *error_msg = "Checksum specifies boot image extension but extensions are not used.";
- return false;
- }
- UNREACHABLE(); // TODO: Boot image extensions.
- }
- ImageHeader header;
- if (!ReadSpecificImageHeader(current_filename.c_str(), &header, error_msg)) {
+ if (StartsWith(oat_checksums, "i")) {
+ // Use only the matching part of the BCP for validation.
+ BootImageLayout layout(image_location, boot_class_path.SubArray(/*pos=*/ 0u, bcp_size));
+ std::string primary_image_location = layout.GetPrimaryImageLocation();
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ bool is_global_cache = false;
+ if (!FindImageFilename(primary_image_location.c_str(),
+ image_isa,
+ &system_filename,
+ &has_system,
+ &cache_filename,
+ &dalvik_cache_exists,
+ &has_cache,
+ &is_global_cache)) {
+ *error_msg = StringPrintf("Unable to find image file for %s and %s",
+ image_location.c_str(),
+ GetInstructionSetString(image_isa));
return false;
}
- size_t component_count = header.GetComponentCount();
- if (component_count == 0u || component_count > bcp_size - bcp_pos) {
- *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
- "expected non-zero and <= %zu",
- current_filename.c_str(),
- header.GetComponentCount(),
- bcp_size - bcp_pos);
+
+ DCHECK(has_system || has_cache);
+ bool use_system = (order == ImageSpaceLoadingOrder::kSystemFirst) ? has_system : !has_cache;
+ bool image_checksums_ok = use_system
+ ? layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)
+ : layout.ValidateFromDalvikCache(cache_filename, &oat_checksums, error_msg);
+ if (!image_checksums_ok) {
return false;
}
- if (!CheckAndRemoveImageChecksum(header, &oat_checksums, error_msg)) {
- DCHECK(!error_msg->empty());
- return false;
- }
- bcp_pos += component_count;
- if (oat_checksums.empty()) {
- if (bcp_pos != bcp_size) {
- *error_msg = StringPrintf("Checksum too short, missing %zu components.",
- bcp_size - bcp_pos);
- return false;
- }
- return true;
- }
- if (!StartsWith(oat_checksums, ":")) {
- *error_msg = StringPrintf("Missing ':' separator at start of %s",
- std::string(oat_checksums).c_str());
- return false;
- }
- oat_checksums.remove_prefix(1u);
+ bcp_pos = layout.GetNextBcpIndex();
}
for ( ; bcp_pos != bcp_size; ++bcp_pos) {
@@ -2662,7 +3267,8 @@
uint32_t component_count = current_header.GetComponentCount();
DCHECK_NE(component_count, 0u);
DCHECK_LE(component_count, image_spaces.size() - image_pos);
- if (!CheckAndRemoveImageChecksum(current_header, &oat_checksums, error_msg)) {
+ uint32_t checksum = current_header.GetImageChecksum();
+ if (!CheckAndRemoveImageChecksum(component_count, checksum, &oat_checksums, error_msg)) {
DCHECK(!error_msg->empty());
return false;
}
@@ -2673,7 +3279,9 @@
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[image_pos + component_index]);
+ // TODO: Get rid of the weird ResolveRelativeEncodedDexLocation() stuff from oat_file.cc
+ // and enable this check:
+ // CHECK_EQ(main_location, boot_class_path_locations[image_pos + component_index]);
CHECK(!DexFileLoader::IsMultiDexLocation(main_location.c_str()));
for (size_t i = 1u; i != num_dex_files; ++i) {
CHECK(DexFileLoader::IsMultiDexLocation(
@@ -2708,14 +3316,6 @@
}
std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
- const std::vector<std::string>& dex_locations,
- const std::string& image_location,
- bool boot_image_extension) {
- return ExpandMultiImageLocations(
- ArrayRef<const std::string>(dex_locations), image_location, boot_image_extension);
-}
-
-std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
ArrayRef<const std::string> dex_locations,
const std::string& image_location,
bool boot_image_extension) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 837facc..f56b42b 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -39,11 +39,61 @@
return kSpaceTypeImageSpace;
}
- // Load boot image spaces from a primary image file for a specified instruction set.
+ // Load boot image spaces for specified boot class path, image location, instruction set, etc.
//
// On successful return, the loaded spaces are added to boot_image_spaces (which must be
// empty on entry) and `extra_reservation` is set to the requested reservation located
// after the end of the last loaded oat file.
+ //
+ // IMAGE LOCATION
+ //
+ // The "image location" is a colon-separated list that specifies one or more
+ // components by name and may also specify search paths for extensions
+ // corresponding to the remaining boot class path (BCP) extensions.
+ //
+ // The primary boot image can be specified as one of
+ // <path>/<base-name>
+ // <base-name>
+ // and the path of the first BCP component is used for the second form.
+ //
+ // Named extension specifications must correspond to an expansion of the
+ // <base-name> with a BCP component (for example boot.art with the BCP
+ // component name <jar-path>/framework.jar expands to boot-framework.art).
+ // They can be similarly specified as one of
+ // <ext-path>/<ext-name>
+ // <ext-name>
+ // and must be listed in the order of their corresponding BCP components.
+ //
+ // Search paths for remaining extensions can be specified after named
+ // components as one of
+ // <search-path>/*
+ // *
+ // where the second form means that the path of a particular BCP component
+ // should be used to search for that component's boot image extension. These
+ // paths will be searched in the specifed order.
+ //
+ // The actual filename shall be derived from the specified locations using
+ // `GetSystemImageFilename()` or `GetDalvikCacheFilename()`.
+ //
+ // Example image locations:
+ // /system/framework/boot.art
+ // - only primary boot image with full path.
+ // boot.art:boot-framework.art
+ // - primary and one extension, use BCP component paths.
+ // /apex/com.android.art/boot.art:*
+ // - primary with exact location, search for the rest based on BCP
+ // component paths.
+ // boot.art:/system/framework/*
+ // - primary based on BCP component path, search for extensions in
+ // /system/framework.
+ // /apex/com.android.art/boot.art:/system/framework/*:*
+ // - primary with exact location, search for extensions first in
+ // /system/framework, then in the corresponding BCP component path.
+ // /apex/com.android.art/boot.art:*:/system/framework/*
+ // - primary with exact location, search for extensions first in the
+ // corresponding BCP component path and then in /system/framework.
+ // /apex/com.android.art/boot.art:*:boot-framework.jar
+ // - invalid, named components may not follow search paths.
static bool LoadBootImage(
const std::vector<std::string>& boot_class_path,
const std::vector<std::string>& boot_class_path_locations,
@@ -54,7 +104,7 @@
bool executable,
bool is_zygote,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
// Try to open an existing app image space.
@@ -167,7 +217,7 @@
// 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,
+ ArrayRef<const std::string> dex_locations,
const std::string& image_location,
bool boot_image_extension = false);
@@ -233,12 +283,7 @@
friend class Space;
private:
- // Internal overload that takes ArrayRef<> instead of vector<>.
- static std::vector<std::string> ExpandMultiImageLocations(
- ArrayRef<const std::string> dex_locations,
- const std::string& image_location,
- bool boot_image_extension = false);
-
+ class BootImageLayout;
class BootImageLoader;
template <typename ReferenceVisitor>
class ClassTableVisitor;
diff --git a/runtime/image.cc b/runtime/image.cc
index 256b957..08b81c1 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -93,6 +93,13 @@
}
}
+bool ImageHeader::IsAppImage() const {
+ // Unlike boot image and boot image extensions which include address space for
+ // oat files in their reservation size, app images are loaded separately from oat
+ // files and their reservation size is the image size rounded up to full page.
+ return image_reservation_size_ == RoundUp(image_size_, kPageSize);
+}
+
bool ImageHeader::IsValid() const {
if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
return false;
diff --git a/runtime/image.h b/runtime/image.h
index 13bf112..a2d163a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -354,11 +354,7 @@
return data_size_;
}
- bool IsAppImage() const {
- // App images currently require a boot image, if the size is non zero then it is an app image
- // header.
- return boot_image_size_ != 0u;
- }
+ bool IsAppImage() const;
// Visit mirror::Objects in the section starting at base.
// TODO: Delete base parameter if it is always equal to GetImageBegin.
diff --git a/runtime/oat.h b/runtime/oat.h
index 20ea5d3..1ff21c1 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Revert^3 Boot image extension.
- static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '5', '\0' } };
+ // Last oat version changed reason: Revert^4 Boot image extension.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '6', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 4b5d5c3..9ef5fbb 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -855,14 +855,18 @@
CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ);
// Make sure the file lists a boot image dependency, otherwise the .data.bimg.rel.ro
// section is bogus. The full dependency is checked before the code is executed.
- const char* boot_class_path_checksum =
- GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
- if (boot_class_path_checksum == nullptr ||
- boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
- *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
- "without boot image dependency.",
- GetLocation().c_str());
- return false;
+ // We cannot do this check if we do not have a key-value store, i.e. for secondary
+ // oat files for boot image extensions.
+ if (GetOatHeader().GetKeyValueStoreSize() != 0u) {
+ const char* boot_class_path_checksum =
+ GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+ if (boot_class_path_checksum == nullptr ||
+ boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
+ *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
+ "without boot image dependency.",
+ GetLocation().c_str());
+ return false;
+ }
}
}
diff --git a/test/run-test b/test/run-test
index eeeefbb..72e7562 100755
--- a/test/run-test
+++ b/test/run-test
@@ -672,12 +672,12 @@
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
- run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art")
+ run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art:*")
run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
else
guess_target_arch_name
run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
- run_args+=(--boot "/data/art-test/core${image_suffix}.art")
+ run_args+=(--boot "/data/art-test/core${image_suffix}.art:/data/art-test/*")
fi
if [ "$relocate" = "yes" ]; then
run_args+=(--relocate)