changeset 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 cde7b1f3b8f2
children 2a77747a5496
files dynload/dynload.h dynload/dynload_syms_mach-o.c dynload/dynload_unix.c dynload/dynload_windows.c test/dynload_plain/dynload_plain.c
diffstat 5 files changed, 178 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/dynload/dynload.h	Tue May 02 03:49:56 2017 +0200
+++ b/dynload/dynload.h	Thu May 04 13:42:17 2017 +0200
@@ -36,17 +36,21 @@
 #define DL_API
 #endif
 
-/* --- public api ---------------------------------------------------------- */
 
 /* shared library loading and explicit symbol resolving */
+/* dlLoadLibrary will search for specified lib (e.g. as leaf name, only), */
+/* to the platforms dynamic linking style */
 
 typedef struct DLLib_ DLLib;
 
-DL_API DLLib* dlLoadLibrary(const char* libpath);
-DL_API void   dlFreeLibrary(DLLib* pLib);
-DL_API void*  dlFindSymbol(DLLib* pLib, const char* pSymbolName);
+DL_API DLLib* dlLoadLibrary   (const char* libPath);
+DL_API void   dlFreeLibrary   (DLLib* pLib);
+DL_API void*  dlFindSymbol    (DLLib* pLib, const char* pSymbolName);
+DL_API int    dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize);
+
 
 /* symbol table enumeration - only for symbol lookup, not resolve */
+/* note that dlSymsInit does not search library paths */
 
 typedef struct DLSyms_ DLSyms;
 
--- a/dynload/dynload_syms_mach-o.c	Tue May 02 03:49:56 2017 +0200
+++ b/dynload/dynload_syms_mach-o.c	Thu May 04 13:42:17 2017 +0200
@@ -97,6 +97,7 @@
 			if(st0.st_ino == st1.st_ino/*!strcmp(name, libPath)*/)
 			{
 				pHeader = (const struct MACH_HEADER_TYPE*) _dyld_get_image_header(i);
+//@@@ slide = _dyld_get_image_vmaddr_slide(i);
 				break; /* found header */
 			}
 		}
--- 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
+
--- a/dynload/dynload_windows.c	Tue May 02 03:49:56 2017 +0200
+++ b/dynload/dynload_windows.c	Thu May 04 13:42:17 2017 +0200
@@ -44,13 +44,21 @@
   return (DLLib*)(libPath != NULL ? LoadLibraryA(libPath) : GetModuleHandle(NULL));
 }
 
-void* dlFindSymbol(DLLib* libHandle, const char* symbol)
+
+void* dlFindSymbol(DLLib* pLib, const char* pSymbolName)
 {
-  return (void*) GetProcAddress((HINSTANCE)libHandle, symbol);
+  return (void*)GetProcAddress((HINSTANCE)pLib, pSymbolName);
 }
 
-void dlFreeLibrary(DLLib* libHandle)
+
+void dlFreeLibrary(DLLib* pLib)
 {
-  FreeLibrary((HINSTANCE)libHandle);
+  FreeLibrary((HINSTANCE)pLib);
 }
 
+
+int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
+{
+  return GetModuleFileNameA((HMODULE)pLib, sOut, bufSize)+1; /* strlen + '\0' */
+}
+
--- a/test/dynload_plain/dynload_plain.c	Tue May 02 03:49:56 2017 +0200
+++ b/test/dynload_plain/dynload_plain.c	Thu May 04 13:42:17 2017 +0200
@@ -27,6 +27,7 @@
 #include "../common/platformInit.h"
 
 #include <string.h>
+#include <sys/stat.h>
 #if defined(DC_WINDOWS)
 #  include <io.h>
 #  define F_OK 0
@@ -42,17 +43,21 @@
   DLLib* pLib;
   DLSyms* pSyms;
   const char* path = NULL;
-  const char* clibs[] = { // hacky/lazy list of some clib paths per platform
+  const char* clibs[] = { /* hacky/lazy list of some clib paths per platform */
     "/lib/libc.so",
     "/lib/libc.so.6",
     "/lib/libc.so.7",
+    "/lib/libc.so.39.3", /* hack: for OpenBSD used in dyncall test env */
     "/lib64/libc.so",
     "/lib64/libc.so.6",
     "/lib64/libc.so.7",
     "/lib32/libc.so",
     "/lib32/libc.so.6",
     "/lib32/libc.so.7",
-	"/usr/lib/system/libsystem_c.dylib",
+    "/usr/lib/libc.so",
+    "/usr/lib/libc.so.6",
+    "/usr/lib/libc.so.7",
+    "/usr/lib/system/libsystem_c.dylib",
     "/usr/lib/libc.dylib",
     "\\ReactOS\\system32\\msvcrt.dll",
     "C:\\ReactOS\\system32\\msvcrt.dll",
@@ -72,26 +77,47 @@
     printf("using clib to test at: %s\n", path);
     ++r;
 
-    // dl*Library tests
-    // --------
-    pLib = dlLoadLibrary(path); // check if we can load a lib
+    /* dl*Library tests */
+    /* ---------------- */
+    pLib = dlLoadLibrary(path); /* check if we can load a lib */
     if(pLib) {
+      char queriedPath[200]; /* enough for our test paths */
+      int bs;
+
       printf("pLib handle: %p\n", pLib);
       ++r;
 
-      p = dlFindSymbol(pLib, "printf"); // check if we can lookup a symbol
+      p = dlFindSymbol(pLib, "printf"); /* check if we can lookup a symbol */
       printf("printf at: %p\n", p);
       r += (p != NULL);
 
+      bs = dlGetLibraryPath(pLib, queriedPath, 200);
+      if(bs && bs <= 200) {
+	    struct stat st0, st1; /* to check if same file */
+        int b;
+        printf("path of lib looked up via handle: %s\n", queriedPath);
+        b = (stat(path, &st0) != -1) && (stat(queriedPath, &st1) != -1);
+        printf("lib (inode:%d) and looked up lib (inode:%d) are same: %d\n", b?st0.st_ino:-1, b?st1.st_ino:-1, b && (st0.st_ino == st1.st_ino));
+        r += b && (st0.st_ino == st1.st_ino); /* compare if same lib using inode */
+/*@@@ check of resolved path is absolute*/
+
+        /* check correct bufsize retval */
+        b = (bs == strlen(queriedPath) + 1);
+        printf("looked up path's needed buffer size (%d) computed correctly: %d\n", bs, b);
+        r += b;
+      }
+      else
+        printf("failed to query lib path using lib's handle\n");
+
       dlFreeLibrary(pLib);
     }
     else
       printf("unable to open library %s\n", path);
 
 
-    // dlSyms* tests (intentionally after freeing lib above, as they work standalone)
-    // --------
-    pSyms = dlSymsInit(path); // check if we can iterate over symbols - init
+    /* dlSyms* tests (intentionally after freeing lib above, as they work standalone) */
+    /* ------------- */
+    pSyms = dlSymsInit(path); /* check if we can iterate over symbols - init */
     if(pSyms) {
       int n;
       const char* name;
@@ -99,13 +125,13 @@
       printf("pSyms handle: %p\n", pSyms);
       ++r;
 
-      n = dlSymsCount(pSyms); // check if there are some syms to iterate over
+      n = dlSymsCount(pSyms); /* check if there are some syms to iterate over */
       printf("num of libc symbols: %d\n", n);
       r += (n > 0);
 
       for(i=0; i<n; ++i) {
         name = dlSymsName(pSyms, i);
-        if(name && strcmp(name, "printf") == 0) { // check if we find "printf" also in iterated symbols
+        if(name && strcmp(name, "printf") == 0) { /* check if we find "printf" also in iterated symbols */
           ++r;
           break;
         }
@@ -113,11 +139,11 @@
       printf("printf symbol found by iteration: %d\n", i<n);
 
       name = (i<n) ? dlSymsName(pSyms, i) : NULL;
-      r += (name && strcmp(name, "printf") == 0); // check if we can lookup "printf" by index
+      r += (name && strcmp(name, "printf") == 0); /* check if we can lookup "printf" by index */
       printf("printf symbol name by index: %s\n", name?name:"");
 
-      pLib = dlLoadLibrary(path); // check if we can resolve ptr -> name,
-      if(pLib) {                  // need to lookup by name again, first
+      pLib = dlLoadLibrary(path); /* check if we can resolve ptr -> name, */
+      if(pLib) {                  /* need to lookup by name again, first */
         p = dlFindSymbol(pLib, "printf");
         name = dlSymsNameFromValue(pSyms, p);
         printf("printf symbol name by its own address (%p): %s\n", p, name?name:"");
@@ -131,8 +157,8 @@
       printf("dlSymsInit failed\n");
   }
 
-  // All worked if we got a score of 6 right ones
-  r = (r == 8);
+  /* Check final score of right ones to see if all worked */
+  r = (r == 10);
   printf("result: dynload_plain: %d\n", r);
   return !r;
 }