Browse Source

Loader.so+LibELF: Move most of Loader.so's logic into ELF::DynamicLinker

Loader.so now just performs the initial self relocations and static
LibC initialisation before handing over to ELF::DynamicLinker::linker_main
to handle the rest of the process.

As a trade-off, ELF::DynamicLinker needs to be explicitly excluded from
Lagom unless we really want to try writing a cross platform dynamic loader
William Marlow 4 years ago
parent
commit
3e815ad

+ 250 - 0
Libraries/LibELF/DynamicLinker.cpp

@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * Copyright (c) 2021, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/HashMap.h>
+#include <AK/HashTable.h>
+#include <AK/LexicalPath.h>
+#include <AK/LogStream.h>
+#include <AK/ScopeGuard.h>
+#include <LibC/mman.h>
+#include <LibC/stdio.h>
+#include <LibC/sys/internals.h>
+#include <LibC/unistd.h>
+#include <LibCore/File.h>
+#include <LibELF/AuxiliaryVector.h>
+#include <LibELF/DynamicLinker.h>
+#include <LibELF/DynamicLoader.h>
+#include <LibELF/DynamicObject.h>
+#include <LibELF/Image.h>
+#include <LibELF/exec_elf.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// #define DYNAMIC_LOAD_VERBOSE
+
+#ifdef DYNAMIC_LOAD_VERBOSE
+#    define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
+#else
+#    define VERBOSE(fmt, ...) \
+        do {                  \
+        } while (0)
+#endif
+#define TLS_VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
+
+namespace ELF {
+
+namespace {
+HashMap<String, NonnullRefPtr<ELF::DynamicLoader>> g_loaders;
+HashMap<String, NonnullRefPtr<ELF::DynamicObject>> g_loaded_objects;
+Vector<NonnullRefPtr<ELF::DynamicObject>> g_global_objects;
+
+using MainFunction = int (*)(int, char**, char**);
+using LibCExitFunction = void (*)(int);
+
+size_t g_current_tls_offset = 0;
+size_t g_total_tls_size = 0;
+char** g_envp = nullptr;
+LibCExitFunction g_libc_exit = nullptr;
+}
+
+DynamicObject::SymbolLookupResult DynamicLinker::lookup_global_symbol(const char* symbol_name)
+{
+    DynamicObject::SymbolLookupResult weak_result = {};
+    for (auto& lib : g_global_objects) {
+        auto res = lib->lookup_symbol(symbol_name);
+        if (res.found) {
+            if (res.bind == STB_GLOBAL) {
+                return res;
+            } else if (res.bind == STB_WEAK && !weak_result.found) {
+                weak_result = res;
+            }
+            // We don't want to allow local symbols to be pulled in to other modules
+        }
+    }
+    return weak_result;
+}
+
+static void map_library(const String& name, int fd)
+{
+    struct stat lib_stat;
+    int rc = fstat(fd, &lib_stat);
+    ASSERT(!rc);
+
+    auto loader = ELF::DynamicLoader::construct(name.characters(), fd, lib_stat.st_size);
+    loader->set_tls_offset(g_current_tls_offset);
+
+    g_loaders.set(name, loader);
+
+    g_current_tls_offset += loader->tls_size();
+}
+
+static void map_library(const String& name)
+{
+    // TODO: Do we want to also look for libs in other paths too?
+    String path = String::format("/usr/lib/%s", name.characters());
+    int fd = open(path.characters(), O_RDONLY);
+    ASSERT(fd >= 0);
+    map_library(name, fd);
+}
+
+static String get_library_name(const StringView& path)
+{
+    return LexicalPath(path).basename();
+}
+
+static Vector<String> get_dependencies(const String& name)
+{
+    auto lib = g_loaders.get(name).value();
+    Vector<String> dependencies;
+
+    lib->for_each_needed_library([&dependencies, &name](auto needed_name) {
+        if (name == needed_name)
+            return IterationDecision::Continue;
+        dependencies.append(needed_name);
+        return IterationDecision::Continue;
+    });
+    return dependencies;
+}
+
+static void map_dependencies(const String& name)
+{
+    VERBOSE("mapping dependencies for: %s\n", name.characters());
+
+    for (const auto& needed_name : get_dependencies(name)) {
+        VERBOSE("needed library: %s\n", needed_name.characters());
+        String library_name = get_library_name(needed_name);
+
+        if (!g_loaders.contains(library_name)) {
+            map_library(library_name);
+            map_dependencies(library_name);
+        }
+    }
+    VERBOSE("mapped dependencies for %s\n", name.characters());
+}
+
+static void allocate_tls()
+{
+    size_t total_tls_size = 0;
+    for (const auto& data : g_loaders) {
+        VERBOSE("%s: TLS Size: %zu\n", data.key.characters(), data.value->tls_size());
+        total_tls_size += data.value->tls_size();
+    }
+    if (total_tls_size) {
+        [[maybe_unused]] void* tls_address = ::allocate_tls(total_tls_size);
+        VERBOSE("from userspace, tls_address: %p\n", tls_address);
+    }
+    g_total_tls_size = total_tls_size;
+}
+
+static void initialize_libc(DynamicObject& libc)
+{
+    // Traditionally, `_start` of the main program initializes libc.
+    // However, since some libs use malloc() and getenv() in global constructors,
+    // we have to initialize libc just after it is loaded.
+    // Also, we can't just mark `__libc_init` with "__attribute__((constructor))"
+    // because it uses getenv() internally, so `environ` has to be initialized before we call `__libc_init`.
+    auto res = libc.lookup_symbol("environ");
+    ASSERT(res.found);
+    *((char***)res.address) = g_envp;
+
+    res = libc.lookup_symbol("__environ_is_malloced");
+    ASSERT(res.found);
+    *((bool*)res.address) = false;
+
+    res = libc.lookup_symbol("exit");
+    ASSERT(res.found);
+    g_libc_exit = (LibCExitFunction)res.address;
+
+    res = libc.lookup_symbol("__libc_init");
+    ASSERT(res.found);
+    typedef void libc_init_func();
+    ((libc_init_func*)res.address)();
+}
+
+static void load_elf(const String& name)
+{
+    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\n", needed_name.characters());
+        String library_name = get_library_name(needed_name);
+        if (!g_loaded_objects.contains(library_name)) {
+            load_elf(library_name);
+        }
+    }
+
+    auto dynamic_object = loader->load_from_image(RTLD_GLOBAL | RTLD_LAZY, g_total_tls_size);
+    ASSERT(dynamic_object);
+
+    g_loaded_objects.set(name, *dynamic_object);
+    g_global_objects.append(*dynamic_object);
+
+    VERBOSE("load_elf: done %s\n", name.characters());
+    if (name == "libc.so") {
+        initialize_libc(*dynamic_object);
+    }
+}
+
+void ELF::DynamicLinker::linker_main(String&& main_program_name, int main_program_fd, int argc, char** argv, char** envp)
+{
+    g_envp = envp;
+    map_library(main_program_name, main_program_fd);
+    map_dependencies(main_program_name);
+
+    VERBOSE("loaded all dependencies");
+    for ([[maybe_unused]] auto& lib : g_loaders) {
+        VERBOSE("%s - tls size: %zu, tls offset: %zu\n", lib.key.characters(), lib.value->tls_size(), lib.value->tls_offset());
+    }
+
+    allocate_tls();
+
+    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());
+    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", (void*)entry_point);
+    g_loaders.clear();
+
+    MainFunction main_function = (MainFunction)(entry_point);
+    VERBOSE("jumping to main program entry point: %p\n", main_function);
+    int rc = main_function(argc, argv, envp);
+    VERBOSE("rc: %d\n", rc);
+    if (g_libc_exit != nullptr) {
+        g_libc_exit(rc);
+    } else {
+        _exit(rc);
+    }
+
+    ASSERT_NOT_REACHED();
+}
+}

+ 45 - 0
Libraries/LibELF/DynamicLinker.h

@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 (c), the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Result.h>
+#include <AK/Vector.h>
+#include <LibELF/DynamicObject.h>
+
+namespace ELF {
+
+class DynamicLinker {
+public:
+    static DynamicObject::SymbolLookupResult lookup_global_symbol(const char* symbol);
+    [[noreturn]] static void linker_main(String&& main_program_name, int fd, int argc, char** argv, char** envp);
+
+private:
+    DynamicLinker() = delete;
+    ~DynamicLinker() = delete;
+};
+
+}

+ 0 - 2
Libraries/LibELF/DynamicLoader.cpp

@@ -162,8 +162,6 @@ RefPtr<DynamicObject> DynamicLoader::load_from_image(unsigned flags, size_t tota
     m_dynamic_object = DynamicObject::construct(m_text_segment_load_address, m_dynamic_section_address);
     m_dynamic_object = DynamicObject::construct(m_text_segment_load_address, m_dynamic_section_address);
     m_dynamic_object->set_tls_offset(m_tls_offset);
     m_dynamic_object->set_tls_offset(m_tls_offset);
     m_dynamic_object->set_tls_size(m_tls_size);
     m_dynamic_object->set_tls_size(m_tls_size);
-    ASSERT(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) {

+ 0 - 3
Libraries/LibELF/DynamicLoader.h

@@ -75,9 +75,6 @@ public:
     template<typename F>
     template<typename F>
     void for_each_needed_library(F) const;
     void for_each_needed_library(F) const;
 
 
-    DynamicObject::SymbolLookupFunction m_global_symbol_lookup_func { nullptr };
-    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(); }
     bool is_dynamic() const { return m_elf_image.is_dynamic(); }
 
 

+ 5 - 5
Libraries/LibELF/DynamicObject.cpp

@@ -27,6 +27,7 @@
 
 
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/StringBuilder.h>
 #include <AK/StringBuilder.h>
+#include <LibELF/DynamicLinker.h>
 #include <LibELF/DynamicObject.h>
 #include <LibELF/DynamicObject.h>
 #include <LibELF/exec_elf.h>
 #include <LibELF/exec_elf.h>
 #include <stdio.h>
 #include <stdio.h>
@@ -406,12 +407,12 @@ static const char* name_for_dtag(Elf32_Sword d_tag)
     }
     }
 }
 }
 
 
-Optional<DynamicObject::SymbolLookupResult> DynamicObject::lookup_symbol(const char* name) const
+DynamicObject::SymbolLookupResult DynamicObject::lookup_symbol(const char* name) const
 {
 {
     auto res = hash_section().lookup_symbol(name);
     auto res = hash_section().lookup_symbol(name);
     if (res.is_undefined())
     if (res.is_undefined())
         return {};
         return {};
-    return SymbolLookupResult { true, res.value(), (FlatPtr)res.address().as_ptr(), this };
+    return SymbolLookupResult { true, res.value(), (FlatPtr)res.address().as_ptr(), res.bind(), this };
 }
 }
 
 
 NonnullRefPtr<DynamicObject> DynamicObject::construct(VirtualAddress base_address, VirtualAddress dynamic_section_address)
 NonnullRefPtr<DynamicObject> DynamicObject::construct(VirtualAddress base_address, VirtualAddress dynamic_section_address)
@@ -450,10 +451,9 @@ DynamicObject::SymbolLookupResult DynamicObject::lookup_symbol(const ELF::Dynami
     VERBOSE("looking up symbol: %s\n", symbol.name());
     VERBOSE("looking up symbol: %s\n", symbol.name());
     if (!symbol.is_undefined()) {
     if (!symbol.is_undefined()) {
         VERBOSE("symbol is defined in its object\n");
         VERBOSE("symbol is defined in its object\n");
-        return { true, symbol.value(), (FlatPtr)symbol.address().as_ptr(), &symbol.object() };
+        return { true, symbol.value(), (FlatPtr)symbol.address().as_ptr(), symbol.bind(), &symbol.object() };
     }
     }
-    ASSERT(m_global_symbol_lookup_func);
-    return m_global_symbol_lookup_func(symbol.name());
+    return DynamicLinker::lookup_global_symbol(symbol.name());
 }
 }
 
 
 } // end namespace ELF
 } // end namespace ELF

+ 2 - 1
Libraries/LibELF/DynamicObject.h

@@ -276,9 +276,10 @@ public:
         bool found { false };
         bool found { false };
         FlatPtr value { 0 };
         FlatPtr value { 0 };
         FlatPtr address { 0 };
         FlatPtr address { 0 };
+        unsigned bind { STB_LOCAL };
         const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined
         const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined
     };
     };
-    Optional<SymbolLookupResult> lookup_symbol(const char* name) const;
+    SymbolLookupResult lookup_symbol(const char* name) const;
 
 
     // Will be called from _fixup_plt_entry, as part of the PLT trampoline
     // Will be called from _fixup_plt_entry, as part of the PLT trampoline
     Elf32_Addr patch_plt_entry(u32 relocation_offset);
     Elf32_Addr patch_plt_entry(u32 relocation_offset);

+ 4 - 0
Meta/Lagom/CMakeLists.txt

@@ -44,6 +44,10 @@ file(GLOB LIBREGEX_LIBC_SOURCES "../../Libraries/LibRegex/C/Regex.cpp")
 file(GLOB LIBREGEX_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibRegex/*.cpp")
 file(GLOB LIBREGEX_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibRegex/*.cpp")
 file(GLOB LIBCORE_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibCore/*.cpp")
 file(GLOB LIBCORE_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibCore/*.cpp")
 file(GLOB LIBELF_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibELF/*.cpp")
 file(GLOB LIBELF_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibELF/*.cpp")
+# There's no way we can reliably make this cross platform
+list(REMOVE_ITEM LIBELF_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../Libraries/LibELF/DynamicLinker.cpp")
+
+
 file(GLOB LIBGEMINI_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibGemini/*.cpp")
 file(GLOB LIBGEMINI_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibGemini/*.cpp")
 file(GLOB LIBGFX_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibGfx/*.cpp")
 file(GLOB LIBGFX_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibGfx/*.cpp")
 file(GLOB LIBHTTP_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibHTTP/*.cpp")
 file(GLOB LIBHTTP_SOURCES CONFIGURE_DEPENDS "../../Libraries/LibHTTP/*.cpp")

+ 21 - 209
Userland/DynamicLoader/main.cpp

@@ -35,6 +35,7 @@
 #include <LibC/unistd.h>
 #include <LibC/unistd.h>
 #include <LibCore/File.h>
 #include <LibCore/File.h>
 #include <LibELF/AuxiliaryVector.h>
 #include <LibELF/AuxiliaryVector.h>
+#include <LibELF/DynamicLinker.h>
 #include <LibELF/DynamicLoader.h>
 #include <LibELF/DynamicLoader.h>
 #include <LibELF/DynamicObject.h>
 #include <LibELF/DynamicObject.h>
 #include <LibELF/Image.h>
 #include <LibELF/Image.h>
@@ -57,23 +58,14 @@
 
 
 char* __static_environ[] = { nullptr }; // We don't get the environment without some libc workarounds..
 char* __static_environ[] = { nullptr }; // We don't get the environment without some libc workarounds..
 
 
-static HashMap<String, NonnullRefPtr<ELF::DynamicLoader>> g_loaders;
-static HashMap<String, NonnullRefPtr<ELF::DynamicObject>> g_loaded_objects;
-
-using MainFunction = int (*)(int, char**, char**);
-using LibCExitFunction = void (*)(int);
-
-static size_t g_current_tls_offset = 0;
-static size_t g_total_tls_size = 0;
-static char** g_envp = nullptr;
-static LibCExitFunction g_libc_exit = nullptr;
-
 static void init_libc()
 static void init_libc()
 {
 {
     environ = __static_environ;
     environ = __static_environ;
     __environ_is_malloced = false;
     __environ_is_malloced = false;
     __stdio_is_initialized = false;
     __stdio_is_initialized = false;
-    __malloc_init();
+    // Initialise the copy of libc included statically in Loader.so,
+    // initialisation of the dynamic libc.so is done by the DynamicLinker
+    __libc_init();
 }
 }
 
 
 static void perform_self_relocations(auxv_t* auxvp)
 static void perform_self_relocations(auxv_t* auxvp)
@@ -112,144 +104,6 @@ static void perform_self_relocations(auxv_t* auxvp)
     });
     });
 }
 }
 
 
-static ELF::DynamicObject::SymbolLookupResult global_symbol_lookup(const char* symbol_name)
-{
-    VERBOSE("global symbol lookup: %s\n", symbol_name);
-    for (auto& lib : g_loaded_objects) {
-        VERBOSE("looking up in object: %s\n", lib.key.characters());
-        auto res = lib.value->lookup_symbol(symbol_name);
-        if (!res.has_value())
-            continue;
-        return res.value();
-    }
-    // ASSERT_NOT_REACHED();
-    return {};
-}
-
-static void map_library(const String& name, int fd)
-{
-    struct stat lib_stat;
-    int rc = fstat(fd, &lib_stat);
-    ASSERT(!rc);
-
-    auto loader = ELF::DynamicLoader::construct(name.characters(), fd, lib_stat.st_size);
-    loader->set_tls_offset(g_current_tls_offset);
-    loader->set_global_symbol_lookup_function(global_symbol_lookup);
-
-    g_loaders.set(name, loader);
-
-    g_current_tls_offset += loader->tls_size();
-}
-
-static void map_library(const String& name)
-{
-    // TODO: Do we want to also look for libs in other paths too?
-    String path = String::format("/usr/lib/%s", name.characters());
-    int fd = open(path.characters(), O_RDONLY);
-    ASSERT(fd >= 0);
-    map_library(name, fd);
-}
-
-static String get_library_name(const StringView& path)
-{
-    return LexicalPath(path).basename();
-}
-
-static Vector<String> get_dependencies(const String& name)
-{
-    auto lib = g_loaders.get(name).value();
-    Vector<String> dependencies;
-
-    lib->for_each_needed_library([&dependencies, &name](auto needed_name) {
-        if (name == needed_name)
-            return IterationDecision::Continue;
-        dependencies.append(needed_name);
-        return IterationDecision::Continue;
-    });
-    return dependencies;
-}
-
-static void map_dependencies(const String& name)
-{
-    VERBOSE("mapping dependencies for: %s\n", name.characters());
-
-    for (const auto& needed_name : get_dependencies(name)) {
-        VERBOSE("needed library: %s\n", needed_name.characters());
-        String library_name = get_library_name(needed_name);
-
-        if (!g_loaders.contains(library_name)) {
-            map_library(library_name);
-            map_dependencies(library_name);
-        }
-    }
-}
-
-static void allocate_tls()
-{
-    size_t total_tls_size = 0;
-    for (const auto& data : g_loaders) {
-        VERBOSE("%s: TLS Size: %zu\n", data.key.characters(), data.value->tls_size());
-        total_tls_size += data.value->tls_size();
-    }
-    if (total_tls_size) {
-        [[maybe_unused]] void* tls_address = allocate_tls(total_tls_size);
-        VERBOSE("from userspace, tls_address: %p\n", tls_address);
-    }
-    g_total_tls_size = total_tls_size;
-}
-
-static void initialize_libc()
-{
-    // Traditionally, `_start` of the main program initializes libc.
-    // However, since some libs use malloc() and getenv() in global constructors,
-    // we have to initialize libc just after it is loaded.
-    // Also, we can't just mark `__libc_init` with "__attribute__((constructor))"
-    // because it uses getenv() internally, so `environ` has to be initialized before we call `__libc_init`.
-    auto res = global_symbol_lookup("environ");
-    *((char***)res.address) = g_envp;
-    ASSERT(res.found);
-    res = global_symbol_lookup("__environ_is_malloced");
-    ASSERT(res.found);
-    *((bool*)res.address) = false;
-
-    res = global_symbol_lookup("exit");
-    ASSERT(res.found);
-    g_libc_exit = (LibCExitFunction)res.address;
-
-    res = global_symbol_lookup("__libc_init");
-    ASSERT(res.found);
-    typedef void libc_init_func();
-    ((libc_init_func*)res.address)();
-}
-
-static void load_elf(const String& name)
-{
-    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\n", needed_name.characters());
-        String library_name = get_library_name(needed_name);
-        if (!g_loaded_objects.contains(library_name)) {
-            load_elf(library_name);
-        }
-    }
-
-    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());
-
-    if (name == "libc.so") {
-        initialize_libc();
-    }
-}
-
-static void clear_temporary_objects_mappings()
-{
-
-    g_loaders.clear();
-}
-
 static void display_help()
 static void display_help()
 {
 {
     const char message[] =
     const char message[] =
@@ -261,11 +115,24 @@ This helper program loads the shared libraries needed by the program,
 prepares the program to run, and runs it. You do not need to invoke
 prepares the program to run, and runs it. You do not need to invoke
 this helper program directly.
 this helper program directly.
 )";
 )";
-    write(1, message, sizeof(message));
+    fprintf(stderr, "%s", message);
 }
 }
 
 
-static FlatPtr loader_main(auxv_t* auxvp)
+extern "C" {
+
+// The compiler expects a previous declaration
+void _start(int, char**, char**);
+
+void _start(int argc, char** argv, char** envp)
 {
 {
+    char** env;
+    for (env = envp; *env; ++env) {
+    }
+
+    auxv_t* auxvp = (auxv_t*)++env;
+    perform_self_relocations(auxvp);
+    init_libc();
+
     int main_program_fd = -1;
     int main_program_fd = -1;
     String main_program_name;
     String main_program_name;
     for (; auxvp->a_type != AT_NULL; ++auxvp) {
     for (; auxvp->a_type != AT_NULL; ++auxvp) {
@@ -288,62 +155,7 @@ static FlatPtr loader_main(auxv_t* auxvp)
         _exit(1);
         _exit(1);
     }
     }
 
 
-    map_library(main_program_name, main_program_fd);
-    map_dependencies(main_program_name);
-
-    VERBOSE("loaded all dependencies");
-    for ([[maybe_unused]] auto& lib : g_loaders) {
-        VERBOSE("%s - tls size: %zu, tls offset: %zu\n", lib.key.characters(), lib.value->tls_size(), lib.value->tls_offset());
-    }
-
-    allocate_tls();
-
-    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());
-    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", (void*)entry_point);
-
-    // This will unmap the temporary memory maps we had for loading the libraries
-    clear_temporary_objects_mappings();
-
-    return entry_point;
-}
-
-extern "C" {
-
-// The compiler expects a previous declaration
-void _start(int, char**, char**);
-
-void _start(int argc, char** argv, char** envp)
-{
-    g_envp = envp;
-    char** env;
-    for (env = envp; *env; ++env) {
-    }
-
-    auxv_t* auxvp = (auxv_t*)++env;
-    perform_self_relocations(auxvp);
-    init_libc();
-
-    FlatPtr entry = loader_main(auxvp);
-    VERBOSE("Loaded libs:\n");
-    for ([[maybe_unused]] auto& obj : g_loaded_objects) {
-        VERBOSE("%s: %p\n", obj.key.characters(), obj.value->base_address().as_ptr());
-    }
-
-    MainFunction main_function = (MainFunction)(entry);
-    VERBOSE("jumping to main program entry point: %p\n", main_function);
-    int rc = main_function(argc, argv, envp);
-    VERBOSE("rc: %d\n", rc);
-    if (g_libc_exit != nullptr) {
-        g_libc_exit(rc);
-    } else {
-        _exit(rc);
-    }
+    ELF::DynamicLinker::linker_main(move(main_program_name), main_program_fd, argc, argv, envp);
+    ASSERT_NOT_REACHED();
 }
 }
 }
 }