+/*
+ * 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>
#include <string.h>
#include <glib.h>
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
#define SOPREFIX ""
static const char suffixes [][5] = {
".dll"
};
#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 ())
}
#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:
* 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.
*/
{
MonoDl *module;
void *lib;
+ MonoDlFallbackHandler *dl_fallback = NULL;
int lflags = LL_SO_TRFLAGS (flags);
if (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);
}
}
module->handle = lib;
+ module->dl_fallback = dl_fallback;
return module;
}
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;
}
if (symbol)
*symbol = NULL;
- return LL_SO_ERROR ();
+ return (module->dl_fallback != NULL) ? err : LL_SO_ERROR ();
}
/**
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);
}
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