Loader: Support loading non-position independent executables
This commit is contained in:
parent
0cb636078a
commit
a83a9f3a55
Notes:
sideshowbarker
2024-07-19 00:37:40 +09:00
Author: https://github.com/itamar8910 Commit: https://github.com/SerenityOS/serenity/commit/a83a9f3a55a
5 changed files with 88 additions and 41 deletions
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue