diff dynload/dynload_unix.c @ 242:85b61e8facfe

dynload: - added new function dlGetLibraryPath to get path of already loaded lib - covered new function in dynload_plain test - cleanups/cosmetics for consistency
author Tassilo Philipp
date Thu, 04 May 2017 13:42:17 +0200
parents 76ed51a690a6
children 0ba6189a51dd
line wrap: on
line diff
--- a/dynload/dynload_unix.c	Tue May 02 03:49:56 2017 +0200
+++ b/dynload/dynload_unix.c	Thu May 04 13:42:17 2017 +0200
@@ -6,7 +6,7 @@
  Description: 
  License:
 
-   Copyright (c) 2007-2015 Daniel Adler <dadler@uni-goettingen.de>, 
+   Copyright (c) 2007-2017 Daniel Adler <dadler@uni-goettingen.de>, 
                            Tassilo Philipp <tphilipp@potion-studios.com>
 
    Permission to use, copy, modify, and distribute this software for any
@@ -24,7 +24,6 @@
 */
 
 
-
 /*
 
   dynload_unix.c
@@ -35,7 +34,14 @@
 
 
 #include "dynload.h"
+#include "../autovar/autovar_OS.h"
 
+#include <string.h>
+
+#if defined(__GLIBC__) /* to access dlinfo */
+#  define _GNU_SOURCE
+#  define __USE_GNU
+#endif
 #include <dlfcn.h>
 
 
@@ -45,20 +51,118 @@
 }
 
 
-void* dlFindSymbol(DLLib* libHandle, const char* symbol)
+void* dlFindSymbol(DLLib* pLib, const char* pSymbolName)
 {
-  return dlsym((void*)libHandle, symbol);
+  return dlsym((void*)pLib, pSymbolName);
+}
+
+
+void dlFreeLibrary(DLLib* pLib)
+{
+  /* Check for NULL for cross-platform consistency. *BSD seems to do that in
+  dlclose, Linux does not. POSIX states "if handle does not refer to an open
+  object, dlclose() returns a non-zero value", which unfortunately sounds
+  like it's not explicitly specified. */
+  if(pLib)
+    dlclose((void*)pLib);
 }
 
 
-void dlFreeLibrary(DLLib* libHandle)
+/* code for dlGetLibraryPath differs on Darwin */
+#if defined(OS_Darwin)
+
+#include <stdint.h>
+#include <mach-o/dyld.h>
+
+int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
 {
+  uint32_t i;
+  int l = -1;
+
+  /*if(pLib == RTLD_DEFAULT)
+    return NULL; @@@ return exec's path */
 
-  /* Check for NULL for cross-platform consistency. *BSD seems to do that in
-     dlclose, Linux does not. POSIX states "if handle does not refer to an open
-     object, dlclose() returns a non-zero value", which unfortunately sounds
-     like it's not explicitly specified. */
-  if(libHandle)
-  	dlclose((void*)libHandle);
+  /* Darwin's code doesn't come with (non-standard) dlinfo(), so use dyld(1) */
+  /* code. There doesn't seem to be a direct way to query the library path,  */
+  /* so "double-load" temporarily all already loaded images (just increases  */
+  /* ref count) and compare handles until we found ours. Return the name.    */
+  for(i=_dyld_image_count(); i>0;) /* iterate libs from end, more likely ours */
+  {
+    const char* libPath = _dyld_get_image_name(--i);
+    void* lib = dlopen(libPath, RTLD_LAZY);
+    if(lib) {
+      dlclose(lib);
+      /* compare handle pointers' high bits (in low 2 bits some flags might */
+      /* be stored - should be safe b/c address needs alignment, anywas) */
+      if(((intptr_t)pLib ^ (intptr_t)lib) < 4) {
+        l = strlen(libPath);
+        if(l < bufSize) /* l+'\0' <= bufSize */
+          strcpy(sOut, libPath);
+        break;
+      }
+    }
+  }
+
+  return l+1; /* strlen + '\0' */
 }
 
+#else /* non-Darwin --> */
+
+#if defined(OS_OpenBSD) /* doesn't have dlinfo() but dl_iterate_phdr() --> */
+
+/* @@@ dl_iterate_phdr() only exists on OpenBSD >= 3.7 */
+
+#include <sys/types.h>
+#include <link.h>
+
+typedef struct {
+  DLLib* pLib;
+  char*  sOut;
+  int    bufSize;
+} iter_phdr_data;
+
+static int iter_phdr_cb(struct dl_phdr_info* info, size_t size, void* data)
+{
+  int l = -1;
+  iter_phdr_data* d = (iter_phdr_data*)data;
+  /* unable to relate info->dlpi_addr directly to our dlopen handle, let's */
+  /* do what we do on macOS above, re-dlopen the already loaded lib (just  */
+  /* increases ref count) and compare handles. */
+  void* lib = dlopen(info->dlpi_name, RTLD_LAZY);
+  if(lib) {
+    dlclose(lib);
+    if(lib == (void*)d->pLib) {
+      l = strlen(info->dlpi_name);
+      if(l < d->bufSize) /* l+'\0' <= bufSize */
+        strcpy(d->sOut, info->dlpi_name);
+    }
+  }
+  return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */
+}
+
+int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
+{
+  iter_phdr_data d = { pLib, sOut, bufSize };
+  return dl_iterate_phdr(iter_phdr_cb, &d);
+}
+
+#else /* use dlinfo() --> */
+
+#include <link.h>
+
+int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
+{
+  struct link_map* p;
+  int l = -1;
+  if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0) {
+    l = strlen(p->l_name);
+    if(l < bufSize) /* l+'\0' <= bufSize */
+      strcpy(sOut, p->l_name);
+  }
+  return l+1; /* strlen + '\0' */
+}
+
+#endif
+
+#endif
+