Merge pull request #920
[mono.git] / mono / utils / mono-dl.c
index c65e380e9228fb6d0d9a967aec44bb879aa74b5f..b59ab2d5aae0de5322e91c8f626010e934fad50e 100644 (file)
@@ -1,5 +1,16 @@
+/*
+ * mono-dl.c: Interface to the dynamic linker
+ *
+ * Author:
+ *    Mono Team (http://www.mono-project.com)
+ *
+ * Copyright 2001-2004 Ximian, Inc.
+ * Copyright 2004-2009 Novell, Inc.
+ */
 #include "config.h"
 #include "mono/utils/mono-dl.h"
+#include "mono/utils/mono-embed.h"
+#include "mono/utils/mono-path.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -7,7 +18,7 @@
 #include <string.h>
 #include <glib.h>
 
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
 #define SOPREFIX ""
 static const char suffixes [][5] = {
        ".dll"
@@ -26,44 +37,41 @@ static const char suffixes [][4] = {
 };
 #endif
 
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
 
 #include <windows.h>
+#include <psapi.h>
 
 #define SO_HANDLE_TYPE HMODULE
-#define LL_SO_OPEN(file,flags) (file)? LoadLibrary ((file)): GetModuleHandle (NULL)
-#define LL_SO_CLOSE(module) do { if ((module)->main_module) FreeLibrary ((module)->handle); } while (0)
-#define LL_SO_SYMBOL(handle, name) GetProcAddress ((handle), (name))
+#define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
+#define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
+#define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
 #define LL_SO_TRFLAGS(flags) 0
 #define LL_SO_ERROR() w32_dlerror ()
 
-static char*
-w32_dlerror (void)
-{
-       char* ret;
-       TCHAR* buf = NULL;
-       DWORD code = GetLastError ();
-
-       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
-               code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, NULL);
-
-       ret = g_strdup (buf);
-       LocalFree (buf);
-       return ret;
-}
-
 #elif defined (HAVE_DL_LOADER)
 
 #include <dlfcn.h>
+#include <unistd.h>
+
+#ifdef __MACH__
+#include <mach-o/dyld.h>
+#endif
+
 
 #ifndef RTLD_LAZY
 #define RTLD_LAZY       1
 #endif  /* RTLD_LAZY */
 
 #define SO_HANDLE_TYPE void*
-#define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
-#define LL_SO_CLOSE(handle) dlclose ((handle))
-#define LL_SO_SYMBOL(handle, name) dlsym ((handle), (name))
+#ifdef PLATFORM_ANDROID
+/* Bionic doesn't support NULL filenames */
+#  define LL_SO_OPEN(file,flags) ((file) ? dlopen ((file), (flags)) : NULL)
+#else
+#  define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
+#endif
+#define LL_SO_CLOSE(module) dlclose ((module)->handle)
+#define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
 #define LL_SO_ERROR() g_strdup (dlerror ())
 
@@ -80,21 +88,135 @@ convert_flags (int flags)
 }
 
 #else
-/* no duynamic loader supported */
+/* no dynamic loader supported */
 #define SO_HANDLE_TYPE void*
 #define LL_SO_OPEN(file,flags) NULL
-#define LL_SO_CLOSE(handle) 
-#define LL_SO_SYMBOL(handle, name) NULL
+#define LL_SO_CLOSE(module) 
+#define LL_SO_SYMBOL(module, name) NULL
 #define LL_SO_TRFLAGS(flags) (flags)
 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
 
 #endif
 
+static GSList *fallback_handlers;
+
+struct MonoDlFallbackHandler {
+       MonoDlFallbackLoad load_func;
+       MonoDlFallbackSymbol symbol_func;
+       MonoDlFallbackClose close_func;
+       void *user_data;
+};
+       
 struct _MonoDl {
        SO_HANDLE_TYPE handle;
        int main_module;
+
+       /* If not NULL, use the methods in MonoDlFallbackHandler instead of the LL_* methods */
+       MonoDlFallbackHandler *dl_fallback;
 };
 
+#ifdef TARGET_WIN32
+
+static char*
+w32_dlerror (void)
+{
+       char* ret = NULL;
+       wchar_t* buf = NULL;
+       DWORD code = GetLastError ();
+
+       if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
+               code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
+       {
+               ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
+               LocalFree (buf);
+       } else {
+               g_assert_not_reached ();
+       }
+       return ret;
+}
+
+static gpointer
+w32_find_symbol (MonoDl *module, const gchar *symbol_name)
+{
+       HMODULE *modules;
+       DWORD buffer_size = sizeof (HMODULE) * 1024;
+       DWORD needed, i;
+       gpointer proc = NULL;
+
+       /* get the symbol directly from the specified module */
+       if (!module->main_module)
+               return GetProcAddress (module->handle, symbol_name);
+
+       /* get the symbol from the main module */
+       proc = GetProcAddress (module->handle, symbol_name);
+       if (proc != NULL)
+               return proc;
+
+       /* get the symbol from the loaded DLLs */
+       modules = (HMODULE *) g_malloc (buffer_size);
+       if (modules == NULL)
+               return NULL;
+
+       if (!EnumProcessModules (GetCurrentProcess (), modules,
+                                buffer_size, &needed)) {
+               g_free (modules);
+               return NULL;
+       }
+
+       /* check whether the supplied buffer was too small, realloc, retry */
+       if (needed > buffer_size) {
+               g_free (modules);
+
+               buffer_size = needed;
+               modules = (HMODULE *) g_malloc (buffer_size);
+
+               if (modules == NULL)
+                       return NULL;
+
+               if (!EnumProcessModules (GetCurrentProcess (), modules,
+                                        buffer_size, &needed)) {
+                       g_free (modules);
+                       return NULL;
+               }
+       }
+
+       for (i = 0; i < needed / sizeof (HANDLE); i++) {
+               proc = GetProcAddress (modules [i], symbol_name);
+               if (proc != NULL) {
+                       g_free (modules);
+                       return proc;
+               }
+       }
+
+       g_free (modules);
+       return NULL;
+}
+
+
+static gpointer
+w32_load_module (const char* file, int flags)
+{
+       gpointer hModule = NULL;
+       if (file) {
+               gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
+               guint last_sem = SetErrorMode (SEM_FAILCRITICALERRORS);
+               guint32 last_error = 0;
+
+               hModule = LoadLibrary (file_utf16);
+               if (!hModule)
+                       last_error = GetLastError ();
+
+               SetErrorMode (last_sem);
+               g_free (file_utf16);
+
+               if (!hModule)
+                       SetLastError (last_error);
+       } else {
+               hModule = GetModuleHandle (NULL);
+       }
+       return hModule;
+}
+#endif
 
 /*
  * read a value string from line with any of the following formats:
@@ -193,7 +315,7 @@ get_dl_name_from_libtool (const char *libtool_file)
  * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
  * to lazily load the symbols instead of resolving everithing at load time.
  * @error_msg points to a string where an error message will be stored in
- * case of failure.
+ * case of failure.   The error must be released with g_free.
  *
  * Returns: a MonoDl pointer on success, NULL on failure.
  */
@@ -202,6 +324,7 @@ mono_dl_open (const char *name, int flags, char **error_msg)
 {
        MonoDl *module;
        void *lib;
+       MonoDlFallbackHandler *dl_fallback = NULL;
        int lflags = LL_SO_TRFLAGS (flags);
 
        if (error_msg)
@@ -216,10 +339,35 @@ mono_dl_open (const char *name, int flags, char **error_msg)
        module->main_module = name == NULL? TRUE: FALSE;
        lib = LL_SO_OPEN (name, lflags);
        if (!lib) {
+               GSList *node;
+               for (node = fallback_handlers; node != NULL; node = node->next){
+                       MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
+                       if (error_msg)
+                               *error_msg = NULL;
+                       
+                       lib = handler->load_func (name, lflags, error_msg, handler->user_data);
+                       if (error_msg && *error_msg != NULL)
+                               g_free (*error_msg);
+                       
+                       if (lib != NULL){
+                               dl_fallback = handler;
+                               break;
+                       }
+               }
+       }
+       if (!lib && !dl_fallback) {
                char *lname;
                char *llname;
-               const char *suff = ".la";
-               const char *ext = strrchr (name, '.');
+               const char *suff;
+               const char *ext;
+               /* This platform does not support dlopen */
+               if (name == NULL) {
+                       free (module);
+                       return NULL;
+               }
+               
+               suff = ".la";
+               ext = strrchr (name, '.');
                if (ext && strcmp (ext, ".la") == 0)
                        suff = "";
                lname = g_strconcat (name, suff, NULL);
@@ -238,6 +386,7 @@ mono_dl_open (const char *name, int flags, char **error_msg)
                }
        }
        module->handle = lib;
+       module->dl_fallback = dl_fallback;
        return module;
 }
 
@@ -256,18 +405,24 @@ char*
 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
 {
        void *sym;
+       char *err = NULL;
 
+       if (module->dl_fallback) {
+               sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
+       } else {
 #if MONO_DL_NEED_USCORE
-       {
-               char *usname = malloc (strlen (name) + 2);
-               *usname = '_';
-               strcpy (usname + 1, name);
-               sym = LL_SO_SYMBOL (module->handle, usname);
-               free (usname);
-       }
+               {
+                       char *usname = malloc (strlen (name) + 2);
+                       *usname = '_';
+                       strcpy (usname + 1, name);
+                       sym = LL_SO_SYMBOL (module, usname);
+                       free (usname);
+               }
 #else
-       sym = LL_SO_SYMBOL (module->handle, name);
+               sym = LL_SO_SYMBOL (module, name);
 #endif
+       }
+
        if (sym) {
                if (symbol)
                        *symbol = sym;
@@ -275,7 +430,7 @@ mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
        }
        if (symbol)
                *symbol = NULL;
-       return LL_SO_ERROR ();
+       return (module->dl_fallback != NULL) ? err :  LL_SO_ERROR ();
 }
 
 /**
@@ -289,7 +444,14 @@ mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
 void
 mono_dl_close (MonoDl *module)
 {
-       LL_SO_CLOSE (module);
+       MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
+       
+       if (dl_fallback){
+               if (dl_fallback->close_func != NULL)
+                       dl_fallback->close_func (module->handle, dl_fallback->user_data);
+       } else
+               LL_SO_CLOSE (module);
+       
        free (module);
 }
 
@@ -314,30 +476,165 @@ mono_dl_build_path (const char *directory, const char *name, void **iter)
        int idx;
        const char *prefix;
        const char *suffix;
+       gboolean first_call;
        int prlen;
+       int suffixlen;
        char *res;
+
        if (!iter)
                return NULL;
+
+       /*
+         The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
+         "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
+         the dll thus named, we start appending suffixes, each time increasing idx twice (since now
+         the 0 value became special and we need to offset idx to a 0-based array index). This is
+         done to handle situations when mapped dll name is specified as libsomething.so.1 or
+         libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
+         here.
+        */
        idx = GPOINTER_TO_UINT (*iter);
-       if (idx >= G_N_ELEMENTS (suffixes))
-               return NULL;
+       if (idx == 0) {
+               first_call = TRUE;
+               suffix = "";
+               suffixlen = 0;
+       } else {
+               idx--;
+               if (idx >= G_N_ELEMENTS (suffixes))
+                       return NULL;
+               first_call = FALSE;
+               suffix = suffixes [idx];
+               suffixlen = strlen (suffix);
+       }
 
        prlen = strlen (SOPREFIX);
        if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
                prefix = SOPREFIX;
        else
                prefix = "";
-       /* if the platform prefix is already provided, we suppose the caller knows the full name already */
-       if (prlen && strncmp (name, SOPREFIX, prlen) == 0)
+
+       if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
                suffix = "";
-       else
-               suffix = suffixes [idx];
+
        if (directory && *directory)
-               res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffixes [idx], NULL);
+               res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
        else
-               res = g_strconcat (prefix, name, suffixes [idx], NULL);
+               res = g_strconcat (prefix, name, suffix, NULL);
        ++idx;
+       if (!first_call)
+               idx++;
        *iter = GUINT_TO_POINTER (idx);
        return res;
 }
 
+MonoDlFallbackHandler *
+mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
+{
+       MonoDlFallbackHandler *handler;
+       
+       g_return_val_if_fail (load_func != NULL, NULL);
+       g_return_val_if_fail (symbol_func != NULL, NULL);
+
+       handler = g_new (MonoDlFallbackHandler, 1);
+       handler->load_func = load_func;
+       handler->symbol_func = symbol_func;
+       handler->close_func = close_func;
+       handler->user_data = user_data;
+
+       fallback_handlers = g_slist_prepend (fallback_handlers, handler);
+       
+       return handler;
+}
+
+void
+mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
+{
+       GSList *found;
+
+       found = g_slist_find (fallback_handlers, handler);
+       if (found == NULL)
+               return;
+
+       g_slist_remove (fallback_handlers, handler);
+       g_free (handler);
+}
+
+
+#if defined (HAVE_DL_LOADER)
+
+static MonoDl*
+try_load (const char *lib_name, char *dir, int flags, char **err)
+{
+       gpointer iter;
+       MonoDl *runtime_lib;
+       char *path;
+       iter = NULL;
+       *err = NULL;
+       while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
+               g_free (*err);
+               runtime_lib = mono_dl_open (path, flags, err);
+               g_free (path);
+               if (runtime_lib)
+                       return runtime_lib;
+       }
+       return NULL;
+}
+
+MonoDl*
+mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
+{
+       MonoDl *runtime_lib = NULL;
+       char buf [4096];
+       int binl;
+       binl = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
+       *error_msg = NULL;
+
+#ifdef __MACH__
+       if (binl == -1) {
+               uint32_t bsize = sizeof (buf);
+               if (_NSGetExecutablePath (buf, &bsize) == 0) {
+                       binl = strlen (buf);
+               }
+       }
+#endif
+       if (binl != -1) {
+               char *base;
+               char *resolvedname, *name;
+               buf [binl] = 0;
+               resolvedname = mono_path_resolve_symlinks (buf);
+               base = g_path_get_dirname (resolvedname);
+               name = g_strdup_printf ("%s/.libs", base);
+               runtime_lib = try_load (lib_name, name, flags, error_msg);
+               g_free (name);
+               if (!runtime_lib) {
+                       char *newbase = g_path_get_dirname (base);
+                       name = g_strdup_printf ("%s/lib", newbase);
+                       runtime_lib = try_load (lib_name, name, flags, error_msg);
+                       g_free (name);
+               }
+#ifdef __MACH__
+               if (!runtime_lib) {
+                       char *newbase = g_path_get_dirname (base);
+                       name = g_strdup_printf ("%s/Libraries", newbase);
+                       runtime_lib = try_load (lib_name, name, flags, error_msg);
+                       g_free (name);
+               }
+#endif
+               g_free (base);
+               g_free (resolvedname);
+       }
+       if (!runtime_lib)
+               runtime_lib = try_load (lib_name, NULL, flags, error_msg);
+
+       return runtime_lib;
+}
+
+#else
+
+MonoDl*
+mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
+{
+       return NULL;
+}
+
+#endif