Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit fc89c8a

Browse files
committed
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
1 parent 5530c8e commit fc89c8a

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

linker/linker_phdr.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,7 @@ size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phd
567567
continue;
568568
}
569569

570-
if (phdr->p_align > maximum_alignment) {
571-
maximum_alignment = phdr->p_align;
572-
}
570+
maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
573571
}
574572

575573
#if defined(__LP64__)
@@ -579,6 +577,30 @@ size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phd
579577
#endif
580578
}
581579

580+
// Returns the minimum p_align associated with a loadable segment in the ELF
581+
// program header table. Used to determine if the program alignment is compatible
582+
// with the page size of this system.
583+
size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
584+
size_t minimum_alignment = page_size();
585+
586+
for (size_t i = 0; i < phdr_count; ++i) {
587+
const ElfW(Phdr)* phdr = &phdr_table[i];
588+
589+
// p_align must be 0, 1, or a positive, integral power of two.
590+
if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
591+
continue;
592+
}
593+
594+
if (phdr->p_align <= 1) {
595+
continue;
596+
}
597+
598+
minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
599+
}
600+
601+
return minimum_alignment;
602+
}
603+
582604
// Reserve a virtual address range such that if it's limits were extended to the next 2**align
583605
// boundary, it would not overlap with any existing mappings.
584606
static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
@@ -830,6 +852,15 @@ static inline void _extend_load_segment_vma(const ElfW(Phdr)* phdr_table, size_t
830852
}
831853

832854
bool ElfReader::LoadSegments() {
855+
size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
856+
// Only enforce this on 16 KB systems. Apps may rely on undefined behavior
857+
// here on 4 KB systems, which is the norm before this change is introduced.
858+
if (kPageSize >= 16384 && min_palign < kPageSize) {
859+
DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
860+
name_.c_str(), min_palign, kPageSize);
861+
return false;
862+
}
863+
833864
for (size_t i = 0; i < phdr_num_; ++i) {
834865
const ElfW(Phdr)* phdr = &phdr_table_[i];
835866

linker/linker_phdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
126126
ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr);
127127

128128
size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
129+
size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count);
129130

130131
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
131132
ElfW(Addr) load_bias, bool should_pad_segments,

0 commit comments

Comments
 (0)