diff dynload/dynload_unix.c @ 315:3840e0188520

- allowing lookup of running executable's path by passing NULL to dynload's dlGetLibraryPath()
author Tassilo Philipp
date Wed, 06 Nov 2019 14:13:49 +0100
parents b2e4e23d9953
children 3df50603afa9
line wrap: on
line diff
--- a/dynload/dynload_unix.c	Wed Nov 06 12:32:53 2019 +0100
+++ b/dynload/dynload_unix.c	Wed Nov 06 14:13:49 2019 +0100
@@ -39,7 +39,7 @@
 #include <string.h>
 
 #if defined(__GLIBC__)
-/* @@@ version check glibc correctly... dl_iterate_phdr(): glibc ver >= 2.2.4*/
+/* @@@ version check glibc more precisely... dl_iterate_phdr(): glibc ver >= 2.2.4*/
 #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 3)
 #  define DL_USE_GLIBC_ITER_PHDR
 #endif
@@ -83,11 +83,20 @@
 #endif
 
 
+/* helper copying string if buffer big enough, returning length (without \0) */
+static int dl_strlen_strcpy(char* dst, const char* src, int dstSize)
+{
+  int l = strlen(src);
+  if(l < dstSize) /* l+'\0' <= bufSize */
+    strcpy(dst, src);
+  return l;
+}
+
 /* code for dlGetLibraryPath() is platform specific */
 
 /* if dlinfo() exists use it (except on glibc, where it exists since version
  * 2.3.3, but its implementation is dangerous, as no checks are done whether
- * the handle is valid, thus rendering the returned values useless): check for
+ * the handle is valid, thus rendering the returned values useless) check for
  * RTLD_DI_LINKMAP which is a #define for dlinfo() on most supported targets,
  * or specifically check the OS (e.g. dlinfo() is originally from Solaris) */
 #if (defined(RTLD_DI_LINKMAP) || defined(OS_SunOS)) && !defined(DL_USE_GLIBC_ITER_PHDR)
@@ -98,11 +107,9 @@
 {
   struct link_map* p = NULL;
   int l = -1;
-  if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0 && p) {
-    l = strlen(p->l_name);
-    if(l < bufSize) /* l+'\0' <= bufSize */
-      strcpy(sOut, p->l_name);
-  }
+  if(dlinfo(pLib ? pLib : RTLD_SELF, RTLD_DI_LINKMAP, &p) == 0)
+    l = dl_strlen_strcpy(sOut, p->l_name, bufSize);
+
   return l+1; /* strlen + '\0' */
 }
 
@@ -118,26 +125,30 @@
   uint32_t i;
   int l = -1;
 
-  /*if(pLib == RTLD_DEFAULT)
-    return NULL; @@@ return exec's path */
+  /* request info about own process? lookup first loaded image */
+  if(pLib == NULL) {
+    const char* libPath = _dyld_get_image_name(0); //@@@ consider using _NSGetExecutablePath()
+    if(libPath)
+      l = dl_strlen_strcpy(sOut, libPath, bufSize);
+  }
+  else {
+    /* 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;) /* backwards, ours is more likely at end */
+    {
+      const char* libPath = _dyld_get_image_name(--i);
+      void* lib = dlopen(libPath, RTLD_LIGHTEST);
+      if(lib) {
+        dlclose(lib);
 
-  /* 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;) /* backwards, ours is more likely at end */
-  {
-    const char* libPath = _dyld_get_image_name(--i);
-    void* lib = dlopen(libPath, RTLD_LIGHTEST);
-    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;
+        /* compare handle pointers' high bits (in low 2 bits some flags might */
+        /* be stored - should be safe b/c address needs alignment, anyways) */
+        if(((intptr_t)pLib ^ (intptr_t)lib) < 4) {
+          l = dl_strlen_strcpy(sOut, libPath, bufSize);
+          break;
+        }
       }
     }
   }
@@ -164,18 +175,32 @@
 {
   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_LIGHTEST);
-  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);
+  void* lib = NULL;
+
+  /* get loaded object's handle if not requesting info about process itself */
+  if(d->pLib != NULL) {
+    /* 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 */
+    lib = dlopen(info->dlpi_name, RTLD_LIGHTEST);
+    if(lib)
+      dlclose(lib);
+  }
+
+  /* compare handles and get name if found; if d->pLib == NULL this will
+     enter info on first iterated object, which is the process itself */
+  if(lib == (void*)d->pLib) {
+    l = dl_strlen_strcpy(d->sOut, info->dlpi_name, d->bufSize);
+
+    /* on some platforms (e.g. Linux) dlpi_name is empty for the main process'
+       object, but dlpi_addr is given, in that case lookup name via dladdr */
+    if(l == 0 && d->pLib == NULL && (void*)info->dlpi_addr != NULL) {
+      Dl_info i;
+      if(dladdr((void*)info->dlpi_addr, &i) != 0)
+        l = dl_strlen_strcpy(d->sOut, i.dli_fname, d->bufSize);
     }
   }
+
   return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */
 }
 
@@ -206,16 +231,14 @@
 
 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
 {
+/*@@@ missing handler for pLib == NULL*/
   /* cross fingers that shared object is standard ELF and look for _fini */
   int l = -1;
   void* s = dlsym((void*)pLib, "_fini");
   if(s) {
     Dl_info i;
-    if(dladdr(s, &i) != 0) {
-      l = strlen(i.dli_fname);
-      if(l < bufSize) /* l+'\0' <= bufSize */
-        strcpy(sOut, i.dli_fname);
-    }
+    if(dladdr(s, &i) != 0)
+      l = dl_strlen_strcpy(sOut, i.dli_fname, bufSize);
   }
   return l+1; /* strlen + '\0' */
 }