Sfoglia il codice sorgente

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.
Gunnar Beutner 4 anni fa
parent
commit
89a38b72b7

+ 1 - 0
Userland/Libraries/LibC/dlfcn.cpp

@@ -10,3 +10,4 @@
 DlCloseFunction __dlclose;
 DlCloseFunction __dlclose;
 DlOpenFunction __dlopen;
 DlOpenFunction __dlopen;
 DlSymFunction __dlsym;
 DlSymFunction __dlsym;
+DlAddrFunction __dladdr;

+ 12 - 0
Userland/Libraries/LibDl/dlfcn.cpp

@@ -60,3 +60,15 @@ void* dlsym(void* handle, const char* symbol_name)
     }
     }
     return result.value();
     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;
+}

+ 8 - 0
Userland/Libraries/LibDl/dlfcn.h

@@ -16,9 +16,17 @@ __BEGIN_DECLS
 #define RTLD_GLOBAL 8
 #define RTLD_GLOBAL 8
 #define RTLD_LOCAL 16
 #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*);
 int dlclose(void*);
 char* dlerror();
 char* dlerror();
 void* dlopen(const char*, int);
 void* dlopen(const char*, int);
 void* dlsym(void*, const char*);
 void* dlsym(void*, const char*);
+int dladdr(void*, Dl_info*);
 
 
 __END_DECLS
 __END_DECLS

+ 5 - 0
Userland/Libraries/LibDl/dlfcn_integration.h

@@ -23,12 +23,17 @@ struct DlErrorMessage {
     String text;
     String text;
 };
 };
 
 
+struct __Dl_info;
+typedef struct __Dl_info Dl_info;
+
 typedef Result<void, DlErrorMessage> (*DlCloseFunction)(void*);
 typedef Result<void, DlErrorMessage> (*DlCloseFunction)(void*);
 typedef Result<void*, DlErrorMessage> (*DlOpenFunction)(const char*, int);
 typedef Result<void*, DlErrorMessage> (*DlOpenFunction)(const char*, int);
 typedef Result<void*, DlErrorMessage> (*DlSymFunction)(void*, const char*);
 typedef Result<void*, DlErrorMessage> (*DlSymFunction)(void*, const char*);
+typedef Result<void, DlErrorMessage> (*DlAddrFunction)(void*, Dl_info*);
 
 
 extern "C" {
 extern "C" {
 extern DlCloseFunction __dlclose;
 extern DlCloseFunction __dlclose;
 extern DlOpenFunction __dlopen;
 extern DlOpenFunction __dlopen;
 extern DlSymFunction __dlsym;
 extern DlSymFunction __dlsym;
+extern DlAddrFunction __dladdr;
 }
 }

+ 47 - 0
Userland/Libraries/LibELF/DynamicLinker.cpp

@@ -54,6 +54,7 @@ static bool s_do_breakpoint_trap_before_entry { false };
 static Result<void, DlErrorMessage> __dlclose(void* handle);
 static Result<void, DlErrorMessage> __dlclose(void* handle);
 static Result<void*, DlErrorMessage> __dlopen(const char* filename, int flags);
 static Result<void*, DlErrorMessage> __dlopen(const char* filename, int flags);
 static Result<void*, DlErrorMessage> __dlsym(void* handle, const char* symbol_name);
 static Result<void*, DlErrorMessage> __dlsym(void* handle, const char* symbol_name);
+static Result<void, DlErrorMessage> __dladdr(void* addr, Dl_info* info);
 
 
 Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(const StringView& name)
 Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(const StringView& name)
 {
 {
@@ -237,6 +238,10 @@ static void initialize_libc(DynamicObject& libc)
     VERIFY(res.has_value());
     VERIFY(res.has_value());
     *((DlSymFunction*)res.value().address.as_ptr()) = __dlsym;
     *((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);
     res = libc.lookup_symbol("__libc_init"sv);
     VERIFY(res.has_value());
     VERIFY(res.has_value());
     typedef void libc_init_func();
     typedef void libc_init_func();
@@ -430,6 +435,48 @@ static Result<void*, DlErrorMessage> __dlsym(void* handle, const char* symbol_na
     return symbol.value().address.as_ptr();
     return symbol.value().address.as_ptr();
 }
 }
 
 
+static Result<void, DlErrorMessage> __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<DynamicObject> 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<DynamicObject::Symbol> 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()
 static void read_environment_variables()
 {
 {
     for (char** env = s_envp; *env; ++env) {
     for (char** env = s_envp; *env; ++env) {