From 982799f7a04459170335acd2837e7bb56d89087c Mon Sep 17 00:00:00 2001 From: Dan Klishch Date: Sat, 20 Jan 2024 16:20:42 -0500 Subject: [PATCH] LibC+LibELF: Pass information from linker via magic lookup This works by defining a set of weak symbols in dynamic linker whose value would be provided by it. This has the same effect as preloading library that magically knows right addresses of functions shared between dynamic linker and LibC. We were previously passing the same information by rewriting values based on hardcoded library name, so the new approach seems a little nicer to me. --- Userland/Libraries/LibC/dlfcn.cpp | 8 +- Userland/Libraries/LibC/libcinit.cpp | 4 +- Userland/Libraries/LibC/link.cpp | 2 +- Userland/Libraries/LibC/ssp.cpp | 4 +- Userland/Libraries/LibC/stdlib.cpp | 2 +- Userland/Libraries/LibELF/DynamicLinker.cpp | 110 +++++++++----------- 6 files changed, 59 insertions(+), 71 deletions(-) diff --git a/Userland/Libraries/LibC/dlfcn.cpp b/Userland/Libraries/LibC/dlfcn.cpp index e5eecdfbf8..ed4a400007 100644 --- a/Userland/Libraries/LibC/dlfcn.cpp +++ b/Userland/Libraries/LibC/dlfcn.cpp @@ -11,10 +11,10 @@ #include // These are filled in by the dynamic loader. -DlCloseFunction __dlclose; -DlOpenFunction __dlopen; -DlSymFunction __dlsym; -DlAddrFunction __dladdr; +[[gnu::weak]] DlCloseFunction __dlclose; +[[gnu::weak]] DlOpenFunction __dlopen; +[[gnu::weak]] DlSymFunction __dlsym; +[[gnu::weak]] DlAddrFunction __dladdr; // FIXME: use thread_local and a String once TLS works #ifdef NO_TLS diff --git a/Userland/Libraries/LibC/libcinit.cpp b/Userland/Libraries/LibC/libcinit.cpp index e1024acfd9..dabe2d3d2c 100644 --- a/Userland/Libraries/LibC/libcinit.cpp +++ b/Userland/Libraries/LibC/libcinit.cpp @@ -17,8 +17,8 @@ int errno_storage; #else __thread int errno_storage; #endif -char** environ; -bool __environ_is_malloced; +[[gnu::weak]] char** environ; +bool __environ_is_malloced = false; bool __stdio_is_initialized; void* __auxiliary_vector; diff --git a/Userland/Libraries/LibC/link.cpp b/Userland/Libraries/LibC/link.cpp index 84397eb687..f686ae18fc 100644 --- a/Userland/Libraries/LibC/link.cpp +++ b/Userland/Libraries/LibC/link.cpp @@ -12,7 +12,7 @@ extern "C" { using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*); using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*); -DlIteratePhdrFunction __dl_iterate_phdr; +[[gnu::weak]] DlIteratePhdrFunction __dl_iterate_phdr; int dl_iterate_phdr(int (*callback)(struct dl_phdr_info* info, size_t size, void* data), void* data) { diff --git a/Userland/Libraries/LibC/ssp.cpp b/Userland/Libraries/LibC/ssp.cpp index 126a946b86..734ca7c561 100644 --- a/Userland/Libraries/LibC/ssp.cpp +++ b/Userland/Libraries/LibC/ssp.cpp @@ -18,8 +18,8 @@ extern "C" { extern uintptr_t __stack_chk_guard; -// Initialized in `initialize_libc` (we leave a placeholder value here before initialization). -__attribute__((used)) uintptr_t __stack_chk_guard = (uintptr_t)0xc6c7c8c9; +// Populated by DynamicLinker in shared executables. +[[gnu::weak]] uintptr_t __stack_chk_guard = (uintptr_t)0xc6c7c8c9; __attribute__((noreturn)) void __stack_chk_fail() { diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp index 29325d555b..76ea655e4e 100644 --- a/Userland/Libraries/LibC/stdlib.cpp +++ b/Userland/Libraries/LibC/stdlib.cpp @@ -345,7 +345,7 @@ static T c_str_to_floating_point(char const* str, char** endptr) extern "C" { -void (*__call_fini_functions)(); +[[gnu::weak]] void (*__call_fini_functions)(); void exit(int status) { diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index e00f1fe3cb..191f5565c1 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,6 @@ static size_t s_current_tls_offset = 0; static size_t s_total_tls_size = 0; static size_t s_allocated_tls_block_size = 0; static char** s_envp = nullptr; -static LibCExitFunction s_libc_exit = nullptr; static __pthread_mutex_t s_loader_lock = __PTHREAD_MUTEX_INITIALIZER; static ByteString s_cwd; @@ -65,11 +65,32 @@ static StringView s_ld_library_path; static StringView s_main_program_pledge_promises; static ByteString s_loader_pledge_promises; -static Result __dlclose(void* handle); -static Result __dlopen(char const* filename, int flags); -static Result __dlsym(void* handle, char const* symbol_name); -static Result __dladdr(void const* addr, Dl_info* info); -static void __call_fini_functions(); +class MagicWeakSymbol : public RefCounted { + AK_MAKE_NONCOPYABLE(MagicWeakSymbol); + AK_MAKE_NONMOVABLE(MagicWeakSymbol); + +public: + template + MagicWeakSymbol(unsigned int type, T value) + { + m_storage = reinterpret_cast(value); + m_lookup_result.size = 8; + m_lookup_result.type = type; + m_lookup_result.address = VirtualAddress { &m_storage }; + m_lookup_result.bind = STB_GLOBAL; + } + + auto lookup_result() const + { + return m_lookup_result; + } + +private: + DynamicObject::SymbolLookupResult m_lookup_result; + uintptr_t m_storage; +}; + +static HashMap> s_magic_weak_symbols; Optional DynamicLinker::lookup_global_symbol(StringView name) { @@ -87,6 +108,10 @@ Optional DynamicLinker::lookup_global_symbol( weak_result = res; // We don't want to allow local symbols to be pulled in to other modules } + + if (auto magic_lookup = s_magic_weak_symbols.get(name); magic_lookup.has_value()) + weak_result = (*magic_lookup)->lookup_result(); + return weak_result; } @@ -260,61 +285,7 @@ static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data) 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"sv); - VERIFY(res.has_value()); - *((char***)res.value().address.as_ptr()) = s_envp; - - // __stack_chk_guard should be initialized before anything significant (read: global constructors) is running. - // This is not done in __libc_init, as we definitely have to return from that, and it might affect Loader as well. - res = libc.lookup_symbol("__stack_chk_guard"sv); - VERIFY(res.has_value()); - void* stack_guard = res.value().address.as_ptr(); - arc4random_buf(stack_guard, sizeof(uintptr_t)); - -#ifdef AK_ARCH_64_BIT - // For 64-bit platforms we include an additional hardening: zero the first byte of the stack guard to avoid - // leaking or overwriting the stack guard with C-style string functions. - ((char*)stack_guard)[0] = 0; -#endif - - res = libc.lookup_symbol("__environ_is_malloced"sv); - VERIFY(res.has_value()); - *((bool*)res.value().address.as_ptr()) = false; - - res = libc.lookup_symbol("exit"sv); - VERIFY(res.has_value()); - s_libc_exit = (LibCExitFunction)res.value().address.as_ptr(); - - res = libc.lookup_symbol("__dl_iterate_phdr"sv); - VERIFY(res.has_value()); - *((DlIteratePhdrFunction*)res.value().address.as_ptr()) = __dl_iterate_phdr; - - res = libc.lookup_symbol("__dlclose"sv); - VERIFY(res.has_value()); - *((DlCloseFunction*)res.value().address.as_ptr()) = __dlclose; - - res = libc.lookup_symbol("__dlopen"sv); - VERIFY(res.has_value()); - *((DlOpenFunction*)res.value().address.as_ptr()) = __dlopen; - - res = libc.lookup_symbol("__dlsym"sv); - VERIFY(res.has_value()); - *((DlSymFunction*)res.value().address.as_ptr()) = __dlsym; - - res = libc.lookup_symbol("__dladdr"sv); - VERIFY(res.has_value()); - *((DlAddrFunction*)res.value().address.as_ptr()) = __dladdr; - - res = libc.lookup_symbol("__call_fini_functions"sv); - VERIFY(res.has_value()); - *((CallFiniFunctionsFunction*)res.value().address.as_ptr()) = __call_fini_functions; - - res = libc.lookup_symbol("__libc_init"sv); + auto res = libc.lookup_symbol("__libc_init"sv); VERIFY(res.has_value()); typedef void libc_init_func(); ((libc_init_func*)res.value().address.as_ptr())(); @@ -662,6 +633,23 @@ void ELF::DynamicLinker::linker_main(ByteString&& main_program_path, int main_pr s_envp = envp; + uintptr_t stack_guard = get_random(); + +#ifdef AK_ARCH_64_BIT + // For 64-bit platforms we include an additional hardening: zero the first byte of the stack guard to avoid + // leaking or overwriting the stack guard with C-style string functions. + stack_guard &= ~0xffULL; +#endif + + s_magic_weak_symbols.set("environ"sv, make_ref_counted(STT_OBJECT, s_envp)); + s_magic_weak_symbols.set("__stack_chk_guard"sv, make_ref_counted(STT_OBJECT, stack_guard)); + s_magic_weak_symbols.set("__call_fini_functions"sv, make_ref_counted(STT_FUNC, __call_fini_functions)); + s_magic_weak_symbols.set("__dl_iterate_phdr"sv, make_ref_counted(STT_FUNC, __dl_iterate_phdr)); + s_magic_weak_symbols.set("__dlclose"sv, make_ref_counted(STT_FUNC, __dlclose)); + s_magic_weak_symbols.set("__dlopen"sv, make_ref_counted(STT_FUNC, __dlopen)); + s_magic_weak_symbols.set("__dlsym"sv, make_ref_counted(STT_FUNC, __dlsym)); + s_magic_weak_symbols.set("__dladdr"sv, make_ref_counted(STT_FUNC, __dladdr)); + char* raw_current_directory = getcwd(nullptr, 0); s_cwd = raw_current_directory; free(raw_current_directory);