Linker: clear error for incorrect page size load.
When the alignment of an ELF program is smaller than page size,
the program is malformed, and the linker will crash with an unclear
error:
dlopen failed: empty/missing DT_HASH/DT_GNU_HASH
Now, we explicitly check the alignment, only on >= 16KB page size
machines, so we get a clearer error:
Test: e.g.:
dlopen failed: "/data/local/tmp/bionic-unit-tests/x86_64/bionic-loader-test-libs/prebuilt-elf-files/libtest_invalid-rw_load_segment.so" program alignment (4096) cannot be smaller than system page size (16384)
Bug: 342480592
Change-Id: I2fa50c0d8cca34acde03d71d8dc600dc3858ca04
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index b9229ca..30f3161 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -567,9 +567,7 @@
continue;
}
- if (phdr->p_align > maximum_alignment) {
- maximum_alignment = phdr->p_align;
- }
+ maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
}
#if defined(__LP64__)
@@ -579,6 +577,30 @@
#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) {
+ continue;
+ }
+
+ minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
+ }
+
+ return minimum_alignment;
+}
+
// Reserve a virtual address range such that if it's limits were extended to the next 2**align
// boundary, it would not overlap with any existing mappings.
static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
@@ -830,6 +852,15 @@
}
bool ElfReader::LoadSegments() {
+ size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
+ // Only enforce this on 16 KB systems. 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) {
+ DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
+ name_.c_str(), min_palign, kPageSize);
+ return false;
+ }
+
for (size_t i = 0; i < phdr_num_; ++i) {
const ElfW(Phdr)* phdr = &phdr_table_[i];