diff options
author | 2024-12-19 17:56:02 +0000 | |
---|---|---|
committer | 2025-01-30 10:55:25 -0800 | |
commit | 55cb961ae83563eed14fb3aecc7debafc9c86192 (patch) | |
tree | 9c4ba4f5769cedc56873a6db63feee65ec0887e3 | |
parent | f10036f351515b670e9480aebef009f35ced8bb6 (diff) |
Reduce alignment for .rodata section in OAT files
This patch removes almost 16 KiB of padding at the start of each
OAT file, by reducing .rodata alignment from kElfSegmentAlignment
(16 KiB) to 4 bytes. It is valid to do because:
* The first loadable segment in the OAT (ELF) file is specified to
start at 0x0 and includes the .rodata and data before it (ELF
header, program headers, etc.):
+--------+---------+--------------------+---------+
| ELF | program | optional | .rodata |
| header | headers | .note.gnu.build-id | |
+--------+---------+--------------------+---------+
^ ^
| |
+-------- first loadable segment in memory -------+
|
+-- page-aligned
This patch does not change the alignment of this first loadable
segment mapped by the linker (both bionic's linker and ART's one),
it only reduces the size of the mapping by reducing the padding
within it.
* Content of .rodata doesn't need to be page-aligned, the maximum
memory alignment that it requires is 4 bytes.
At the moment the OatWriter uses offsets relative to the oat header
(actually the start of the .rodata); so all data *in other sections*
that requires more than 4-byte memory-alignment must account for
the fact that the header is no longer kElfSegmentAlignment aligned
and instead use the offset from the start of the file.
This patch also fixes an issue with the algorithm in
ImmuneSpaces::CreateLargestImmuneRegion, specifically when ART's
ELF loader is used. As this loader only guarantees mmaps begin on a
page-size aligned virtual address (rather than aligning to the largest
segment alignment as done via dlopen implementations), it is possible
that when an image space's end is rounded up to 16K, it may overlap
the next space's begin (see an illustration below), causing a failure.
Therefore, instead round up the end to the next 4K boundary when
calculating the region.
image space interval
/---------------------------+ oat space interval
\ +------+----------------------------/
/ | | \
\---+----------+-----+------+----------------------------/
/ | | | | \
\---+----------+-----+------+----------------------------/
^ ^ ^ ^
| | | +----- image space end (16 KiB aligned)
image end | |
| +-- oatdata (.rodata)
|
oat file (4 KiB aligned)
Actual disk usage reduction from this patch:
* Approx 90% of tested dense .odex files are reduced by 16 KiB
(all reductions are in range 0-16 KiB).
* Approx 75% of tested sparse .odex files are reduced by 4 KiB
(all reductions are in range 0-4 KiB).
Authors: Richard Neill <richard.neill@arm.com>
Konstantin Baladurin <konstantin.baladurin@arm.com>
Test: testrunner.py --optimizing
Bug: 378792349
Change-Id: I874bad5bea28fa1bcf66f331e7d74a506dda72cc
-rw-r--r-- | dex2oat/linker/multi_oat_relative_patcher.cc | 1 | ||||
-rw-r--r-- | dex2oat/linker/multi_oat_relative_patcher.h | 3 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.cc | 45 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.h | 12 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer_test.cc | 93 | ||||
-rw-r--r-- | libelffile/elf/elf_builder.h | 2 | ||||
-rw-r--r-- | runtime/gc/collector/immune_spaces.cc | 2 | ||||
-rw-r--r-- | runtime/oat/image.cc | 3 | ||||
-rw-r--r-- | runtime/oat/oat.cc | 22 | ||||
-rw-r--r-- | runtime/oat/oat.h | 14 | ||||
-rw-r--r-- | test/552-checker-sharpening/run.py | 8 |
11 files changed, 171 insertions, 34 deletions
diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 15f495e05e..df17314720 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -50,7 +50,6 @@ MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, } void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) { - DCHECK_ALIGNED(adjustment, kElfSegmentAlignment); adjustment_ = adjustment; start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize(); diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 2daada44bc..58f2922fd2 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -52,7 +52,8 @@ class MultiOatRelativePatcher final { // It must must never point directly to a method's code to avoid relative offsets // with value 0 because this value is used as a missing offset indication in // GetOffset() and an error indication in WriteThunks(). Additionally, it must be - // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP. + // relative to a page-aligned boundary, so that it does not skew alignment calculations, + // say arm64 ADRP. void StartOatFile(uint32_t adjustment); // Get relative offset. Returns 0 when the offset has not been set yet. diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index a2500bd38a..ffd982df67 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -646,7 +646,8 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { offset = InitDataImgRelRoLayout(offset); } oat_size_ = offset; // .bss does not count towards oat_size_. - bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kElfSegmentAlignment) : 0u; + bss_start_ = (bss_size_ != 0u) ? + GetOffsetFromOatDataAlignedToFile(oat_size_, kElfSegmentAlignment) : 0u; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); @@ -1310,8 +1311,11 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi const MethodReference& method_ref, uint32_t thumb_offset) { offset_ = relative_patcher_->ReserveSpace(offset_, compiled_method, method_ref); - offset_ += CodeAlignmentSize(offset_, *compiled_method); - DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), + // `offset_` is relative to the oat data, but we need to align the code relative to the + // beginning of the oat file to make it aligned in the memory, so we need to use the file + // offset here. + offset_ += CodeAlignmentSize(writer_->GetFileOffset(offset_), *compiled_method); + DCHECK_ALIGNED_PARAM(writer_->GetFileOffset(offset_) + sizeof(OatQuickMethodHeader), GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet())); return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset; } @@ -1613,7 +1617,11 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { ReportWriteFailure("relative call thunk", method_ref); return false; } - uint32_t alignment_size = CodeAlignmentSize(offset_, *compiled_method); + // `offset_` is relative to the oat data, but we need to align the code relative to the + // beginning of the oat file to make it aligned in the memory, so we need to use the file + // offset here. + uint32_t alignment_size = + CodeAlignmentSize(writer_->GetFileOffset(offset_), *compiled_method); if (alignment_size != 0) { if (!writer_->WriteCodeAlignment(out, alignment_size)) { ReportWriteFailure("code alignment padding", method_ref); @@ -1622,7 +1630,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { offset_ += alignment_size; DCHECK_OFFSET_(); } - DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), + DCHECK_ALIGNED_PARAM(writer_->GetFileOffset(offset_) + sizeof(OatQuickMethodHeader), GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet())); DCHECK_EQ( method_offsets.code_offset_, @@ -1972,7 +1980,8 @@ size_t OatWriter::InitOatHeader(uint32_t num_dex_files, oat_header_.reset(OatHeader::Create(GetCompilerOptions().GetInstructionSet(), GetCompilerOptions().GetInstructionSetFeatures(), num_dex_files, - key_value_store)); + key_value_store, + oat_data_offset_)); size_oat_header_ += sizeof(OatHeader); size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); return oat_header_->GetHeaderSize(); @@ -2278,7 +2287,7 @@ size_t OatWriter::InitOatCode(size_t offset) { // calculate the offsets within OatHeader to executable code size_t old_offset = offset; // required to be on a new page boundary - offset = RoundUp(offset, kElfSegmentAlignment); + offset = GetOffsetFromOatDataAlignedToFile(offset, kElfSegmentAlignment); oat_header_->SetExecutableOffset(offset); size_executable_offset_alignment_ = offset - old_offset; InstructionSet instruction_set = compiler_options_.GetInstructionSet(); @@ -2288,7 +2297,8 @@ size_t OatWriter::InitOatCode(size_t offset) { #define DO_TRAMPOLINE(field, fn_name) \ /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \ - offset = CompiledCode::AlignCode(offset + 4, instruction_set); \ + offset = GetOffsetFromOatDataAlignedToFile(offset + 4, \ + GetInstructionSetCodeAlignment(instruction_set)); \ adjusted_offset = offset + GetInstructionSetEntryPointAdjustment(instruction_set); \ oat_header_->Set ## fn_name ## Offset(adjusted_offset); \ (field) = compiler_driver_->Create ## fn_name(); \ @@ -2394,7 +2404,7 @@ size_t OatWriter::InitDataImgRelRoLayout(size_t offset) { return offset; } - data_img_rel_ro_start_ = RoundUp(offset, kElfSegmentAlignment); + data_img_rel_ro_start_ = GetOffsetFromOatDataAlignedToFile(offset, kElfSegmentAlignment); for (auto& entry : boot_image_rel_ro_entries_) { size_t& entry_offset = entry.second; @@ -2536,7 +2546,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { // Write padding. off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent); relative_offset += size_executable_offset_alignment_; - DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset()); + DCHECK_EQ(relative_offset, GetOatHeader().GetExecutableOffset()); size_t expected_file_offset = file_offset + relative_offset; if (static_cast<uint32_t>(new_offset) != expected_file_offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset @@ -2622,7 +2632,7 @@ bool OatWriter::WriteDataImgRelRo(OutputStream* out) { // Record the padding before the .data.img.rel.ro section. // Do not write anything, this zero-filled part was skipped (Seek()) when starting the section. size_t code_end = GetOatHeader().GetExecutableOffset() + code_size_; - DCHECK_EQ(RoundUp(code_end, kElfSegmentAlignment), relative_offset); + DCHECK_EQ(GetOffsetFromOatDataAlignedToFile(code_end, kElfSegmentAlignment), relative_offset); size_t padding_size = relative_offset - code_end; DCHECK_EQ(size_data_img_rel_ro_alignment_, 0u); size_data_img_rel_ro_alignment_ = padding_size; @@ -3150,7 +3160,8 @@ size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relati #define DO_TRAMPOLINE(field) \ do { \ /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \ - uint32_t aligned_offset = CompiledCode::AlignCode(relative_offset + 4, instruction_set); \ + uint32_t aligned_offset = GetOffsetFromOatDataAlignedToFile( \ + relative_offset + 4, GetInstructionSetCodeAlignment(instruction_set)); \ uint32_t alignment_padding = aligned_offset - relative_offset; \ for (size_t i = 0; i < alignment_padding; i++) { \ uint8_t padding = 0xFF; \ @@ -3540,8 +3551,9 @@ bool OatWriter::WriteDexLayoutSections(OutputStream* oat_rodata, DCHECK_EQ(oat_dex_file->dex_sections_layout_offset_, 0u); // Write dex layout section alignment bytes. + size_t rodata_file_offset = GetFileOffset(rodata_offset); const size_t padding_size = - RoundUp(rodata_offset, alignof(DexLayoutSections)) - rodata_offset; + RoundUp(rodata_file_offset, alignof(DexLayoutSections)) - rodata_file_offset; if (padding_size != 0u) { std::vector<uint8_t> buffer(padding_size, 0u); if (!oat_rodata->WriteFully(buffer.data(), padding_size)) { @@ -3788,12 +3800,15 @@ void OatWriter::SetMultiOatRelativePatcherAdjustment() { DCHECK(dex_files_ != nullptr); DCHECK(relative_patcher_ != nullptr); DCHECK_NE(oat_data_offset_, 0u); + size_t elf_file_offset = 0; if (image_writer_ != nullptr && !dex_files_->empty()) { // The oat data begin may not be initialized yet but the oat file offset is ready. size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front()); - size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index); - relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_); + elf_file_offset = image_writer_->GetOatFileOffset(oat_index); } + // Relative patcher expects offsets from the page-aligned boundary, as the oat data is + // unaligned in the ELF file we always need to set its correct start. + relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_); } OatWriter::OatDexFile::OatDexFile(std::unique_ptr<const DexFile> dex_file) diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index c985812f94..96243ff2a1 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -355,6 +355,18 @@ class OatWriter { return dex_files_ != nullptr && extract_dex_files_into_vdex_; } + // Return the file offset that corresponds to `offset_from_oat_data`. + size_t GetFileOffset(size_t offset_from_oat_data) const { + DCHECK_NE(oat_data_offset_, 0u); + return offset_from_oat_data + oat_data_offset_; + } + + // Return the next offset (relative to the oat data) that is on or after `offset_from_oat_data`, + // that is aligned by `alignment` to the beginning of the file. + size_t GetOffsetFromOatDataAlignedToFile(size_t offset_from_oat_data, size_t alignment) const { + return RoundUp(GetFileOffset(offset_from_oat_data), alignment) - oat_data_offset_; + } + enum class WriteState { kAddingDexFileSources, kStartRoData, diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 0a585edb53..f3b598fe77 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -76,6 +76,8 @@ class OatTest : public CommonCompilerDriverTest { const void* quick_oat_code = oat_method.GetQuickCode(); EXPECT_TRUE(quick_oat_code != nullptr) << method->PrettyMethod(); uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2); + EXPECT_EQ(RoundDown(oat_code_aligned, + GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet())), oat_code_aligned); quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned); ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); EXPECT_FALSE(quick_code.empty()); @@ -445,6 +447,12 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; const OatHeader& oat_header = oat_file->GetOatHeader(); ASSERT_TRUE(oat_header.IsValid()); + // .text section in the ELF program header is specified to be aligned to kElfSegmentAlignment. + // However, ART's ELF loader does not adhere to this and only guarantees to align it to the + // runtime page size. Therefore, we assert that the executable segment is page-aligned in + // virtual memory. + const uint8_t* text_section = oat_file->Begin() + oat_header.GetExecutableOffset(); + ASSERT_TRUE(IsAlignedParam(text_section, GetPageSizeSlow())); ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core ASSERT_TRUE(oat_header.GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey) != nullptr); ASSERT_STREQ("testkey", oat_header.GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey)); @@ -489,7 +497,7 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(68U, sizeof(OatHeader)); + EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(4U, sizeof(OatQuickMethodHeader)); EXPECT_EQ(173 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), @@ -875,5 +883,88 @@ TEST_F(OatTest, ZipFileInputWithEmptyDex) { TestZipFileInputWithEmptyDex(); } +TEST_F(OatTest, AlignmentCheck) { + TimingLogger timings("OatTest::AlignmentCheck", false, false); + + // OatWriter sets trampoline offsets to non-zero values only for primary boot oat + // file (e.g. boot.oat), so we use it to check trampolines alignment. + std::string location = GetCoreOatLocation(); + std::string filename = GetSystemImageFilename(location.c_str(), kRuntimeISA); + + // Find the absolute path for core-oj.jar and use it to open boot.oat. Otherwise, + // OatFile::Open will attempt to open the dex file using its relative location, + // which may result in a "file not found" error. + ASSERT_TRUE(java_lang_dex_file_ != nullptr); + const DexFile& dex_file = *java_lang_dex_file_; + std::string dex_location = dex_file.GetLocation(); + std::vector<std::string> filenames = GetLibCoreDexFileNames(); + auto it = std::find_if( + filenames.cbegin(), + filenames.cend(), + [&dex_location](const std::string& filename) { + return filename.ends_with(dex_location); + }); + ASSERT_NE(it, filenames.cend()) + << "cannot find: " << dex_location << " in libcore dex filenames"; + + std::string dex_filename = *it; + std::string error_msg; + std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1, + filename, + filename, + /*executable=*/ false, + /*low_4gb=*/ false, + dex_filename, + &error_msg)); + ASSERT_NE(oat_file, nullptr) << error_msg; + ASSERT_TRUE(IsAligned<alignof(OatHeader)>(oat_file->Begin())) + << "oat header: " << reinterpret_cast<const void*>(oat_file->Begin()) + << ", alignment: " << alignof(OatHeader); + + const OatHeader& oat_header = oat_file->GetOatHeader(); + ASSERT_TRUE(oat_header.IsValid()); + + // Check trampolines alignment. + size_t alignment = GetInstructionSetCodeAlignment(instruction_set_); + size_t adjustment = GetInstructionSetEntryPointAdjustment(instruction_set_); + for (size_t i = 0; i <= static_cast<size_t>(StubType::kLast); i++) { + StubType stub_type = static_cast<StubType>(i); + const uint8_t* address = oat_header.GetOatAddress(stub_type); + ASSERT_NE(address, nullptr); + const uint8_t* adjusted_address = address - adjustment; + EXPECT_TRUE(IsAlignedParam(adjusted_address, alignment)) + << "stub: " << stub_type + << ", address: " << reinterpret_cast<const void*>(adjusted_address) + << ", code alignment: " << alignment; + } + + // Check code alignment. + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str()); + for (ClassAccessor accessor : dex_file.GetClasses()) { + const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(accessor.GetClassDefIndex()); + if (oat_class.GetType() == OatClassType::kNoneCompiled) { + continue; + } + + uint32_t method_index = 0; + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + const OatFile::OatMethod& oat_method = oat_class.GetOatMethod(method_index++); + uintptr_t code = reinterpret_cast<uintptr_t>(oat_method.GetQuickCode()); + if (code == 0) { + continue; + } + const void* adjusted_address = reinterpret_cast<const void*>(code - adjustment); + EXPECT_TRUE(IsAlignedParam(adjusted_address, alignment)) + << "method: " << method.GetReference().PrettyMethod() + << ", code: " << adjusted_address + << ", code alignment: " << alignment; + } + EXPECT_EQ(method_index, accessor.NumMethods()); + } + + // Check DexLayoutSections alignment. + EXPECT_TRUE(IsAligned<alignof(DexLayoutSections)>(oat_dex_file->GetDexLayoutSections())); +} + } // namespace linker } // namespace art diff --git a/libelffile/elf/elf_builder.h b/libelffile/elf/elf_builder.h index bb82b109bd..c93877ce32 100644 --- a/libelffile/elf/elf_builder.h +++ b/libelffile/elf/elf_builder.h @@ -460,7 +460,7 @@ class ElfBuilder final { ElfBuilder(InstructionSet isa, OutputStream* output) : isa_(isa), stream_(output), - rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kElfSegmentAlignment, 0), + rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4u, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kElfSegmentAlignment, 0), data_img_rel_ro_(this, ".data.img.rel.ro", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 4128242cf7..778d47f605 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -51,7 +51,7 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { space::ImageSpace* image_space = space->AsImageSpace(); // Update the end to include the other non-heap sections. space_end = RoundUp(reinterpret_cast<uintptr_t>(image_space->GetImageEnd()), - kElfSegmentAlignment); + MemMap::GetPageSize()); // For the app image case, GetOatFileBegin is where the oat file was mapped during image // creation, the actual oat file could be somewhere else. const OatFile* const image_oat_file = image_space->GetOatFile(); diff --git a/runtime/oat/image.cc b/runtime/oat/image.cc index b62a4e0621..309f36edbc 100644 --- a/runtime/oat/image.cc +++ b/runtime/oat/image.cc @@ -30,6 +30,7 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_array.h" +#include "oat.h" namespace art HIDDEN { @@ -72,7 +73,7 @@ ImageHeader::ImageHeader(uint32_t image_reservation_size, CHECK_EQ(image_begin, RoundUp(image_begin, kElfSegmentAlignment)); if (oat_checksum != 0u) { CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kElfSegmentAlignment)); - CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kElfSegmentAlignment)); + CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, alignof(OatHeader))); CHECK_LT(image_roots, oat_file_begin); CHECK_LE(oat_file_begin, oat_data_begin); CHECK_LT(oat_data_begin, oat_data_end); diff --git a/runtime/oat/oat.cc b/runtime/oat/oat.cc index 0cb22065b0..1136458c28 100644 --- a/runtime/oat/oat.cc +++ b/runtime/oat/oat.cc @@ -45,7 +45,8 @@ static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* vari OatHeader* OatHeader::Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, - const SafeMap<std::string, std::string>* variable_data) { + const SafeMap<std::string, std::string>* variable_data, + uint32_t base_oat_offset) { // Estimate size of optional data. size_t needed_size = ComputeOatHeaderSize(variable_data); @@ -56,19 +57,22 @@ OatHeader* OatHeader::Create(InstructionSet instruction_set, return new (memory) OatHeader(instruction_set, instruction_set_features, dex_file_count, - variable_data); + variable_data, + base_oat_offset); } OatHeader::OatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, - const SafeMap<std::string, std::string>* variable_data) + const SafeMap<std::string, std::string>* variable_data, + uint32_t base_oat_offset) : oat_checksum_(0u), instruction_set_(instruction_set), instruction_set_features_bitmap_(instruction_set_features->AsBitmap()), dex_file_count_(dex_file_count), oat_dex_files_offset_(0), bcp_bss_info_offset_(0), + base_oat_offset_(base_oat_offset), executable_offset_(0), jni_dlsym_lookup_trampoline_offset_(0), jni_dlsym_lookup_critical_trampoline_offset_(0), @@ -100,7 +104,9 @@ bool OatHeader::IsValid() const { if (version_ != kOatVersion) { return false; } - if (!IsAligned<kElfSegmentAlignment>(executable_offset_)) { + // Only check the offset is valid after it has been set. + if (executable_offset_ != 0u && + !IsAligned<kElfSegmentAlignment>(executable_offset_ + base_oat_offset_)) { return false; } if (!IsValidInstructionSet(instruction_set_)) { @@ -122,7 +128,9 @@ std::string OatHeader::GetValidationErrorMessage() const { kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3], version_[0], version_[1], version_[2], version_[3]); } - if (!IsAligned<kElfSegmentAlignment>(executable_offset_)) { + // Only check the offset is valid after it has been set. + if (executable_offset_ != 0u && + !IsAligned<kElfSegmentAlignment>(executable_offset_ + base_oat_offset_)) { return "Executable offset not properly aligned."; } if (!IsValidInstructionSet(instruction_set_)) { @@ -199,13 +207,13 @@ void OatHeader::SetBcpBssInfoOffset(uint32_t bcp_info_offset) { uint32_t OatHeader::GetExecutableOffset() const { DCHECK(IsValid()); - DCHECK_ALIGNED(executable_offset_, kElfSegmentAlignment); + DCHECK_ALIGNED(executable_offset_ + base_oat_offset_, kElfSegmentAlignment); CHECK_GT(executable_offset_, sizeof(OatHeader)); return executable_offset_; } void OatHeader::SetExecutableOffset(uint32_t executable_offset) { - DCHECK_ALIGNED(executable_offset, kElfSegmentAlignment); + DCHECK_ALIGNED(executable_offset + base_oat_offset_, kElfSegmentAlignment); CHECK_GT(executable_offset, sizeof(OatHeader)); DCHECK(IsValid()); DCHECK_EQ(executable_offset_, 0U); diff --git a/runtime/oat/oat.h b/runtime/oat/oat.h index 2b5c21e129..8ee3ad235e 100644 --- a/runtime/oat/oat.h +++ b/runtime/oat/oat.h @@ -44,8 +44,8 @@ std::ostream& operator<<(std::ostream& stream, StubType stub_type); class EXPORT PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: reland "arm64: Store resolved MethodType-s in .bss." - static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '5', '4', '\0'}}; + // Last oat version changed reason: reduce alignment for .rodata section in OAT files. + static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '5', '5', '\0'}}; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; @@ -66,7 +66,8 @@ class EXPORT PACKED(4) OatHeader { static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, - const SafeMap<std::string, std::string>* variable_data); + const SafeMap<std::string, std::string>* variable_data, + uint32_t base_oat_offset = 0u); bool IsValid() const; std::string GetValidationErrorMessage() const; @@ -131,7 +132,8 @@ class EXPORT PACKED(4) OatHeader { OatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, - const SafeMap<std::string, std::string>* variable_data); + const SafeMap<std::string, std::string>* variable_data, + uint32_t base_oat_offset); // Returns true if the value of the given key is "true", false otherwise. bool IsKeyEnabled(const char* key) const; @@ -147,6 +149,10 @@ class EXPORT PACKED(4) OatHeader { uint32_t dex_file_count_; uint32_t oat_dex_files_offset_; uint32_t bcp_bss_info_offset_; + // Offset of the oat header (i.e. start of the oat data) in the ELF file. + // It is used to additional validation of the oat header as it is not + // page-aligned in the memory. + uint32_t base_oat_offset_; uint32_t executable_offset_; uint32_t jni_dlsym_lookup_trampoline_offset_; uint32_t jni_dlsym_lookup_critical_trampoline_offset_; diff --git a/test/552-checker-sharpening/run.py b/test/552-checker-sharpening/run.py index 1a893fbb1c..4c1c0aeaea 100644 --- a/test/552-checker-sharpening/run.py +++ b/test/552-checker-sharpening/run.py @@ -16,5 +16,9 @@ def run(ctx, args): - # Use a profile to put specific classes in the app image. - ctx.default_run(args, profile=True) + # Use a profile to put specific classes in the app image. Also run tests with different + # dex2oat options to cover cases with varying .rodata offsets. + # Since a build ID section appears before .rodata in an oat file, .rodata offset depends on + # presence of build id section in the file. + ctx.default_run(args, profile=True, compiler_only_option=["--generate-build-id"]) + ctx.default_run(args, profile=True, compiler_only_option=["--no-generate-build-id"]) |