Refactor the phdr max/min alignment code.
There's no reason to duplicate this code, nor to call either of these functions more than once.
Bug: http://b/183651285
Change-Id: Ie265e1b569d76942ae9d69f638f1ac4c2fa8a03d
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index e5369ac..f942562 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -159,8 +159,8 @@
ElfReader::ElfReader()
: did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
- strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr),
- mapped_by_caller_(false) {
+ strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), max_align_(0), min_align_(0),
+ loaded_phdr_(nullptr), mapped_by_caller_(false) {
}
bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
@@ -175,13 +175,14 @@
if (ReadElfHeader() &&
VerifyElfHeader() &&
ReadProgramHeaders() &&
+ CheckProgramHeaderAlignment() &&
ReadSectionHeaders() &&
ReadDynamicSection() &&
ReadPadSegmentNote()) {
did_read_ = true;
}
- if (kPageSize == 0x4000 && phdr_table_get_minimum_alignment(phdr_table_, phdr_num_) == 0x1000) {
+ if (kPageSize == 16*1024 && min_align_ == 4096) {
// This prop needs to be read on 16KiB devices for each ELF where min_palign is 4KiB.
// It cannot be cached since the developer may toggle app compat on/off.
// This check will be removed once app compat is made the default on 16KiB devices.
@@ -562,52 +563,28 @@
return max_vaddr - min_vaddr;
}
-// Returns the maximum p_align associated with a loadable segment in the ELF
-// program header table. Used to determine whether the file should be loaded at
-// a specific virtual address alignment for use with huge pages.
-size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
- size_t maximum_alignment = page_size();
+bool ElfReader::CheckProgramHeaderAlignment() {
+ max_align_ = min_align_ = page_size();
- for (size_t i = 0; i < phdr_count; ++i) {
- const ElfW(Phdr)* phdr = &phdr_table[i];
+ for (size_t i = 0; i < phdr_num_; ++i) {
+ const ElfW(Phdr)* phdr = &phdr_table_[i];
// p_align must be 0, 1, or a positive, integral power of two.
if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
+ // TODO: reject ELF files with bad p_align values.
continue;
}
- maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
- }
-
-#if defined(__LP64__)
- return maximum_alignment;
-#else
- return page_size();
+#if defined(__LP64__) // TODO: remove this historical accident #if
+ max_align_ = std::max(max_align_, static_cast<size_t>(phdr->p_align));
#endif
-}
-// Returns the minimum p_align associated with a loadable segment in the ELF
-// program header table. Used to determine if the program alignment is compatible
-// with the page size of this system.
-size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
- size_t minimum_alignment = page_size();
-
- for (size_t i = 0; i < phdr_count; ++i) {
- const ElfW(Phdr)* phdr = &phdr_table[i];
-
- // p_align must be 0, 1, or a positive, integral power of two.
- if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
- continue;
+ if (phdr->p_align > 1) {
+ min_align_ = std::min(min_align_, static_cast<size_t>(phdr->p_align));
}
-
- if (phdr->p_align <= 1) {
- continue;
- }
-
- minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
}
- return minimum_alignment;
+ return true;
}
// Reserve a virtual address range such that if it's limits were extended to the next 2**align
@@ -717,10 +694,9 @@
}
size_t start_alignment = page_size();
if (get_transparent_hugepages_supported() && get_application_target_sdk_version() >= 31) {
- size_t maximum_alignment = phdr_table_get_maximum_alignment(phdr_table_, phdr_num_);
// Limit alignment to PMD size as other alignments reduce the number of
// bits available for ASLR for no benefit.
- start_alignment = maximum_alignment == kPmdSize ? kPmdSize : page_size();
+ start_alignment = max_align_ == kPmdSize ? kPmdSize : page_size();
}
start = ReserveWithAlignmentPadding(load_size_, kLibraryAlignment, start_alignment, &gap_start_,
&gap_size_);
@@ -1011,13 +987,12 @@
// are not 16KiB aligned.
size_t seg_align = should_use_16kib_app_compat_ ? kCompatPageSize : kPageSize;
- size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
// Only enforce this on 16 KB systems with app compat disabled.
// Apps may rely on undefined behavior here on 4 KB systems,
// which is the norm before this change is introduced
- if (kPageSize >= 16384 && min_palign < kPageSize && !should_use_16kib_app_compat_) {
+ if (kPageSize >= 16384 && min_align_ < kPageSize && !should_use_16kib_app_compat_) {
DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
- name_.c_str(), min_palign, kPageSize);
+ name_.c_str(), min_align_, kPageSize);
return false;
}
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index e15ece4..3b68528 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -76,6 +76,7 @@
[[nodiscard]] bool ReadElfHeader();
[[nodiscard]] bool VerifyElfHeader();
[[nodiscard]] bool ReadProgramHeaders();
+ [[nodiscard]] bool CheckProgramHeaderAlignment();
[[nodiscard]] bool ReadSectionHeaders();
[[nodiscard]] bool ReadDynamicSection();
[[nodiscard]] bool ReadPadSegmentNote();
@@ -130,6 +131,10 @@
// Load bias.
ElfW(Addr) load_bias_;
+ // Maximum and minimum alignment requirements across all phdrs.
+ size_t max_align_;
+ size_t min_align_;
+
// Loaded phdr.
const ElfW(Phdr)* loaded_phdr_;
@@ -153,9 +158,6 @@
size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr);
-size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
-size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
-
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
ElfW(Addr) load_bias, bool should_pad_segments,
bool should_use_16kib_app_compat,