From 2e349337d3de20d1f2f3725ec394b8e1ae97a53d Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Wed, 8 Jan 2020 21:38:05 -0700 Subject: [PATCH] LibELF: Map .text segment with MAP_ANONYMOUS for shared objects We need to workaround the fact that MAP_PRIVATE when passed a file descriptor doesn't work the way we expect. We can't change the permissions on our mmap to PROT_WRITE if the original executable doesn't have PROT_WRITE. Because of this, we need to construct our ELFDynamicObject using the actual virtual address of the .dynamic section, instead of using the offset into the ELFImage that was actually getting modified by accident ...somehow. Not clear what was going on. --- Libraries/LibELF/ELFDynamicLoader.cpp | 31 ++++++++++++++++----------- Libraries/LibELF/ELFDynamicLoader.h | 1 + Libraries/LibELF/ELFDynamicObject.cpp | 6 +++--- Libraries/LibELF/ELFDynamicObject.h | 6 +++--- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Libraries/LibELF/ELFDynamicLoader.cpp b/Libraries/LibELF/ELFDynamicLoader.cpp index 2a6dcb0780..c6b463d34e 100644 --- a/Libraries/LibELF/ELFDynamicLoader.cpp +++ b/Libraries/LibELF/ELFDynamicLoader.cpp @@ -32,7 +32,8 @@ ELFDynamicLoader::ELFDynamicLoader(const char* filename, int fd, size_t size) { String file_mmap_name = String::format("ELF_DYN: %s", m_filename.characters()); - m_file_mapping = mmap_with_name(nullptr, size, PROT_READ, MAP_PRIVATE, m_image_fd, 0, file_mmap_name.characters()); + // FIXME: When MAP_PRIVATE is implemented for file-backed regions, change to MAP_PRIVATE + m_file_mapping = mmap_with_name(nullptr, size, PROT_READ, MAP_SHARED, m_image_fd, 0, file_mmap_name.characters()); if (MAP_FAILED == m_file_mapping) { m_valid = false; } @@ -64,20 +65,13 @@ bool ELFDynamicLoader::load_from_image(unsigned flags) return false; } - const ELFImage::DynamicSection probably_dynamic_section = elf_image.dynamic_section(); - if (StringView(".dynamic") != probably_dynamic_section.name() || probably_dynamic_section.type() != SHT_DYNAMIC) { - m_valid = false; - return false; - } - #ifdef DYNAMIC_LOAD_VERBOSE m_image->dump(); #endif load_program_headers(elf_image); - const ELFImage::DynamicSection image_dynamic_section = elf_image.dynamic_section(); - m_dynamic_object = AK::make(m_text_segment_load_address, image_dynamic_section.offset()); + m_dynamic_object = AK::make(m_text_segment_load_address, m_dynamic_section_address); return load_stage_2(flags); } @@ -94,7 +88,7 @@ bool ELFDynamicLoader::load_stage_2(unsigned flags) if (m_dynamic_object->has_text_relocations()) { ASSERT(m_text_segment_load_address.get() != 0); if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_WRITE)) { - perror("mprotect"); // FIXME: dlerror? + perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror? return false; } } @@ -105,7 +99,7 @@ bool ELFDynamicLoader::load_stage_2(unsigned flags) // Clean up our setting of .text to PROT_READ | PROT_WRITE if (m_dynamic_object->has_text_relocations()) { if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_EXEC)) { - perror("mprotect"); // FIXME: dlerror? + perror("mprotect .text: PROT_READ | PROT_EXEC"); // FIXME: dlerror? return false; } } @@ -127,6 +121,7 @@ void ELFDynamicLoader::load_program_headers(const ELFImage& elf_image) ProgramHeaderRegion* text_region_ptr = nullptr; ProgramHeaderRegion* data_region_ptr = nullptr; ProgramHeaderRegion* tls_region_ptr = nullptr; + VirtualAddress dynamic_region_desired_vaddr; elf_image.for_each_program_header([&](const ELFImage::ProgramHeader& program_header) { ProgramHeaderRegion new_region; @@ -143,18 +138,30 @@ void ELFDynamicLoader::load_program_headers(const ELFImage& elf_image) else data_region_ptr = ®ion; } + else if (region.is_dynamic()) { + dynamic_region_desired_vaddr = region.desired_load_address(); + } }); ASSERT(text_region_ptr && data_region_ptr); // Process regions in order: .text, .data, .tls auto* region = text_region_ptr; - void* text_segment_begin = mmap_with_name(nullptr, region->required_load_size(), region->mmap_prot(), MAP_PRIVATE, m_image_fd, region->offset(), String::format(".text: %s", m_filename.characters()).characters()); + // FIXME: When MAP_PRIVATE is implemented for file-backed regions, change to MAP_PRIVATE without the mprotect and memcpy + //void* text_segment_begin = mmap_with_name(nullptr, region->required_load_size(), region->mmap_prot(), MAP_PRIVATE, m_image_fd, region->offset(), String::format(".text: %s", m_filename.characters()).characters()); + void* text_segment_begin = mmap_with_name(nullptr, region->required_load_size(), PROT_READ | PROT_WRITE , MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, String::format(".text: %s", m_filename.characters()).characters()); if (MAP_FAILED == text_segment_begin) { ASSERT_NOT_REACHED(); } m_text_segment_size = region->required_load_size(); m_text_segment_load_address = VirtualAddress { (u32)text_segment_begin }; + memcpy(m_text_segment_load_address.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image()); + if (0 > mprotect(text_segment_begin, m_text_segment_size, region->mmap_prot())) { + perror("mprotect .text PROT_READ | PROT_EXEC"); + ASSERT_NOT_REACHED(); + } + + m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get()); region = data_region_ptr; void* data_segment_begin = mmap_with_name((u8*)text_segment_begin + m_text_segment_size, region->required_load_size(), region->mmap_prot(), MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, String::format(".data: %s", m_filename.characters()).characters()); diff --git a/Libraries/LibELF/ELFDynamicLoader.h b/Libraries/LibELF/ELFDynamicLoader.h index 4fb5e1d32c..81fa0f384d 100644 --- a/Libraries/LibELF/ELFDynamicLoader.h +++ b/Libraries/LibELF/ELFDynamicLoader.h @@ -86,4 +86,5 @@ private: size_t m_text_segment_size; VirtualAddress m_tls_segment_address; + VirtualAddress m_dynamic_section_address; }; diff --git a/Libraries/LibELF/ELFDynamicObject.cpp b/Libraries/LibELF/ELFDynamicObject.cpp index ef2ffbef40..3f073a489d 100644 --- a/Libraries/LibELF/ELFDynamicObject.cpp +++ b/Libraries/LibELF/ELFDynamicObject.cpp @@ -8,9 +8,9 @@ static const char* name_for_dtag(Elf32_Sword d_tag); -ELFDynamicObject::ELFDynamicObject(VirtualAddress base_address, u32 dynamic_offset) +ELFDynamicObject::ELFDynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_addresss) : m_base_address(base_address) - , m_dynamic_offset(dynamic_offset) + , m_dynamic_address(dynamic_section_addresss) { parse(); } @@ -32,7 +32,7 @@ void ELFDynamicObject::dump() const return IterationDecision::Continue; }); - dbgprintf("Dynamic section at offset 0x%x contains %zu entries:\n", m_dynamic_offset, num_dynamic_sections); + dbgprintf("Dynamic section at address 0x%x contains %zu entries:\n", m_dynamic_address.as_ptr(), num_dynamic_sections); dbgprintf(builder.to_string().characters()); } diff --git a/Libraries/LibELF/ELFDynamicObject.h b/Libraries/LibELF/ELFDynamicObject.h index 42a5ea9114..45bbd0de42 100644 --- a/Libraries/LibELF/ELFDynamicObject.h +++ b/Libraries/LibELF/ELFDynamicObject.h @@ -6,7 +6,7 @@ class ELFDynamicObject { public: - explicit ELFDynamicObject(VirtualAddress base_address, u32 dynamic_offset); + explicit ELFDynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address); ~ELFDynamicObject(); void dump() const; @@ -196,7 +196,7 @@ private: void for_each_dynamic_entry(F) const; VirtualAddress m_base_address; - u32 m_dynamic_offset; + VirtualAddress m_dynamic_address; Symbol m_the_undefined_symbol { *this, 0, {} }; unsigned m_symbol_count { 0 }; @@ -255,7 +255,7 @@ inline void ELFDynamicObject::for_each_symbol(F func) const template inline void ELFDynamicObject::for_each_dynamic_entry(F func) const { - auto* dyns = reinterpret_cast(m_base_address.offset(m_dynamic_offset).as_ptr()); + auto* dyns = reinterpret_cast(m_dynamic_address.as_ptr()); for (unsigned i = 0;; ++i) { auto&& dyn = DynamicEntry(dyns[i]); if (dyn.tag() == DT_NULL)