diff options
Diffstat (limited to 'Userland/Libraries/LibELF/DynamicLoader.cpp')
-rw-r--r-- | Userland/Libraries/LibELF/DynamicLoader.cpp | 173 |
1 files changed, 100 insertions, 73 deletions
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 <AK/Debug.h> #include <AK/Optional.h> +#include <AK/QuickSort.h> #include <AK/StringBuilder.h> #include <LibELF/DynamicLinker.h> #include <LibELF/DynamicLoader.h> @@ -167,7 +168,9 @@ RefPtr<DynamicObject> 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<DynamicObject> 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<ProgramHeaderRegion> text_region; - Optional<ProgramHeaderRegion> data_region; + Vector<ProgramHeaderRegion> load_regions; + Vector<ProgramHeaderRegion> text_regions; + Vector<ProgramHeaderRegion> data_regions; Optional<ProgramHeaderRegion> tls_region; Optional<ProgramHeaderRegion> 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. - - 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); + // to hold all regions contiguously in the address space. - size_t total_mapping_size = ph_data_end - ph_text_base; + 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 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()); - - if (text_segment_begin == MAP_FAILED) { - perror("mmap text"); - VERIFY_NOT_REACHED(); + 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; + + 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; - - // 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; + 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; + + auto data_segment_offset = ph_data_base - ph_load_base; + auto* data_segment_address = (u8*)reservation + data_segment_offset; + + // 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(); + } - 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()); + 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(); - if (MAP_FAILED == data_segment) { - perror("mmap data"); - VERIFY_NOT_REACHED(); + 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. } |