Avoid creating preresolved strings array for dex files that have no strings
Had exposed a bug in the image writer where having an empty non-null
array caused an assert failure.
Added test.
(cherry-picked from commit adbe2b10d3e9d1cba9c313043941ca591ed109f0)
Bug: 152809035
Test: test-art-host
Merged-In: I0671eef56ae7cbe67bb4ecdbcd72df4e26c8aaa3
Change-Id: I0671eef56ae7cbe67bb4ecdbcd72df4e26c8aaa3
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index db0eb8a..372ee35 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2185,6 +2185,71 @@
/*use_zip_fd=*/ true));
}
+TEST_F(Dex2oatTest, AppImageEmptyDex) {
+ // Create a profile with the startup method marked.
+ ScratchFile profile_file;
+ ScratchFile temp_dex;
+ const std::string& dex_location = temp_dex.GetFilename();
+ std::vector<uint16_t> methods;
+ std::vector<dex::TypeIndex> classes;
+ {
+ MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("StringLiterals"), [&] (DexFile* dex) {
+ // Modify the header to make the dex file valid but empty.
+ DexFile::Header* header = const_cast<DexFile::Header*>(&dex->GetHeader());
+ header->string_ids_size_ = 0;
+ header->string_ids_off_ = 0;
+ header->type_ids_size_ = 0;
+ header->type_ids_off_ = 0;
+ header->proto_ids_size_ = 0;
+ header->proto_ids_off_ = 0;
+ header->field_ids_size_ = 0;
+ header->field_ids_off_ = 0;
+ header->method_ids_size_ = 0;
+ header->method_ids_off_ = 0;
+ header->class_defs_size_ = 0;
+ header->class_defs_off_ = 0;
+ ASSERT_GT(header->file_size_,
+ sizeof(*header) + sizeof(dex::MapList) + sizeof(dex::MapItem) * 2);
+ // Move map list to be right after the header.
+ header->map_off_ = sizeof(DexFile::Header);
+ dex::MapList* map_list = const_cast<dex::MapList*>(dex->GetMapList());
+ map_list->list_[0].type_ = DexFile::kDexTypeHeaderItem;
+ map_list->list_[0].size_ = 1u;
+ map_list->list_[0].offset_ = 0u;
+ map_list->list_[1].type_ = DexFile::kDexTypeMapList;
+ map_list->list_[1].size_ = 1u;
+ map_list->list_[1].offset_ = header->map_off_;
+ map_list->size_ = 2;
+ header->data_off_ = header->map_off_;
+ header->data_size_ = map_list->Size();
+ });
+ }
+ std::unique_ptr<const DexFile> dex_file(OpenDexFile(temp_dex.GetFilename().c_str()));
+ const std::string out_dir = GetScratchDir();
+ const std::string odex_location = out_dir + "/base.odex";
+ const std::string app_image_location = out_dir + "/base.art";
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::Filter::kSpeedProfile,
+ { "--app-image-file=" + app_image_location,
+ "--resolve-startup-const-strings=true",
+ "--profile-file=" + profile_file.GetFilename()},
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
+ [](const OatFile&) {}));
+ // Open our generated oat file.
+ std::string error_msg;
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
+ odex_location.c_str(),
+ odex_location.c_str(),
+ /*executable=*/ false,
+ /*low_4gb=*/ false,
+ &error_msg));
+ ASSERT_TRUE(odex_file != nullptr);
+}
+
+
TEST_F(Dex2oatTest, AppImageResolveStrings) {
using Hotness = ProfileCompilationInfo::MethodHotness;
// Create a profile with the startup method marked.
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 9efcf16..b7776ff 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -682,7 +682,11 @@
<< "Dex cache should have been pruned " << dex_file->GetLocation()
<< "; possibly in class path";
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
- DCHECK(layout.Valid());
+ // Empty dex files will not have a "valid" DexCacheArraysLayout.
+ if (dex_file->NumTypeIds() + dex_file->NumStringIds() + dex_file->NumMethodIds() +
+ dex_file->NumFieldIds() + dex_file->NumProtoIds() + dex_file->NumCallSiteIds() != 0) {
+ DCHECK(layout.Valid());
+ }
size_t oat_index = GetOatIndexForDexFile(dex_file);
ImageInfo& image_info = GetImageInfo(oat_index);
uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file);
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index eab26d8..40997f6 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -220,18 +220,20 @@
Thread* const self = Thread::Current();
LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
const size_t num_strings = GetDexFile()->NumStringIds();
- GcRoot<mirror::String>* strings =
- linear_alloc->AllocArray<GcRoot<mirror::String>>(self, num_strings);
- if (strings == nullptr) {
- // Failed to allocate pre-resolved string array (probably due to address fragmentation), bail.
- return false;
- }
- SetField32<false>(NumPreResolvedStringsOffset(), num_strings);
+ if (num_strings != 0) {
+ GcRoot<mirror::String>* strings =
+ linear_alloc->AllocArray<GcRoot<mirror::String>>(self, num_strings);
+ if (strings == nullptr) {
+ // Failed to allocate pre-resolved string array (probably due to address fragmentation), bail.
+ return false;
+ }
+ SetField32<false>(NumPreResolvedStringsOffset(), num_strings);
- CHECK(strings != nullptr);
- SetPreResolvedStrings(strings);
- for (size_t i = 0; i < GetDexFile()->NumStringIds(); ++i) {
- CHECK(GetPreResolvedStrings()[i].Read() == nullptr);
+ CHECK(strings != nullptr);
+ SetPreResolvedStrings(strings);
+ for (size_t i = 0; i < GetDexFile()->NumStringIds(); ++i) {
+ CHECK(GetPreResolvedStrings()[i].Read() == nullptr);
+ }
}
return true;
}