Uniform use of the return error from mono_dl
[mono.git] / mono / utils / mono-dl.c
index c65e380e9228fb6d0d9a967aec44bb879aa74b5f..2c01a79e55e9b8f4672d3261a897b7a656fedc64 100644 (file)
@@ -1,5 +1,15 @@
+/*
+ * 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 <stdlib.h>
 #include <stdio.h>
@@ -7,7 +17,7 @@
 #include <string.h>
 #include <glib.h>
 
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
 #define SOPREFIX ""
 static const char suffixes [][5] = {
        ".dll"
@@ -19,6 +29,11 @@ static const char suffixes [][8] = {
        ".so",
        ".bundle"
 };
+#elif EMBEDDED_PINVOKE
+#define SOPREFIX ""
+static const char suffixes [][1] = {
+       ""
+};
 #else
 #define SOPREFIX "lib"
 static const char suffixes [][4] = {
@@ -26,32 +41,18 @@ 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>
@@ -62,8 +63,8 @@ w32_dlerror (void)
 
 #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))
+#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 ())
 
@@ -79,12 +80,21 @@ convert_flags (int flags)
        return lflags;
 }
 
+#elif EMBEDDED_PINVOKE
+#define SO_HANDLE_TYPE void*
+void *LL_SO_OPEN   (const char *file, int flags);
+int   LL_SO_CLOSE  (void *handle);
+#define LL_SO_SYMBOL(module,symbol) _LL_SO_SYMBOL((module)->handle, (symbol))
+void *_LL_SO_SYMBOL (void *handle, const char *symbol);
+char *LL_SO_ERROR();
+#define LL_SO_TRFLAGS(flags)      0
+
 #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")
 
@@ -95,6 +105,108 @@ struct _MonoDl {
        int main_module;
 };
 
+#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 +305,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.
  */
@@ -218,8 +330,16 @@ mono_dl_open (const char *name, int flags, char **error_msg)
        if (!lib) {
                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);
@@ -262,11 +382,11 @@ mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
                char *usname = malloc (strlen (name) + 2);
                *usname = '_';
                strcpy (usname + 1, name);
-               sym = LL_SO_SYMBOL (module->handle, usname);
+               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)
@@ -341,3 +461,94 @@ mono_dl_build_path (const char *directory, const char *name, void **iter)
        return res;
 }
 
+#if EMBEDDED_PINVOKE
+static GHashTable *mono_dls;
+static char *ll_last_error = "";
+
+/**
+ * mono_dl_register_library:
+ * @name: Library name, this is the name used by the DllImport as the external library name
+ * @mappings: the mappings to register for P/Invoke.
+ *
+ * This function is only available on builds that define
+ * EMBEDDED_PINVOKE, this is available for systems that do not provide
+ * a dynamic linker but still want to use DllImport to easily invoke
+ * code from the managed side into the unmanaged world.
+ *
+ * Mappings is a pointer to the first element of an array of
+ * MonoDlMapping values.  The list must be terminated with both 
+ * the name and addr fields set to NULL.
+ *
+ * This is typically used like this:
+ * MonoDlMapping sample_library_mappings [] = {
+ *   { "CallMe", CallMe },
+ *   { NULL, NULL }
+ * };
+ *
+ * ...
+ * main ()
+ * {
+ *    ...
+ *    mono_dl_register_library ("sample", sample_library_mappings);
+ *    ...
+ * }
+ *
+ * Then the C# code can use this P/Invoke signature:
+ *
+ *     [DllImport ("sample")]
+ *     extern static int CallMe (int f);
+ */
+void
+mono_dl_register_library (const char *name, MonoDlMapping *mappings)
+{
+       if (mono_dls == NULL)
+               mono_dls = g_hash_table_new (g_str_hash, g_str_equal);
+       
+       printf ("Inserting: 0x%p\n", mappings);
+       g_hash_table_insert (mono_dls, g_strdup (name), mappings);
+}
+
+void *
+LL_SO_OPEN (const char *file, int flag)
+{
+       void *mappings;
+       
+       if (mono_dls == NULL){
+               ll_last_error = "Library not registered";
+               return NULL;
+       }
+               
+       mappings = g_hash_table_lookup (mono_dls, file);
+       ll_last_error = mappings == NULL ? "File not registered" : "";
+       printf ("Returning mappings=0x%p\n", mappings);
+       return mappings;
+}
+
+int LL_SO_CLOSE (void *handle)
+{
+       // No-op
+       return 0;
+}
+
+void *
+_LL_SO_SYMBOL (void *handle, const char *symbol)
+{
+       MonoDlMapping *mappings = (MonoDlMapping *) handle;
+       
+       printf ("During lookup: 0x%p\n", handle);
+       for (;mappings->name; mappings++){
+               if (strcmp (symbol, mappings->name) == 0){
+                       ll_last_error = "";
+                       return mappings->addr;
+               }
+       }
+       ll_last_error = "Symbol not found";
+       return NULL;
+}
+
+char *
+LL_SO_ERROR (void)
+{
+       return g_strdup (ll_last_error);
+}
+#endif