mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:57:35 +00:00
LibELF: Add support for loading objects with multiple data and text segments
This enables loading executables with multiple data and text segments. Also it fixes loading executables where the text segment has a non-zero offset. Example: $ echo "main () {}" > test.c $ gcc -Wl,-z,separate-code -o test test.c $ objdump -p test test: file format elf32-i386 Program Header: PHDR off 0x00000034 vaddr 0x00000034 paddr 0x00000034 align 2**2 filesz 0x000000e0 memsz 0x000000e0 flags r-- INTERP off 0x00000114 vaddr 0x00000114 paddr 0x00000114 align 2**0 filesz 0x00000013 memsz 0x00000013 flags r-- LOAD off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**12 filesz 0x000003c4 memsz 0x000003c4 flags r-- LOAD off 0x00001000 vaddr 0x00001000 paddr 0x00001000 align 2**12 filesz 0x00000279 memsz 0x00000279 flags r-x LOAD off 0x00002000 vaddr 0x00002000 paddr 0x00002000 align 2**12 filesz 0x00000004 memsz 0x00000004 flags r-- LOAD off 0x00002004 vaddr 0x00003004 paddr 0x00003004 align 2**12 filesz 0x00000100 memsz 0x00000124 flags rw- DYNAMIC off 0x00002014 vaddr 0x00003014 paddr 0x00003014 align 2**2 filesz 0x000000c8 memsz 0x000000c8 flags rw-
This commit is contained in:
parent
c3ee70591a
commit
cd7512a2ad
4 changed files with 121 additions and 77 deletions
|
@ -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.
|
||||
// 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.
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue