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
Notes: sideshowbarker 2024-07-19 00:37:40 +09:00
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)
{
m_valid = m_elf_image.is_valid() && m_elf_image.is_dynamic();
m_valid = m_elf_image.is_valid();
if (!m_valid) {
dbgprintf("DynamicLoader::load_from_image failed: image is invalid\n");
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;
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 m_dynamic_object;
}
@ -235,7 +238,9 @@ void DynamicLoader::load_program_headers()
// Process regions in order: .text, .data, .tls
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->mmap_prot(),
MAP_PRIVATE,
@ -245,13 +250,18 @@ void DynamicLoader::load_program_headers()
if (MAP_FAILED == text_segment_begin) {
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_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;
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->mmap_prot(),
MAP_ANONYMOUS | MAP_PRIVATE,
@ -261,10 +271,14 @@ void DynamicLoader::load_program_headers()
if (MAP_FAILED == data_segment_begin) {
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());
// FIXME: Initialize the values in the TLS section. Currently, it will always be zeroed.
// FIXME: Initialize the values in the TLS section. Currently, it is zeroed.
}
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();
main_relocation_section.for_each_relocation([&](ELF::DynamicObject::Relocation relocation) {
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 base address: %p\n", m_dynamic_object->base_address());
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());
[[maybe_unused]] auto rc = m_dynamic_object->patch_plt_entry(relocation.offset_in_section());
} 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);
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;
});

View file

@ -79,6 +79,7 @@ public:
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; }
bool is_dynamic() const { return m_elf_image.is_dynamic(); }
private:
class ProgramHeaderRegion {

View file

@ -50,6 +50,14 @@ DynamicObject::DynamicObject(VirtualAddress base_address, VirtualAddress dynamic
: m_base_address(base_address)
, 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();
}
@ -82,31 +90,31 @@ void DynamicObject::parse()
for_each_dynamic_entry([&](const DynamicEntry& entry) {
switch (entry.tag()) {
case DT_INIT:
m_init_offset = entry.ptr();
m_init_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_FINI:
m_fini_offset = entry.ptr();
m_fini_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_INIT_ARRAY:
m_init_array_offset = entry.ptr();
m_init_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_INIT_ARRAYSZ:
m_init_array_size = entry.val();
break;
case DT_FINI_ARRAY:
m_fini_array_offset = entry.ptr();
m_fini_array_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_FINI_ARRAYSZ:
m_fini_array_size = entry.val();
break;
case DT_HASH:
m_hash_table_offset = entry.ptr();
m_hash_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_SYMTAB:
m_symbol_table_offset = entry.ptr();
m_symbol_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_STRTAB:
m_string_table_offset = entry.ptr();
m_string_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_STRSZ:
m_size_of_string_table = entry.val();
@ -115,7 +123,7 @@ void DynamicObject::parse()
m_size_of_symbol_table_entry = entry.val();
break;
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;
case DT_PLTRELSZ:
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));
break;
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;
case DT_RELA:
case DT_REL:
m_relocation_table_offset = entry.ptr();
m_relocation_table_offset = entry.ptr() - (FlatPtr)m_elf_base_address.as_ptr();
break;
case DT_RELASZ:
case DT_RELSZ:
@ -171,12 +179,13 @@ void DynamicObject::parse()
if (!m_size_of_relocation_entry) {
// 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
m_size_of_relocation_entry = sizeof(Elf32_Rel);
}
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];
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]) {
auto symbol = m_dynamic.symbol(i);
if (strcmp(name, symbol.name()) == 0) {
#ifdef DYNAMIC_LOAD_DEBUG
dbgprintf("Returning dynamic symbol with index %u for %s: %p\n", i, symbol.name(), symbol.address().as_ptr());
#endif
VERBOSE("Returning dynamic symbol with index %u for %s: %p\n", i, symbol.name(), symbol.address().as_ptr());
return symbol;
}
}

View file

@ -105,7 +105,12 @@ public:
{
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; }
private:
@ -135,7 +140,10 @@ public:
{
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:
friend class RelocationSection;
@ -176,7 +184,12 @@ public:
unsigned type() const { return ELF32_R_TYPE(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()); }
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:
const DynamicObject& m_dynamic;
@ -274,6 +287,8 @@ public:
using SymbolLookupFunction = DynamicObject::SymbolLookupResult (*)(const char*);
SymbolLookupFunction m_global_symbol_lookup_func { nullptr };
bool elf_is_dynamic() const { return m_is_elf_dynamic; }
private:
explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address);
@ -288,6 +303,7 @@ private:
VirtualAddress m_base_address;
VirtualAddress m_dynamic_address;
VirtualAddress m_elf_base_address;
unsigned m_symbol_count { 0 };
@ -318,6 +334,7 @@ private:
size_t m_size_of_relocation_entry { 0 };
size_t m_size_of_relocation_table { 0 };
FlatPtr m_relocation_table_offset { 0 };
bool m_is_elf_dynamic { false };
// DT_FLAGS
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());
for (unsigned i = 0;; ++i) {
// dbgprintf("%d\n", i);
auto&& dyn = DynamicEntry(dyns[i]);
if (dyn.tag() == DT_NULL)
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) {
if (reloc.type() != R_386_RELATIVE)
return IterationDecision::Continue;
*(u32*)reloc.address().as_ptr() += base_address;
return IterationDecision::Continue;
});
@ -171,10 +172,10 @@ static Vector<String> get_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)) {
VERBOSE("needed library: %s", needed_name.characters());
VERBOSE("needed library: %s\n", needed_name.characters());
String library_name = get_library_name(needed_name);
if (!g_loaders.contains(library_name)) {
@ -193,7 +194,7 @@ static void allocate_tls()
}
if (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;
}
@ -224,17 +225,17 @@ static void initialize_libc()
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();
VERBOSE("a1\n");
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);
if (!g_loaded_objects.contains(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);
ASSERT(!dynamic_object.is_null());
g_loaded_objects.set(name, dynamic_object.release_nonnull());
@ -270,7 +271,7 @@ static FlatPtr loader_main(auxv_t* auxvp)
VERBOSE("loaded all dependencies");
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();
@ -278,8 +279,12 @@ static FlatPtr loader_main(auxv_t* auxvp)
load_elf(main_program_name);
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
clear_temporary_objects_mappings();
@ -310,9 +315,9 @@ void _start(int argc, char** argv, char** envp)
}
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);
VERBOSE("rc: %d", rc);
VERBOSE("rc: %d\n", rc);
if (g_libc_exit != nullptr) {
g_libc_exit(rc);
} else {