diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index a598fe8c56..439229e246 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -276,7 +276,7 @@ void ELF::DynamicLinker::linker_main(String&& main_program_name, int main_progra auto main_executable_loader = load_main_executable(main_program_name); auto entry_point = main_executable_loader->image().entry(); if (main_executable_loader->is_dynamic()) - entry_point = entry_point.offset(main_executable_loader->text_segment_load_address().get()); + entry_point = entry_point.offset(main_executable_loader->base_address().get()); return (EntryPointFunction)(entry_point.as_ptr()); }(); diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index b019063911..6bd8d69eb7 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -167,7 +168,9 @@ RefPtr DynamicLoader::map() load_program_headers(); - m_dynamic_object = DynamicObject::create(m_text_segment_load_address, m_dynamic_section_address); + VERIFY(!m_base_address.is_null()); + + m_dynamic_object = DynamicObject::create(m_base_address, m_dynamic_section_address); m_dynamic_object->set_tls_offset(m_tls_offset); m_dynamic_object->set_tls_size(m_tls_size); @@ -184,19 +187,21 @@ bool DynamicLoader::load_stage_2(unsigned flags, size_t total_tls_size) VERIFY(flags & RTLD_GLOBAL); if (m_dynamic_object->has_text_relocations()) { - VERIFY(m_text_segment_load_address.get() != 0); + for (auto& text_segment : m_text_segments) { + VERIFY(text_segment.address().get() != 0); #ifndef AK_OS_MACOS - // Remap this text region as private. - if (mremap(m_text_segment_load_address.as_ptr(), m_text_segment_size, m_text_segment_size, MAP_PRIVATE) == MAP_FAILED) { - perror("mremap .text: MAP_PRIVATE"); - return false; - } + // Remap this text region as private. + if (mremap(text_segment.address().as_ptr(), text_segment.size(), text_segment.size(), MAP_PRIVATE) == MAP_FAILED) { + perror("mremap .text: MAP_PRIVATE"); + return false; + } #endif - if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_WRITE)) { - perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror? - return false; + if (0 > mprotect(text_segment.address().as_ptr(), text_segment.size(), PROT_READ | PROT_WRITE)) { + perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror? + return false; + } } } do_main_relocations(total_tls_size); @@ -230,9 +235,11 @@ RefPtr DynamicLoader::load_stage_3(unsigned flags, size_t total_t setup_plt_trampoline(); } - if (mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_EXEC) < 0) { - perror("mprotect .text: PROT_READ | PROT_EXEC"); // FIXME: dlerror? - return nullptr; + for (auto& text_segment : m_text_segments) { + if (mprotect(text_segment.address().as_ptr(), text_segment.size(), PROT_READ | PROT_EXEC) < 0) { + perror("mprotect .text: PROT_READ | PROT_EXEC"); // FIXME: dlerror? + return nullptr; + } } if (m_relro_segment_size) { @@ -269,8 +276,9 @@ void DynamicLoader::do_lazy_relocations(size_t total_tls_size) void DynamicLoader::load_program_headers() { - Optional text_region; - Optional data_region; + Vector load_regions; + Vector text_regions; + Vector data_regions; Optional tls_region; Optional relro_region; @@ -283,12 +291,11 @@ void DynamicLoader::load_program_headers() VERIFY(!tls_region.has_value()); tls_region = region; } else if (region.is_load()) { + load_regions.append(region); if (region.is_executable()) { - VERIFY(!text_region.has_value()); - text_region = region; + text_regions.append(region); } else { - VERIFY(!data_region.has_value()); - data_region = region; + data_regions.append(region); } } else if (region.is_dynamic()) { dynamic_region_desired_vaddr = region.desired_load_address(); @@ -299,11 +306,19 @@ void DynamicLoader::load_program_headers() return IterationDecision::Continue; }); - VERIFY(text_region.has_value()); - VERIFY(data_region.has_value()); + VERIFY(!text_regions.is_empty()); + VERIFY(!data_regions.is_empty()); + + auto compare_load_address = [](ProgramHeaderRegion& a, ProgramHeaderRegion& b) { + return a.desired_load_address().as_ptr() < b.desired_load_address().as_ptr(); + }; + + quick_sort(load_regions, compare_load_address); + quick_sort(text_regions, compare_load_address); + quick_sort(data_regions, compare_load_address); // Process regions in order: .text, .data, .tls - void* requested_load_address = m_elf_image.is_dynamic() ? nullptr : text_region.value().desired_load_address().as_ptr(); + void* requested_load_address = m_elf_image.is_dynamic() ? nullptr : load_regions.first().desired_load_address().as_ptr(); int reservation_mmap_flags = MAP_ANON | MAP_PRIVATE | MAP_NORESERVE; if (m_elf_image.is_dynamic()) @@ -311,20 +326,16 @@ void DynamicLoader::load_program_headers() else reservation_mmap_flags |= MAP_FIXED; - VERIFY(!text_region.value().is_writable()); + for (auto& text_region : text_regions) + VERIFY(!text_region.is_writable()); // First, we make a dummy reservation mapping, in order to allocate enough VM - // to hold both text+data contiguously in the address space. + // to hold all regions contiguously in the address space. - FlatPtr ph_text_base = text_region.value().desired_load_address().page_base().get(); - FlatPtr ph_text_end = round_up_to_power_of_two(text_region.value().desired_load_address().offset(text_region.value().size_in_memory()).get(), PAGE_SIZE); - FlatPtr ph_data_base = data_region.value().desired_load_address().page_base().get(); - FlatPtr ph_data_end = round_up_to_power_of_two(data_region.value().desired_load_address().offset(data_region.value().size_in_memory()).get(), PAGE_SIZE); + FlatPtr ph_load_base = load_regions.first().desired_load_address().page_base().get(); + FlatPtr ph_load_end = round_up_to_power_of_two(load_regions.last().desired_load_address().offset(load_regions.last().size_in_memory()).get(), PAGE_SIZE); - size_t total_mapping_size = ph_data_end - ph_text_base; - - size_t text_segment_size = ph_text_end - ph_text_base; - size_t data_segment_size = ph_data_end - ph_data_base; + size_t total_mapping_size = ph_load_end - ph_load_base; auto* reservation = mmap(requested_load_address, total_mapping_size, PROT_NONE, reservation_mmap_flags, 0, 0); if (reservation == MAP_FAILED) { @@ -332,68 +343,84 @@ void DynamicLoader::load_program_headers() VERIFY_NOT_REACHED(); } + m_base_address = VirtualAddress { reservation }; + // Then we unmap the reservation. if (munmap(reservation, total_mapping_size) < 0) { perror("munmap reservation"); VERIFY_NOT_REACHED(); } - // Now we can map the text segment at the reserved address. - auto* text_segment_begin = (u8*)mmap_with_name( - reservation, - text_segment_size, - PROT_READ, - MAP_FILE | MAP_SHARED | MAP_FIXED, - m_image_fd, - text_region.value().offset(), - String::formatted("{}: .text", m_filename).characters()); + for (auto& text_region : text_regions) { + FlatPtr ph_text_base = text_region.desired_load_address().page_base().get(); + FlatPtr ph_text_end = round_up_to_power_of_two(text_region.desired_load_address().offset(text_region.size_in_memory()).get(), PAGE_SIZE); + size_t text_segment_size = ph_text_end - ph_text_base; - if (text_segment_begin == MAP_FAILED) { - perror("mmap text"); - VERIFY_NOT_REACHED(); + auto text_segment_offset = ph_text_base - ph_load_base; + auto* text_segment_address = (u8*)reservation + text_segment_offset; + + // Now we can map the text segment at the reserved address. + auto* text_segment_begin = (u8*)mmap_with_name( + text_segment_address, + text_segment_size, + PROT_READ, + MAP_FILE | MAP_SHARED | MAP_FIXED, + m_image_fd, + text_region.offset(), + String::formatted("{}: .text", m_filename).characters()); + + if (text_segment_begin == MAP_FAILED) { + perror("mmap text"); + VERIFY_NOT_REACHED(); + } + + m_text_segments.append({ VirtualAddress { (FlatPtr)text_segment_begin }, text_segment_size }); } - VERIFY(requested_load_address == nullptr || requested_load_address == text_segment_begin); - m_text_segment_size = text_segment_size; - m_text_segment_load_address = VirtualAddress { (FlatPtr)text_segment_begin }; + VERIFY(requested_load_address == nullptr || requested_load_address == reservation); if (relro_region.has_value()) { m_relro_segment_size = relro_region->size_in_memory(); - m_relro_segment_address = m_text_segment_load_address.offset(relro_region->desired_load_address().get()); + m_relro_segment_address = VirtualAddress { (u8*)reservation + relro_region->desired_load_address().get() }; } if (m_elf_image.is_dynamic()) - m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get()); + m_dynamic_section_address = VirtualAddress { (u8*)reservation + dynamic_region_desired_vaddr.get() }; else m_dynamic_section_address = dynamic_region_desired_vaddr; - FlatPtr data_segment_offset_from_text = ph_data_base - ph_text_base; + for (auto& data_region : data_regions) { + FlatPtr ph_data_base = data_region.desired_load_address().page_base().get(); + FlatPtr ph_data_end = round_up_to_power_of_two(data_region.desired_load_address().offset(data_region.size_in_memory()).get(), PAGE_SIZE); + size_t data_segment_size = ph_data_end - ph_data_base; - // Finally, we make an anonymous mapping for the data segment. Contents are then copied from the file. - auto* data_segment_address = (u8*)text_segment_begin + data_segment_offset_from_text; + auto data_segment_offset = ph_data_base - ph_load_base; + auto* data_segment_address = (u8*)reservation + data_segment_offset; - auto* data_segment = (u8*)mmap_with_name( - data_segment_address, - data_segment_size, - PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, - 0, - 0, - String::formatted("{}: .data", m_filename).characters()); + // Finally, we make an anonymous mapping for the data segment. Contents are then copied from the file. + auto* data_segment = (u8*)mmap_with_name( + data_segment_address, + data_segment_size, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, + 0, + 0, + String::formatted("{}: .data", m_filename).characters()); - if (MAP_FAILED == data_segment) { - perror("mmap data"); - VERIFY_NOT_REACHED(); + if (MAP_FAILED == data_segment) { + perror("mmap data"); + VERIFY_NOT_REACHED(); + } + + VirtualAddress data_segment_start; + if (m_elf_image.is_dynamic()) + data_segment_start = VirtualAddress { (u8*)reservation + data_region.desired_load_address().get() }; + else + data_segment_start = data_region.desired_load_address(); + + memcpy(data_segment_start.as_ptr(), (u8*)m_file_data + data_region.offset(), data_region.size_in_image()); } - VirtualAddress data_segment_start; - if (m_elf_image.is_dynamic()) - data_segment_start = data_region.value().desired_load_address().offset((FlatPtr)text_segment_begin); - else - data_segment_start = data_region.value().desired_load_address(); - - memcpy(data_segment_start.as_ptr(), (u8*)m_file_data + data_region.value().offset(), data_region.value().size_in_image()); - // FIXME: Initialize the values in the TLS section. Currently, it is zeroed. } diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h index 4e63af6366..3c3d17533d 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.h +++ b/Userland/Libraries/LibELF/DynamicLoader.h @@ -38,6 +38,22 @@ namespace ELF { +class LoadedSegment { +public: + LoadedSegment(VirtualAddress address, size_t size) + : m_address(address) + , m_size(size) + { + } + + VirtualAddress address() const { return m_address; } + size_t size() const { return m_size; } + +private: + VirtualAddress m_address; + size_t m_size; +}; + class DynamicLoader : public RefCounted { public: static RefPtr try_create(int fd, String filename); @@ -74,7 +90,8 @@ public: template void for_each_needed_library(F) const; - VirtualAddress text_segment_load_address() const { return m_text_segment_load_address; } + VirtualAddress base_address() const { return m_base_address; } + const Vector text_segments() const { return m_text_segments; } bool is_dynamic() const { return m_elf_image.is_dynamic(); } static Optional lookup_symbol(const ELF::DynamicObject::Symbol&); @@ -141,8 +158,8 @@ private: RefPtr m_dynamic_object; - VirtualAddress m_text_segment_load_address; - size_t m_text_segment_size { 0 }; + VirtualAddress m_base_address; + Vector m_text_segments; VirtualAddress m_relro_segment_address; size_t m_relro_segment_size { 0 }; diff --git a/Userland/Libraries/LibELF/DynamicObject.cpp b/Userland/Libraries/LibELF/DynamicObject.cpp index c379d21e82..25778fd19a 100644 --- a/Userland/Libraries/LibELF/DynamicObject.cpp +++ b/Userland/Libraries/LibELF/DynamicObject.cpp @@ -38,9 +38,9 @@ namespace ELF { static const char* name_for_dtag(Elf32_Sword d_tag); -DynamicObject::DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_addresss) +DynamicObject::DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address) : m_base_address(base_address) - , m_dynamic_address(dynamic_section_addresss) + , m_dynamic_address(dynamic_section_address) { auto* header = (Elf32_Ehdr*)base_address.as_ptr(); auto* pheader = (Elf32_Phdr*)(base_address.as_ptr() + header->e_phoff);