From 89a38b72b7181b103aa524e8abd3fe98c8c75864 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sun, 6 Jun 2021 17:40:11 +0200 Subject: [PATCH] LibC+LibELF: Implement dladdr() This implements the dladdr() function which lets the caller look up the symbol name, symbol address as well as library name and library base address for an arbitrary address. --- Userland/Libraries/LibC/dlfcn.cpp | 1 + Userland/Libraries/LibDl/dlfcn.cpp | 12 +++++ Userland/Libraries/LibDl/dlfcn.h | 8 ++++ Userland/Libraries/LibDl/dlfcn_integration.h | 5 +++ Userland/Libraries/LibELF/DynamicLinker.cpp | 47 ++++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/Userland/Libraries/LibC/dlfcn.cpp b/Userland/Libraries/LibC/dlfcn.cpp index 73e54df798..41c13a5f92 100644 --- a/Userland/Libraries/LibC/dlfcn.cpp +++ b/Userland/Libraries/LibC/dlfcn.cpp @@ -10,3 +10,4 @@ DlCloseFunction __dlclose; DlOpenFunction __dlopen; DlSymFunction __dlsym; +DlAddrFunction __dladdr; diff --git a/Userland/Libraries/LibDl/dlfcn.cpp b/Userland/Libraries/LibDl/dlfcn.cpp index ef730e845d..077f62a101 100644 --- a/Userland/Libraries/LibDl/dlfcn.cpp +++ b/Userland/Libraries/LibDl/dlfcn.cpp @@ -60,3 +60,15 @@ void* dlsym(void* handle, const char* symbol_name) } return result.value(); } + +int dladdr(void* addr, Dl_info* info) +{ + auto result = __dladdr(addr, info); + if (result.is_error()) { + // FIXME: According to the man page glibc does _not_ make the error + // available via dlerror(), however we do. Does this break anything? + store_error(result.error().text); + return 0; + } + return 1; +} diff --git a/Userland/Libraries/LibDl/dlfcn.h b/Userland/Libraries/LibDl/dlfcn.h index fd0b427f82..880dce74ac 100644 --- a/Userland/Libraries/LibDl/dlfcn.h +++ b/Userland/Libraries/LibDl/dlfcn.h @@ -16,9 +16,17 @@ __BEGIN_DECLS #define RTLD_GLOBAL 8 #define RTLD_LOCAL 16 +typedef struct __Dl_info { + const char* dli_fname; + void* dli_fbase; + const char* dli_sname; + void* dli_saddr; +} Dl_info; + int dlclose(void*); char* dlerror(); void* dlopen(const char*, int); void* dlsym(void*, const char*); +int dladdr(void*, Dl_info*); __END_DECLS diff --git a/Userland/Libraries/LibDl/dlfcn_integration.h b/Userland/Libraries/LibDl/dlfcn_integration.h index c336e7bee9..19a8251ae2 100644 --- a/Userland/Libraries/LibDl/dlfcn_integration.h +++ b/Userland/Libraries/LibDl/dlfcn_integration.h @@ -23,12 +23,17 @@ struct DlErrorMessage { String text; }; +struct __Dl_info; +typedef struct __Dl_info Dl_info; + typedef Result (*DlCloseFunction)(void*); typedef Result (*DlOpenFunction)(const char*, int); typedef Result (*DlSymFunction)(void*, const char*); +typedef Result (*DlAddrFunction)(void*, Dl_info*); extern "C" { extern DlCloseFunction __dlclose; extern DlOpenFunction __dlopen; extern DlSymFunction __dlsym; +extern DlAddrFunction __dladdr; } diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 43d2d0cb6b..bafb7029c5 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -54,6 +54,7 @@ static bool s_do_breakpoint_trap_before_entry { false }; static Result __dlclose(void* handle); static Result __dlopen(const char* filename, int flags); static Result __dlsym(void* handle, const char* symbol_name); +static Result __dladdr(void* addr, Dl_info* info); Optional DynamicLinker::lookup_global_symbol(const StringView& name) { @@ -237,6 +238,10 @@ static void initialize_libc(DynamicObject& libc) 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("__libc_init"sv); VERIFY(res.has_value()); typedef void libc_init_func(); @@ -430,6 +435,48 @@ static Result __dlsym(void* handle, const char* symbol_na return symbol.value().address.as_ptr(); } +static Result __dladdr(void* addr, Dl_info* info) +{ + VirtualAddress user_addr { addr }; + __pthread_mutex_lock(&s_loader_lock); + ScopeGuard unlock_guard = [] { __pthread_mutex_unlock(&s_loader_lock); }; + + RefPtr best_matching_library; + VirtualAddress best_library_offset; + for (auto& lib : s_global_objects) { + if (user_addr < lib.value->base_address()) + continue; + auto offset = user_addr - lib.value->base_address(); + if (!best_matching_library || offset < best_library_offset) { + best_matching_library = lib.value; + best_library_offset = offset; + } + } + + if (!best_matching_library) { + return DlErrorMessage { "No library found which contains the specified address" }; + } + + Optional best_matching_symbol; + best_matching_library->for_each_symbol([&](auto const& symbol) { + if (user_addr < symbol.address() || user_addr > symbol.address().offset(symbol.size())) + return; + best_matching_symbol = symbol; + }); + + info->dli_fbase = best_matching_library->base_address().as_ptr(); + // This works because we don't support unloading objects. + info->dli_fname = best_matching_library->filename().characters(); + if (best_matching_symbol.has_value()) { + info->dli_saddr = best_matching_symbol.value().address().as_ptr(); + info->dli_sname = best_matching_symbol.value().raw_name(); + } else { + info->dli_saddr = nullptr; + info->dli_sname = nullptr; + } + return {}; +} + static void read_environment_variables() { for (char** env = s_envp; *env; ++env) {