diff options
author | Richard Neill <richard.neill@arm.com> | 2024-04-22 16:21:56 +0100 |
---|---|---|
committer | Steven Moreland <smoreland@google.com> | 2024-05-06 16:28:01 +0000 |
commit | a463b165408222e3ef6a2052ef31b1560b3afea6 (patch) | |
tree | 8b2bfb8f19a1385ae5c272333c67dd36ac69dec1 | |
parent | 53da12c60f200d64b08d9c079593da2cff8f43d2 (diff) | |
download | art-a463b165408222e3ef6a2052ef31b1560b3afea6.tar.gz |
Add ELF alignment mismatch test
When executing 'standalone' tests that run against an on-device deployed
ART APEX (see test/README.atest.md), the test ART configuration and the
existing deployed ART configuration must be compatible for the testing
to be meaningful.
One source of incompatibility is ELF segment alignment, where the value
of the constexpr kElfSegmentAlignment in the test build must match the
ELF files that are compiled on-device. Many tests are expected to fail if
there is a mismatch, but the runtime errors could be difficult to debug.
This patch adds an ELF alignment mismatch test to clearly identify when
this incompatibility occurs, to ease debugging in the future.
The test determines the value of kElfSegmentAlignment used by the
on-device ART APEX by reading the alignment of the boot.oat file.
Bug: 333480073
As the ELF segment alignment is controlled by enabling page-agnostic
mode (kPageSizeAgnostic), tests were run on Oriole running page-agnostic
4K and 16K as well legacy constexpr 4K. Test failures were confirmed
when page-agnostic standalone tests were run against a constexpr 4K ART
APEX, and vice versa.
Test: art/tools/run-gtests.sh
Test: atest ArtGtestsTargetChroot
Test: atest art_standalone_* -- --abi arm64-v8a
Test: atest art_standalone_runtime_tests -- --abi arm64-v8a
Change-Id: I1deac98167443766686c7a2c5b6f6c33166c9530
-rw-r--r-- | runtime/oat/elf_file.cc | 18 | ||||
-rw-r--r-- | runtime/oat/elf_file.h | 2 | ||||
-rw-r--r-- | runtime/oat/elf_file_impl.h | 3 | ||||
-rw-r--r-- | runtime/runtime_test.cc | 34 |
4 files changed, 57 insertions, 0 deletions
diff --git a/runtime/oat/elf_file.cc b/runtime/oat/elf_file.cc index 30e0197470..91af410471 100644 --- a/runtime/oat/elf_file.cc +++ b/runtime/oat/elf_file.cc @@ -1025,6 +1025,20 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) return GetLoadedAddressRange(&vaddr_begin, size, error_msg); } +template <typename ElfTypes> +size_t ElfFileImpl<ElfTypes>::GetElfSegmentAlignmentFromFile() const { + // Return the alignment of the first loadable program segment. + for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) { + Elf_Phdr* program_header = GetProgramHeader(i); + if (program_header->p_type != PT_LOAD) { + continue; + } + return program_header->p_align; + } + LOG(ERROR) << "No loadable segment found in ELF file " << file_path_; + return 0; +} + // Base on bionic phdr_table_get_load_size template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin, @@ -1671,6 +1685,10 @@ bool ElfFile::GetLoadedSize(size_t* size, std::string* error_msg) const { DELEGATE_TO_IMPL(GetLoadedSize, size, error_msg); } +size_t ElfFile::GetElfSegmentAlignmentFromFile() const { + DELEGATE_TO_IMPL(GetElfSegmentAlignmentFromFile); +} + bool ElfFile::Strip(File* file, std::string* error_msg) { std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb=*/false, error_msg)); if (elf_file.get() == nullptr) { diff --git a/runtime/oat/elf_file.h b/runtime/oat/elf_file.h index a9218106de..125b4441f1 100644 --- a/runtime/oat/elf_file.h +++ b/runtime/oat/elf_file.h @@ -82,6 +82,8 @@ class ElfFile { bool GetLoadedSize(size_t* size, std::string* error_msg) const; + size_t GetElfSegmentAlignmentFromFile() const; + // Strip an ELF file of unneeded debugging information. // Returns true on success, false on failure. static bool Strip(File* file, std::string* error_msg); diff --git a/runtime/oat/elf_file_impl.h b/runtime/oat/elf_file_impl.h index ffc6a2c081..67c60e906c 100644 --- a/runtime/oat/elf_file_impl.h +++ b/runtime/oat/elf_file_impl.h @@ -116,6 +116,9 @@ class ElfFileImpl { // Retrieves the expected size when the file is loaded at runtime. Returns true if successful. bool GetLoadedSize(size_t* size, std::string* error_msg) const; + // Get the alignment of the first loadable program segment. Return 0 if no loadable segment found. + size_t GetElfSegmentAlignmentFromFile() const; + // Load segments into memory based on PT_LOAD program headers. // executable is true at run time, false at compile time. bool Load(File* file, diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index 210ad7d711..cd0c862d82 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -21,9 +21,14 @@ #include "android-base/logging.h" #include "base/locks.h" #include "base/mutex.h" +#include "oat/elf_file.h" #include "runtime.h" #include "thread-current-inl.h" +#ifdef ART_TARGET_ANDROID +#include "android-base/properties.h" +#endif + namespace art HIDDEN { class RuntimeTest : public CommonRuntimeTest {}; @@ -75,4 +80,33 @@ TEST_F(RuntimeTest, AbortFromUnattachedThread) { }, ::testing::KilledBySignal(SIGABRT), kDeathRegex); } +// It is possible to run tests that validate an existing deployed on-device ART APEX ('standalone' +// tests). If these tests expect to load ELF files with a particular alignment, but those ELF files +// were created with a different alignment, there will be many difficult-to-debug failures. This +// test aims to identify this mismatch, related to whether or not the runtimes were built to be +// page-size agnostic. +TEST_F(RuntimeTest, ElfAlignmentMismatch) { +#ifdef ART_TARGET_ANDROID + bool platform_pga = android::base::GetBoolProperty("ro.product.build.no_bionic_page_size_macro", + false); + if (kPageSizeAgnostic != platform_pga) { + LOG(WARNING) << "Test configured with kPageSizeAgnostic=" << kPageSizeAgnostic << ", but " + << "platform ro.product.build.no_bionic_page_size_macro=" << platform_pga << "."; + } +#endif + // Determine the alignment of the ART APEX by reading the alignment of boot.oat. + std::string core_oat_location = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); + std::unique_ptr<File> core_oat_file(OS::OpenFileForReading(core_oat_location.c_str())); + ASSERT_TRUE(core_oat_file.get() != nullptr) << core_oat_location; + + std::string error_msg; + std::unique_ptr<ElfFile> elf_file(ElfFile::Open(core_oat_file.get(), + /*writable=*/false, + /*program_header_only=*/true, + /*low_4gb=*/false, + &error_msg)); + ASSERT_TRUE(elf_file != nullptr) << error_msg; + EXPECT_EQ(kElfSegmentAlignment, elf_file->GetElfSegmentAlignmentFromFile()); +} + } // namespace art |