void ImageHeader::RelocateImageReferences(int64_t delta) { // App Images can be relocated to a page aligned address. // Unlike with the Boot Image, for which the memory is reserved in advance of // loading and is aligned to kElfSegmentAlignment, the App Images can be mapped // without reserving memory i.e. via direct file mapping in which case the // memory range is aligned by the kernel and the only guarantee is that it is // aligned to the page sizes. // // NOTE: While this might be less than alignment required via information in // the ELF header, it should be sufficient in practice as the only reason // for the ELF segment alignment to be more than one page size is the // compatibility of the ELF with system configurations that use larger // page size. // // Adding preliminary memory reservation would introduce certain overhead. // // However, technically the alignment requirement isn't fulfilled and that // might be worth addressing even if it adds certain overhead. This will have // to be done in alignment with the dynamic linker's ELF loader as // otherwise inconsistency would still be possible e.g. when using // `dlopen`-like calls to load OAT files.
CHECK_ALIGNED_PARAM(delta, gPageSize) << "relocation delta must be page aligned";
oat_file_begin_ += delta;
oat_data_begin_ += delta;
oat_data_end_ += delta;
oat_file_end_ += delta;
image_begin_ += delta;
image_roots_ += delta;
}
void ImageHeader::RelocateBootImageReferences(int64_t delta) {
CHECK_ALIGNED(delta, kElfSegmentAlignment) << "relocation delta must be Elf segment aligned";
DCHECK_EQ(boot_image_begin_ != 0u, boot_image_size_ != 0u); if (boot_image_begin_ != 0u) {
boot_image_begin_ += delta;
} for (size_t i = 0; i < kImageMethodsCount; ++i) {
image_methods_[i] += delta;
}
}
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 Elf alignment. return image_reservation_size_ == RoundUp(image_size_, kElfSegmentAlignment);
}
uint32_t ImageHeader::GetImageSpaceCount() const {
DCHECK(!IsAppImage());
DCHECK_NE(component_count_, 0u); // Must be the header for the first component. // For images compiled with --single-image, there is only one oat file. To detect // that, check whether the reservation ends at the end of the first oat file. return (image_begin_ + image_reservation_size_ == oat_file_end_) ? 1u : component_count_;
}
bool ImageHeader::IsValid() const { if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) { returnfalse;
} if (memcmp(version_, kImageVersion, sizeof(kImageVersion)) != 0) { returnfalse;
} if (!IsAligned<kElfSegmentAlignment>(image_reservation_size_)) { returnfalse;
} // Unsigned so wraparound is well defined. if (image_begin_ >= image_begin_ + image_size_) { returnfalse;
} if (oat_checksum_ != 0u) { if (oat_file_begin_ > oat_file_end_) { returnfalse;
} if (oat_data_begin_ > oat_data_end_) { returnfalse;
} if (oat_file_begin_ >= oat_data_begin_) { returnfalse;
}
} returntrue;
}
bool LZ4_decompress_safe_checked(constchar* source, char* dest, int compressed_size, int max_decompressed_size, /*out*/ size_t* decompressed_size_checked, /*out*/ std::string* error_msg) { int decompressed_size = LZ4_decompress_safe(source, dest, compressed_size, max_decompressed_size); if (UNLIKELY(decompressed_size < 0)) {
*error_msg = android::base::StringPrintf("LZ4_decompress_safe() returned negative size: %d",
decompressed_size); returnfalse;
} else {
*decompressed_size_checked = static_cast<size_t>(decompressed_size); returntrue;
}
}
bool ImageHeader::Block::Decompress(uint8_t* out_ptr, const uint8_t* in_ptr,
std::string* error_msg) const { switch (storage_mode_) { case kStorageModeUncompressed: {
CHECK_EQ(image_size_, data_size_);
memcpy(out_ptr + image_offset_, in_ptr + data_offset_, data_size_); break;
} case kStorageModeLZ4: case kStorageModeLZ4HC: { // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
size_t decompressed_size; bool ok = LZ4_decompress_safe_checked( reinterpret_cast<constchar*>(in_ptr) + data_offset_, reinterpret_cast<char*>(out_ptr) + image_offset_,
data_size_,
image_size_,
&decompressed_size,
error_msg); if (!ok) { returnfalse;
} if (decompressed_size != image_size_) { if (error_msg != nullptr) { // Maybe some disk / memory corruption, just bail.
*error_msg = (std::ostringstream() << "Decompressed size different than image size: "
<< decompressed_size << ", and " << image_size_).str();
} returnfalse;
} break;
} default: { if (error_msg != nullptr) {
*error_msg = (std::ostringstream() << "Invalid image format " << storage_mode_).str();
} returnfalse;
}
} returntrue;
}
constchar* ImageHeader::GetImageSectionName(ImageSections index) { switch (index) { case kSectionObjects: return"Objects"; case kSectionArtFields: return"ArtFields"; case kSectionArtMethods: return"ArtMethods"; case kSectionImTables: return"ImTables"; case kSectionIMTConflictTables: return"IMTConflictTables"; case kSectionRuntimeMethods: return"RuntimeMethods"; case kSectionJniStubMethods: return"JniStubMethods"; case kSectionInternedStrings: return"InternedStrings"; case kSectionClassTable: return"ClassTable"; case kSectionStringReferenceOffsets: return"StringReferenceOffsets"; case kSectionDexCacheArrays: return"DexCacheArrays"; case kSectionMetadata: return"Metadata"; case kSectionImageBitmap: return"ImageBitmap"; case kSectionCount: return nullptr;
}
}
// Compress data from `source` into `storage`. staticbool CompressData(ArrayRef<const uint8_t> source,
ImageHeader::StorageMode image_storage_mode, /*out*/ dchecked_vector<uint8_t>* storage) { const uint64_t compress_start_time = NanoTime();
// Bound is same for both LZ4 and LZ4HC.
storage->resize(LZ4_compressBound(source.size()));
size_t data_size = 0; if (image_storage_mode == ImageHeader::kStorageModeLZ4) {
data_size = LZ4_compress_default( reinterpret_cast<char*>(const_cast<uint8_t*>(source.data())), reinterpret_cast<char*>(storage->data()),
source.size(),
storage->size());
} else {
DCHECK_EQ(image_storage_mode, ImageHeader::kStorageModeLZ4HC);
data_size = LZ4_compress_HC( reinterpret_cast<constchar*>(const_cast<uint8_t*>(source.data())), reinterpret_cast<char*>(storage->data()),
source.size(),
storage->size(),
LZ4HC_CLEVEL_MAX);
}
if (data_size == 0) { returnfalse;
}
storage->resize(data_size);
VLOG(image) << "Compressed from " << source.size() << " to " << storage->size() << " in "
<< PrettyDuration(NanoTime() - compress_start_time); if (kIsDebugBuild) {
dchecked_vector<uint8_t> decompressed(source.size());
size_t decompressed_size;
std::string error_msg; bool ok = LZ4_decompress_safe_checked( reinterpret_cast<char*>(storage->data()), reinterpret_cast<char*>(decompressed.data()),
storage->size(),
decompressed.size(),
&decompressed_size,
&error_msg); if (!ok) {
LOG(FATAL) << error_msg;
UNREACHABLE();
}
CHECK_EQ(decompressed_size, decompressed.size());
CHECK_EQ(memcmp(source.data(), decompressed.data(), source.size()), 0) << image_storage_mode;
} returntrue;
}
// Add a set of solid blocks such that no block is larger than the maximum size. A solid block // is a block that must be decompressed all at once. auto add_blocks = [&](uint32_t offset, uint32_t size) { while (size != 0u) { const uint32_t cur_size = std::min(size, max_image_block_size);
block_sources.emplace_back(offset, cur_size);
offset += cur_size;
size -= cur_size;
}
};
// Checksum of compressed image data and header.
uint32_t image_checksum = 0u; if (update_checksum) {
image_checksum = adler32(0L, Z_NULL, 0);
image_checksum = adler32(image_checksum, reinterpret_cast<const uint8_t*>(this), sizeof(ImageHeader));
}
// Copy and compress blocks.
uint32_t out_offset = sizeof(ImageHeader); for (const std::pair<uint32_t, uint32_t> block : block_sources) {
ArrayRef<const uint8_t> raw_image_data(data + block.first, block.second);
dchecked_vector<uint8_t> compressed_data;
ArrayRef<const uint8_t> image_data; if (is_compressed) { if (!CompressData(raw_image_data, image_storage_mode, &compressed_data)) {
*error_msg = "Error compressing data for " +
image_file->GetPath() + ": " + std::string(strerror(errno)); returnfalse;
}
image_data = ArrayRef<const uint8_t>(compressed_data);
} else {
image_data = raw_image_data; // For uncompressed, preserve alignment since the image will be directly mapped.
out_offset = block.first;
}
// Fill in the compressed location of the block.
blocks.emplace_back(ImageHeader::Block(
image_storage_mode, /*data_offset=*/ out_offset, /*data_size=*/ image_data.size(), /*image_offset=*/ block.first, /*image_size=*/ block.second));
if (!image_file->PwriteFully(image_data.data(), image_data.size(), out_offset)) {
*error_msg = "Failed to write image file data " +
image_file->GetPath() + ": " + std::string(strerror(errno)); returnfalse;
}
out_offset += image_data.size(); if (update_checksum) {
image_checksum = adler32(image_checksum, image_data.data(), image_data.size());
}
}
if (is_compressed) { // Align up since the compressed data is not necessarily aligned.
out_offset = RoundUp(out_offset, alignof(ImageHeader::Block));
CHECK(!blocks.empty()); const size_t blocks_bytes = blocks.size() * sizeof(blocks[0]); if (!image_file->PwriteFully(&blocks[0], blocks_bytes, out_offset)) {
*error_msg = "Failed to write image blocks " +
image_file->GetPath() + ": " + std::string(strerror(errno)); returnfalse;
} this->blocks_offset_ = out_offset; this->blocks_count_ = blocks.size();
out_offset += blocks_bytes;
}
// Data size includes everything except the bitmap. this->data_size_ = out_offset - sizeof(ImageHeader);
// Update and write the bitmap section. Note that the bitmap section is relative to the // possibly compressed image.
ImageSection& bitmap_section = GetImageSection(ImageHeader::kSectionImageBitmap); // Align up since data size may be unaligned if the image is compressed.
out_offset = RoundUp(out_offset, kElfSegmentAlignment);
bitmap_section = ImageSection(out_offset, bitmap_section.Size());
int err = image_file->Flush(); if (err < 0) {
*error_msg = "Failed to flush image file " + image_file->GetPath() + ": " + std::to_string(err); returnfalse;
}
if (update_checksum) { // Calculate the image checksum of the remaining data.
image_checksum = adler32(image_checksum, reinterpret_cast<const uint8_t*>(bitmap_data),
bitmap_section.Size()); this->SetImageChecksum(image_checksum);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.