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) {