Revert "Revert "Add support for LZ4 compressed image files""
Needed to call compiler_driver_->SetSupportBootImageFixup(false).
Bug: 22858531
This reverts commit 83d4d72aa0e4170209ab50c67ba22e46b71352c1.
Change-Id: Iaed6a810a0c088f1f2c57cf2f12087f3978a3de1
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 74cc899..36c81fb 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -491,9 +491,9 @@
LOCAL_C_INCLUDES += art
ifeq ($$(art_static_or_shared),static)
- LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace
+ LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace liblz4
else
- LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace
+ LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace liblz4
endif
ifeq ($$(art_target_or_host),target)
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index f741117..4884e66 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -113,7 +113,9 @@
/*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size),
/*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size),
/*pointer_size*/sizeof(void*),
- /*compile_pic*/false);
+ /*compile_pic*/false,
+ ImageHeader::kStorageModeUncompressed,
+ /*storage_size*/0u);
return new DummyImageSpace(map.release(), live_bitmap.release());
}
};
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e2b2431..8f67c21 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,12 +17,12 @@
#include "image_space.h"
#include <dirent.h>
+#include <lz4.h>
+#include <random>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>
-#include <random>
-
#include "art_method.h"
#include "base/macros.h"
#include "base/stl_util.h"
@@ -677,11 +677,12 @@
*error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
return nullptr;
}
- // Check that the file is large enough.
- uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
- if (image_header.GetImageSize() > image_file_size) {
- *error_msg = StringPrintf("Image file too small for image heap: %" PRIu64 " vs. %zu.",
- image_file_size, image_header.GetImageSize());
+ // Check that the file is larger or equal to the header size + data size.
+ const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
+ if (image_file_size < sizeof(ImageHeader) + image_header.GetDataSize()) {
+ *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".",
+ image_file_size,
+ image_header.GetDataSize());
return nullptr;
}
@@ -697,7 +698,11 @@
}
const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap);
- auto end_of_bitmap = static_cast<size_t>(bitmap_section.End());
+ // The location we want to map from is the first aligned page after the end of the stored
+ // (possibly compressed) data.
+ const size_t image_bitmap_offset = RoundUp(sizeof(image_header) + image_header.GetDataSize(),
+ kPageSize);
+ const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size();
if (end_of_bitmap != image_file_size) {
*error_msg = StringPrintf(
"Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,
@@ -706,16 +711,60 @@
}
// Note: The image header is part of the image due to mmap page alignment required of offset.
- std::unique_ptr<MemMap> map(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
- image_header.GetImageSize(),
- PROT_READ | PROT_WRITE,
+ std::unique_ptr<MemMap> map;
+ if (image_header.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+ map.reset(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ file->Fd(),
+ 0,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg));
+ } else {
+ // Reserve output and decompress into it.
+ map.reset(MemMap::MapAnonymous(image_location,
+ image_header.GetImageBegin(),
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ error_msg));
+ if (map != nullptr) {
+ const size_t stored_size = image_header.GetDataSize();
+ const size_t write_offset = sizeof(image_header); // Skip the header.
+ std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+ PROT_READ,
MAP_PRIVATE,
file->Fd(),
- 0,
+ /*offset*/0,
/*low_4gb*/false,
- /*reuse*/false,
image_filename,
error_msg));
+ if (temp_map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ memcpy(map->Begin(), &image_header, sizeof(image_header));
+ const uint64_t start = NanoTime();
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
+ reinterpret_cast<char*>(map->Begin()) + write_offset,
+ stored_size,
+ map->Size());
+ // TODO: VLOG(image)
+ VLOG(class_linker) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+ if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
+ *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header.GetImageSize());
+ return nullptr;
+ }
+ }
+ }
+
if (map == nullptr) {
DCHECK(!error_msg->empty());
return nullptr;
@@ -723,16 +772,16 @@
CHECK_EQ(image_header.GetImageBegin(), map->Begin());
DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));
- std::unique_ptr<MemMap> image_map(MemMap::MapFileAtAddress(nullptr,
- bitmap_section.Size(),
- PROT_READ, MAP_PRIVATE,
- file->Fd(),
- bitmap_section.Offset(),
- /*low_4gb*/false,
- /*reuse*/false,
- image_filename,
- error_msg));
- if (image_map.get() == nullptr) {
+ std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
+ bitmap_section.Size(),
+ PROT_READ, MAP_PRIVATE,
+ file->Fd(),
+ image_bitmap_offset,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg));
+ if (image_bitmap_map == nullptr) {
*error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
return nullptr;
}
@@ -741,9 +790,11 @@
bitmap_index));
std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(
accounting::ContinuousSpaceBitmap::CreateFromMemMap(
- bitmap_name, image_map.release(), reinterpret_cast<uint8_t*>(map->Begin()),
+ bitmap_name,
+ image_bitmap_map.release(),
+ reinterpret_cast<uint8_t*>(map->Begin()),
accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_section.Size())));
- if (bitmap.get() == nullptr) {
+ if (bitmap == nullptr) {
*error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
return nullptr;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 2eac3fb..7d2ef75 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '4', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -36,7 +36,9 @@
uint32_t oat_data_end,
uint32_t oat_file_end,
uint32_t pointer_size,
- bool compile_pic)
+ bool compile_pic,
+ StorageMode storage_mode,
+ size_t data_size)
: image_begin_(image_begin),
image_size_(image_size),
oat_checksum_(oat_checksum),
@@ -47,7 +49,9 @@
patch_delta_(0),
image_roots_(image_roots),
pointer_size_(pointer_size),
- compile_pic_(compile_pic) {
+ compile_pic_(compile_pic),
+ storage_mode_(storage_mode),
+ data_size_(data_size) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index a16f3c9..3032beb 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -78,10 +78,27 @@
// header of image files written by ImageWriter, read and validated by Space.
class PACKED(4) ImageHeader {
public:
+ enum StorageMode : uint32_t {
+ kStorageModeUncompressed,
+ kStorageModeLZ4,
+ kStorageModeCount, // Number of elements in enum.
+ };
+ static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed;
+
ImageHeader()
- : image_begin_(0U), image_size_(0U), oat_checksum_(0U), oat_file_begin_(0U),
- oat_data_begin_(0U), oat_data_end_(0U), oat_file_end_(0U), patch_delta_(0),
- image_roots_(0U), pointer_size_(0U), compile_pic_(0) {}
+ : image_begin_(0U),
+ image_size_(0U),
+ oat_checksum_(0U),
+ oat_file_begin_(0U),
+ oat_data_begin_(0U),
+ oat_data_end_(0U),
+ oat_file_end_(0U),
+ patch_delta_(0),
+ image_roots_(0U),
+ pointer_size_(0U),
+ compile_pic_(0),
+ storage_mode_(kDefaultStorageMode),
+ data_size_(0) {}
ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -93,7 +110,9 @@
uint32_t oat_data_end,
uint32_t oat_file_end,
uint32_t pointer_size,
- bool compile_pic);
+ bool compile_pic,
+ StorageMode storage_mode,
+ size_t data_size);
bool IsValid() const;
const char* GetMagic() const;
@@ -194,6 +213,14 @@
return compile_pic_ != 0;
}
+ StorageMode GetStorageMode() const {
+ return storage_mode_;
+ }
+
+ uint64_t GetDataSize() const {
+ return data_size_;
+ }
+
private:
static const uint8_t kImageMagic[4];
static const uint8_t kImageVersion[4];
@@ -241,6 +268,13 @@
// Image methods.
uint64_t image_methods_[kImageMethodsCount];
+ // Storage method for the image, the image may be compressed.
+ StorageMode storage_mode_;
+
+ // Data size for the image data excluding the bitmap and the header. For compressed images, this
+ // is the compressed size in the file.
+ uint32_t data_size_;
+
friend class ImageWriter;
};
@@ -248,6 +282,7 @@
std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageRoot& policy);
std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageSections& section);
std::ostream& operator<<(std::ostream& os, const ImageSection& section);
+std::ostream& operator<<(std::ostream& os, const ImageHeader::StorageMode& mode);
} // namespace art