1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:57:45 +00:00

Loader: Support loading non-position independent executables

This commit is contained in:
Itamar 2020-12-18 15:59:22 +02:00 committed by Andreas Kling
parent 0cb636078a
commit a83a9f3a55
5 changed files with 88 additions and 41 deletions

View file

@ -144,9 +144,10 @@ void* DynamicLoader::symbol_for_name(const char* name)
RefPtr<DynamicObject> DynamicLoader::load_from_image(unsigned flags, size_t total_tls_size) RefPtr<DynamicObject> DynamicLoader::load_from_image(unsigned flags, size_t total_tls_size)
{ {
m_valid = m_elf_image.is_valid() && m_elf_image.is_dynamic(); m_valid = m_elf_image.is_valid();
if (!m_valid) { if (!m_valid) {
dbgprintf("DynamicLoader::load_from_image failed: image is invalid\n");
return nullptr; return nullptr;
} }
@ -163,8 +164,10 @@ RefPtr<DynamicObject> DynamicLoader::load_from_image(unsigned flags, size_t tota
m_dynamic_object->m_global_symbol_lookup_func = m_global_symbol_lookup_func; m_dynamic_object->m_global_symbol_lookup_func = m_global_symbol_lookup_func;
auto rc = load_stage_2(flags, total_tls_size); auto rc = load_stage_2(flags, total_tls_size);
if (!rc) if (!rc) {
dbgprintf("DynamicLoader::load_from_image failed at load_stage_2\n");
return nullptr; return nullptr;
}
return m_dynamic_object; return m_dynamic_object;
} }
@ -235,7 +238,9 @@ void DynamicLoader::load_program_headers()
// Process regions in order: .text, .data, .tls // Process regions in order: .text, .data, .tls
auto* region = text_region_ptr; auto* region = text_region_ptr;
void* text_segment_begin = mmap_with_name(nullptr, void* requested_load_address = m_elf_image.is_dynamic() ? nullptr : region->desired_load_address().as_ptr();
void* text_segment_begin = mmap_with_name(
requested_load_address,
region->required_load_size(), region->required_load_size(),
region->mmap_prot(), region->mmap_prot(),
MAP_PRIVATE, MAP_PRIVATE,
@ -245,13 +250,18 @@ void DynamicLoader::load_program_headers()
if (MAP_FAILED == text_segment_begin) { if (MAP_FAILED == text_segment_begin) {
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
ASSERT(requested_load_address == nullptr || requested_load_address == text_segment_begin);
m_text_segment_size = region->required_load_size(); m_text_segment_size = region->required_load_size();
m_text_segment_load_address = VirtualAddress { (FlatPtr)text_segment_begin }; m_text_segment_load_address = VirtualAddress { (FlatPtr)text_segment_begin };
m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get()); if (m_elf_image.is_dynamic())
m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get());
else
m_dynamic_section_address = dynamic_region_desired_vaddr;
region = data_region_ptr; region = data_region_ptr;
void* data_segment_begin = mmap_with_name((u8*)text_segment_begin + m_text_segment_size, void* data_segment_begin = mmap_with_name(
(u8*)text_segment_begin + m_text_segment_size,
region->required_load_size(), region->required_load_size(),
region->mmap_prot(), region->mmap_prot(),
MAP_ANONYMOUS | MAP_PRIVATE, MAP_ANONYMOUS | MAP_PRIVATE,
@ -261,10 +271,14 @@ void DynamicLoader::load_program_headers()
if (MAP_FAILED == data_segment_begin) { if (MAP_FAILED == data_segment_begin) {
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
VirtualAddress data_segment_actual_addr = region->desired_load_address().offset((FlatPtr)text_segment_begin); VirtualAddress data_segment_actual_addr;
if (m_elf_image.is_dynamic()) {
data_segment_actual_addr = region->desired_load_address().offset((FlatPtr)text_segment_begin);
} else {
data_segment_actual_addr = region->desired_load_address();
}
memcpy(data_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image()); memcpy(data_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image());
// FIXME: Initialize the values in the TLS section. Currently, it is zeroed.
// FIXME: Initialize the values in the TLS section. Currently, it will always be zeroed.
} }
void DynamicLoader::do_relocations(size_t total_tls_size) void DynamicLoader::do_relocations(size_t total_tls_size)
@ -272,7 +286,12 @@ void DynamicLoader::do_relocations(size_t total_tls_size)
auto main_relocation_section = m_dynamic_object->relocation_section(); auto main_relocation_section = m_dynamic_object->relocation_section();
main_relocation_section.for_each_relocation([&](ELF::DynamicObject::Relocation relocation) { main_relocation_section.for_each_relocation([&](ELF::DynamicObject::Relocation relocation) {
VERBOSE("Relocation symbol: %s, type: %d\n", relocation.symbol().name(), relocation.type()); VERBOSE("Relocation symbol: %s, type: %d\n", relocation.symbol().name(), relocation.type());
u32* patch_ptr = (u32*)(m_dynamic_object->base_address().as_ptr() + relocation.offset()); FlatPtr* patch_ptr = nullptr;
if (is_dynamic())
patch_ptr = (FlatPtr*)(m_dynamic_object->base_address().as_ptr() + relocation.offset());
else
patch_ptr = (FlatPtr*)(FlatPtr)relocation.offset();
// VERBOSE("dynamic object name: %s\n", dynamic_object.object_name()); // VERBOSE("dynamic object name: %s\n", dynamic_object.object_name());
VERBOSE("dynamic object base address: %p\n", m_dynamic_object->base_address()); VERBOSE("dynamic object base address: %p\n", m_dynamic_object->base_address());
VERBOSE("relocation offset: 0x%x\n", relocation.offset()); VERBOSE("relocation offset: 0x%x\n", relocation.offset());
@ -393,13 +412,12 @@ void DynamicLoader::do_relocations(size_t total_tls_size)
VERBOSE("patching plt reloaction: 0x%x\n", relocation.offset_in_section()); VERBOSE("patching plt reloaction: 0x%x\n", relocation.offset_in_section());
[[maybe_unused]] auto rc = m_dynamic_object->patch_plt_entry(relocation.offset_in_section()); [[maybe_unused]] auto rc = m_dynamic_object->patch_plt_entry(relocation.offset_in_section());
} else { } else {
// LAZY-ily bind the PLT slots by just adding the base address to the offsets stored there
// This avoids doing symbol lookup, which might be expensive
ASSERT(relocation.type() == R_386_JMP_SLOT); ASSERT(relocation.type() == R_386_JMP_SLOT);
u8* relocation_address = relocation.address().as_ptr(); u8* relocation_address = relocation.address().as_ptr();
*(u32*)relocation_address += (FlatPtr)m_dynamic_object->base_address().as_ptr(); if (m_elf_image.is_dynamic())
*(u32*)relocation_address += (FlatPtr)m_dynamic_object->base_address().as_ptr();
} }
return IterationDecision::Continue; return IterationDecision::Continue;
}); });

View file

@ -79,6 +79,7 @@ public:
void set_global_symbol_lookup_function(DynamicObject::SymbolLookupFunction func) { m_global_symbol_lookup_func = func; } void set_global_symbol_lookup_function(DynamicObject::SymbolLookupFunction func) { m_global_symbol_lookup_func = func; }
VirtualAddress text_segment_load_address() const { return m_text_segment_load_address; } VirtualAddress text_segment_load_address() const { return m_text_segment_load_address; }
bool is_dynamic() const { return m_elf_image.is_dynamic(); }
private: private:
class ProgramHeaderRegion { class ProgramHeaderRegion {

View file

@ -50,6 +50,14 @@ DynamicObject::DynamicObject(VirtualAddress base_address, VirtualAddress dynamic
: m_base_address(base_address) : m_base_address(base_address)
, m_dynamic_address(dynamic_section_addresss) , m_dynamic_address(dynamic_section_addresss)
{ {
Elf32_Ehdr* header = (Elf32_Ehdr*)base_address.as_ptr();
Elf32_Phdr* pheader = (Elf32_Phdr*)(base_address.as_ptr() + header->e_phoff);
m_elf_base_address = VirtualAddress(pheader->p_vaddr - pheader->p_offset);
if (header->e_type == ET_DYN)
m_is_elf_dynamic = true;
else
m_is_elf_dynamic = false;
parse(); parse();
} }
@ -82,31 +90,31 @@ void DynamicObject::parse()
for_each_dynamic_entry([&](const DynamicEntry& entry) { for_each_dynamic_entry([&](const DynamicEntry& entry) {
switch (entry.tag()) { switch (entry.tag()) {
case DT_INIT: case DT_INIT:
m_init_offset = entry.ptr(); m_init_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_FINI: case DT_FINI:
m_fini_offset = entry.ptr(); m_fini_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_INIT_ARRAY: case DT_INIT_ARRAY:
m_init_array_offset = entry.ptr(); m_init_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_INIT_ARRAYSZ: case DT_INIT_ARRAYSZ:
m_init_array_size = entry.val(); m_init_array_size = entry.val();
break; break;
case DT_FINI_ARRAY: case DT_FINI_ARRAY:
m_fini_array_offset = entry.ptr(); m_fini_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_FINI_ARRAYSZ: case DT_FINI_ARRAYSZ:
m_fini_array_size = entry.val(); m_fini_array_size = entry.val();
break; break;
case DT_HASH: case DT_HASH:
m_hash_table_offset = entry.ptr(); m_hash_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_SYMTAB: case DT_SYMTAB:
m_symbol_table_offset = entry.ptr(); m_symbol_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_STRTAB: case DT_STRTAB:
m_string_table_offset = entry.ptr(); m_string_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_STRSZ: case DT_STRSZ:
m_size_of_string_table = entry.val(); m_size_of_string_table = entry.val();
@ -115,7 +123,7 @@ void DynamicObject::parse()
m_size_of_symbol_table_entry = entry.val(); m_size_of_symbol_table_entry = entry.val();
break; break;
case DT_PLTGOT: case DT_PLTGOT:
m_procedure_linkage_table_offset = entry.ptr(); m_procedure_linkage_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_PLTRELSZ: case DT_PLTRELSZ:
m_size_of_plt_relocation_entry_list = entry.val(); m_size_of_plt_relocation_entry_list = entry.val();
@ -125,11 +133,11 @@ void DynamicObject::parse()
ASSERT(m_procedure_linkage_table_relocation_type & (DT_REL | DT_RELA)); ASSERT(m_procedure_linkage_table_relocation_type & (DT_REL | DT_RELA));
break; break;
case DT_JMPREL: case DT_JMPREL:
m_plt_relocation_offset_location = entry.ptr(); m_plt_relocation_offset_location = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_RELA: case DT_RELA:
case DT_REL: case DT_REL:
m_relocation_table_offset = entry.ptr(); m_relocation_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break; break;
case DT_RELASZ: case DT_RELASZ:
case DT_RELSZ: case DT_RELSZ:
@ -171,12 +179,13 @@ void DynamicObject::parse()
if (!m_size_of_relocation_entry) { if (!m_size_of_relocation_entry) {
// TODO: FIXME, this shouldn't be hardcoded // TODO: FIXME, this shouldn't be hardcoded
// The reason we need this here is the for some reason, when there only PLT relocations, the compiler // The reason we need this here is that for some reason, when there only PLT relocations, the compiler
// doesn't insert a 'PLTRELSZ' entry to the dynamic section // doesn't insert a 'PLTRELSZ' entry to the dynamic section
m_size_of_relocation_entry = sizeof(Elf32_Rel); m_size_of_relocation_entry = sizeof(Elf32_Rel);
} }
auto hash_section_address = hash_section().address().as_ptr(); auto hash_section_address = hash_section().address().as_ptr();
// TODO: consider base address - it might not be zero
auto num_hash_chains = ((u32*)hash_section_address)[1]; auto num_hash_chains = ((u32*)hash_section_address)[1];
m_symbol_count = num_hash_chains; m_symbol_count = num_hash_chains;
} }
@ -287,9 +296,7 @@ const DynamicObject::Symbol DynamicObject::HashSection::lookup_symbol(const char
for (u32 i = buckets[hash_value % num_buckets]; i; i = chains[i]) { for (u32 i = buckets[hash_value % num_buckets]; i; i = chains[i]) {
auto symbol = m_dynamic.symbol(i); auto symbol = m_dynamic.symbol(i);
if (strcmp(name, symbol.name()) == 0) { if (strcmp(name, symbol.name()) == 0) {
#ifdef DYNAMIC_LOAD_DEBUG VERBOSE("Returning dynamic symbol with index %u for %s: %p\n", i, symbol.name(), symbol.address().as_ptr());
dbgprintf("Returning dynamic symbol with index %u for %s: %p\n", i, symbol.name(), symbol.address().as_ptr());
#endif
return symbol; return symbol;
} }
} }

View file

@ -105,7 +105,12 @@ public:
{ {
return m_is_undefined; return m_is_undefined;
} }
VirtualAddress address() const { return m_dynamic.base_address().offset(value()); } VirtualAddress address() const
{
if (m_dynamic.elf_is_dynamic())
return m_dynamic.base_address().offset(value());
return VirtualAddress { value() };
}
const DynamicObject& object() const { return m_dynamic; } const DynamicObject& object() const { return m_dynamic; }
private: private:
@ -135,7 +140,10 @@ public:
{ {
return !entry_size() ? 0 : size() / entry_size(); return !entry_size() ? 0 : size() / entry_size();
} }
VirtualAddress address() const { return m_dynamic.base_address().offset(m_section_offset); } VirtualAddress address() const
{
return m_dynamic.base_address().offset(m_section_offset);
}
protected: protected:
friend class RelocationSection; friend class RelocationSection;
@ -176,7 +184,12 @@ public:
unsigned type() const { return ELF32_R_TYPE(m_rel.r_info); } unsigned type() const { return ELF32_R_TYPE(m_rel.r_info); }
unsigned symbol_index() const { return ELF32_R_SYM(m_rel.r_info); } unsigned symbol_index() const { return ELF32_R_SYM(m_rel.r_info); }
const Symbol symbol() const { return m_dynamic.symbol(symbol_index()); } const Symbol symbol() const { return m_dynamic.symbol(symbol_index()); }
VirtualAddress address() const { return m_dynamic.base_address().offset(offset()); } VirtualAddress address() const
{
if (m_dynamic.elf_is_dynamic())
return m_dynamic.base_address().offset(offset());
return VirtualAddress { offset() };
}
private: private:
const DynamicObject& m_dynamic; const DynamicObject& m_dynamic;
@ -274,6 +287,8 @@ public:
using SymbolLookupFunction = DynamicObject::SymbolLookupResult (*)(const char*); using SymbolLookupFunction = DynamicObject::SymbolLookupResult (*)(const char*);
SymbolLookupFunction m_global_symbol_lookup_func { nullptr }; SymbolLookupFunction m_global_symbol_lookup_func { nullptr };
bool elf_is_dynamic() const { return m_is_elf_dynamic; }
private: private:
explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address); explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address);
@ -288,6 +303,7 @@ private:
VirtualAddress m_base_address; VirtualAddress m_base_address;
VirtualAddress m_dynamic_address; VirtualAddress m_dynamic_address;
VirtualAddress m_elf_base_address;
unsigned m_symbol_count { 0 }; unsigned m_symbol_count { 0 };
@ -318,6 +334,7 @@ private:
size_t m_size_of_relocation_entry { 0 }; size_t m_size_of_relocation_entry { 0 };
size_t m_size_of_relocation_table { 0 }; size_t m_size_of_relocation_table { 0 };
FlatPtr m_relocation_table_offset { 0 }; FlatPtr m_relocation_table_offset { 0 };
bool m_is_elf_dynamic { false };
// DT_FLAGS // DT_FLAGS
Elf32_Word m_dt_flags { 0 }; Elf32_Word m_dt_flags { 0 };
@ -356,7 +373,6 @@ inline void DynamicObject::for_each_dynamic_entry(F func) const
{ {
auto* dyns = reinterpret_cast<const Elf32_Dyn*>(m_dynamic_address.as_ptr()); auto* dyns = reinterpret_cast<const Elf32_Dyn*>(m_dynamic_address.as_ptr());
for (unsigned i = 0;; ++i) { for (unsigned i = 0;; ++i) {
// dbgprintf("%d\n", i);
auto&& dyn = DynamicEntry(dyns[i]); auto&& dyn = DynamicEntry(dyns[i]);
if (dyn.tag() == DT_NULL) if (dyn.tag() == DT_NULL)
break; break;

View file

@ -107,6 +107,7 @@ static void perform_self_relocations(auxv_t* auxvp)
dynamic_object->relocation_section().for_each_relocation([base_address](auto& reloc) { dynamic_object->relocation_section().for_each_relocation([base_address](auto& reloc) {
if (reloc.type() != R_386_RELATIVE) if (reloc.type() != R_386_RELATIVE)
return IterationDecision::Continue; return IterationDecision::Continue;
*(u32*)reloc.address().as_ptr() += base_address; *(u32*)reloc.address().as_ptr() += base_address;
return IterationDecision::Continue; return IterationDecision::Continue;
}); });
@ -171,10 +172,10 @@ static Vector<String> get_dependencies(const String& name)
static void map_dependencies(const String& name) static void map_dependencies(const String& name)
{ {
VERBOSE("mapping dependencies for: %s", name.characters()); VERBOSE("mapping dependencies for: %s\n", name.characters());
for (const auto& needed_name : get_dependencies(name)) { for (const auto& needed_name : get_dependencies(name)) {
VERBOSE("needed library: %s", needed_name.characters()); VERBOSE("needed library: %s\n", needed_name.characters());
String library_name = get_library_name(needed_name); String library_name = get_library_name(needed_name);
if (!g_loaders.contains(library_name)) { if (!g_loaders.contains(library_name)) {
@ -193,7 +194,7 @@ static void allocate_tls()
} }
if (total_tls_size) { if (total_tls_size) {
[[maybe_unused]] void* tls_address = allocate_tls(total_tls_size); [[maybe_unused]] void* tls_address = allocate_tls(total_tls_size);
VERBOSE("from userspace, tls_address: %p", tls_address); VERBOSE("from userspace, tls_address: %p\n", tls_address);
} }
g_total_tls_size = total_tls_size; g_total_tls_size = total_tls_size;
} }
@ -224,17 +225,17 @@ static void initialize_libc()
static void load_elf(const String& name) static void load_elf(const String& name)
{ {
VERBOSE("load_elf: %s", name.characters()); VERBOSE("load_elf: %s\n", name.characters());
auto loader = g_loaders.get(name).value(); auto loader = g_loaders.get(name).value();
VERBOSE("a1\n");
for (const auto& needed_name : get_dependencies(name)) { for (const auto& needed_name : get_dependencies(name)) {
VERBOSE("needed library: %s", needed_name.characters()); VERBOSE("needed library: %s\n", needed_name.characters());
String library_name = get_library_name(needed_name); String library_name = get_library_name(needed_name);
if (!g_loaded_objects.contains(library_name)) { if (!g_loaded_objects.contains(library_name)) {
load_elf(library_name); load_elf(library_name);
} }
} }
VERBOSE("loading: %s", name.characters());
auto dynamic_object = loader->load_from_image(RTLD_GLOBAL | RTLD_LAZY, g_total_tls_size); auto dynamic_object = loader->load_from_image(RTLD_GLOBAL | RTLD_LAZY, g_total_tls_size);
ASSERT(!dynamic_object.is_null()); ASSERT(!dynamic_object.is_null());
g_loaded_objects.set(name, dynamic_object.release_nonnull()); g_loaded_objects.set(name, dynamic_object.release_nonnull());
@ -270,7 +271,7 @@ static FlatPtr loader_main(auxv_t* auxvp)
VERBOSE("loaded all dependencies"); VERBOSE("loaded all dependencies");
for ([[maybe_unused]] auto& lib : g_loaders) { for ([[maybe_unused]] auto& lib : g_loaders) {
VERBOSE("%s - tls size: %u, tls offset: %u", lib.key.characters(), lib.value->tls_size(), lib.value->tls_offset()); VERBOSE("%s - tls size: %u, tls offset: %u\n", lib.key.characters(), lib.value->tls_size(), lib.value->tls_offset());
} }
allocate_tls(); allocate_tls();
@ -278,8 +279,12 @@ static FlatPtr loader_main(auxv_t* auxvp)
load_elf(main_program_name); load_elf(main_program_name);
auto main_program_lib = g_loaders.get(main_program_name).value(); auto main_program_lib = g_loaders.get(main_program_name).value();
FlatPtr entry_point = reinterpret_cast<FlatPtr>(main_program_lib->image().entry().as_ptr() + (FlatPtr)main_program_lib->text_segment_load_address().as_ptr());
VERBOSE("entry point: %p", entry_point); FlatPtr entry_point = reinterpret_cast<FlatPtr>(main_program_lib->image().entry().as_ptr());
if (main_program_lib->is_dynamic())
entry_point += reinterpret_cast<FlatPtr>(main_program_lib->text_segment_load_address().as_ptr());
VERBOSE("entry point: %p\n", entry_point);
// This will unmap the temporary memory maps we had for loading the libraries // This will unmap the temporary memory maps we had for loading the libraries
clear_temporary_objects_mappings(); clear_temporary_objects_mappings();
@ -310,9 +315,9 @@ void _start(int argc, char** argv, char** envp)
} }
MainFunction main_function = (MainFunction)(entry); MainFunction main_function = (MainFunction)(entry);
VERBOSE("jumping to main program entry point: %p", main_function); VERBOSE("jumping to main program entry point: %p\n", main_function);
int rc = main_function(argc, argv, envp); int rc = main_function(argc, argv, envp);
VERBOSE("rc: %d", rc); VERBOSE("rc: %d\n", rc);
if (g_libc_exit != nullptr) { if (g_libc_exit != nullptr) {
g_libc_exit(rc); g_libc_exit(rc);
} else { } else {