summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibELF/DynamicLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibELF/DynamicLoader.cpp')
-rw-r--r--Userland/Libraries/LibELF/DynamicLoader.cpp173
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.
}