diff options
author | William Marlow <william.marlow@lux01.co.uk> | 2021-01-01 23:54:43 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-03 17:15:55 +0100 |
commit | 3e815ad5b13aa823fa8298a02fe569cfd2cf1d2b (patch) | |
tree | 58f356ae51857889ea415c4711809ad2709a62e0 /Libraries | |
parent | 4d32121293d840364e484fc8514bc34b123c4f6e (diff) | |
download | serenity-3e815ad5b13aa823fa8298a02fe569cfd2cf1d2b.zip |
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
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibELF/DynamicLinker.cpp | 250 | ||||
-rw-r--r-- | Libraries/LibELF/DynamicLinker.h | 45 | ||||
-rw-r--r-- | Libraries/LibELF/DynamicLoader.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibELF/DynamicLoader.h | 3 | ||||
-rw-r--r-- | Libraries/LibELF/DynamicObject.cpp | 10 | ||||
-rw-r--r-- | Libraries/LibELF/DynamicObject.h | 3 |
6 files changed, 302 insertions, 11 deletions
diff --git a/Libraries/LibELF/DynamicLinker.cpp b/Libraries/LibELF/DynamicLinker.cpp new file mode 100644 index 0000000000..2f47a42cdf --- /dev/null +++ b/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(); +} +}
\ No newline at end of file diff --git a/Libraries/LibELF/DynamicLinker.h b/Libraries/LibELF/DynamicLinker.h new file mode 100644 index 0000000000..b6aa18312d --- /dev/null +++ b/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; +}; + +}
\ No newline at end of file diff --git a/Libraries/LibELF/DynamicLoader.cpp b/Libraries/LibELF/DynamicLoader.cpp index 8938f9f263..d222bc2198 100644 --- a/Libraries/LibELF/DynamicLoader.cpp +++ b/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->set_tls_offset(m_tls_offset); 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); if (!rc) { diff --git a/Libraries/LibELF/DynamicLoader.h b/Libraries/LibELF/DynamicLoader.h index 3ced5ae4e5..7381c5d3fd 100644 --- a/Libraries/LibELF/DynamicLoader.h +++ b/Libraries/LibELF/DynamicLoader.h @@ -75,9 +75,6 @@ public: template<typename F> 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; } bool is_dynamic() const { return m_elf_image.is_dynamic(); } diff --git a/Libraries/LibELF/DynamicObject.cpp b/Libraries/LibELF/DynamicObject.cpp index 4f446203d7..10dcb2eb1d 100644 --- a/Libraries/LibELF/DynamicObject.cpp +++ b/Libraries/LibELF/DynamicObject.cpp @@ -27,6 +27,7 @@ #include <AK/String.h> #include <AK/StringBuilder.h> +#include <LibELF/DynamicLinker.h> #include <LibELF/DynamicObject.h> #include <LibELF/exec_elf.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); if (res.is_undefined()) 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) @@ -450,10 +451,9 @@ DynamicObject::SymbolLookupResult DynamicObject::lookup_symbol(const ELF::Dynami VERBOSE("looking up symbol: %s\n", symbol.name()); if (!symbol.is_undefined()) { 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 diff --git a/Libraries/LibELF/DynamicObject.h b/Libraries/LibELF/DynamicObject.h index b9a8d3e9b9..f9906cc135 100644 --- a/Libraries/LibELF/DynamicObject.h +++ b/Libraries/LibELF/DynamicObject.h @@ -276,9 +276,10 @@ public: bool found { false }; FlatPtr value { 0 }; FlatPtr address { 0 }; + unsigned bind { STB_LOCAL }; 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 Elf32_Addr patch_plt_entry(u32 relocation_offset); |