9c3faa300122594770e5e48bce9dfaa1509257a8
[mono.git] / mono / utils / mono-dl.c
1 /*
2  * mono-dl.c: Interface to the dynamic linker
3  *
4  * Author:
5  *    Mono Team (http://www.mono-project.com)
6  *
7  * Copyright 2001-2004 Ximian, Inc.
8  * Copyright 2004-2009 Novell, Inc.
9  */
10 #include "config.h"
11 #include "mono/utils/mono-dl.h"
12 #include "mono/utils/mono-embed.h"
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <glib.h>
19
20 #ifdef PLATFORM_WIN32
21 #define SOPREFIX ""
22 static const char suffixes [][5] = {
23         ".dll"
24 };
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes [][8] = {
28         ".dylib",
29         ".so",
30         ".bundle"
31 };
32 #elif EMBEDDED_PINVOKE
33 #define SOPREFIX ""
34 static const char suffixes [][1] = {
35         ""
36 };
37 #else
38 #define SOPREFIX "lib"
39 static const char suffixes [][4] = {
40         ".so"
41 };
42 #endif
43
44 #ifdef PLATFORM_WIN32
45
46 #include <windows.h>
47 #include <psapi.h>
48
49 #define SO_HANDLE_TYPE HMODULE
50 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
51 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
52 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
53 #define LL_SO_TRFLAGS(flags) 0
54 #define LL_SO_ERROR() w32_dlerror ()
55
56 #elif defined (HAVE_DL_LOADER)
57
58 #include <dlfcn.h>
59
60 #ifndef RTLD_LAZY
61 #define RTLD_LAZY       1
62 #endif  /* RTLD_LAZY */
63
64 #define SO_HANDLE_TYPE void*
65 #define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
66 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
67 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
68 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
69 #define LL_SO_ERROR() g_strdup (dlerror ())
70
71 static int
72 convert_flags (int flags)
73 {
74         int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
75
76         if (flags & MONO_DL_LAZY)
77                 lflags |= RTLD_LAZY;
78         else
79                 lflags |= RTLD_NOW;
80         return lflags;
81 }
82
83 #elif EMBEDDED_PINVOKE
84 #define SO_HANDLE_TYPE void*
85 void *LL_SO_OPEN   (const char *file, int flags);
86 int   LL_SO_CLOSE  (void *handle);
87 #define LL_SO_SYMBOL(module,symbol) _LL_SO_SYMBOL((module)->handle, (symbol))
88 void *_LL_SO_SYMBOL (void *handle, const char *symbol);
89 char *LL_SO_ERROR();
90 #define LL_SO_TRFLAGS(flags)      0
91
92 #else
93 /* no dynamic loader supported */
94 #define SO_HANDLE_TYPE void*
95 #define LL_SO_OPEN(file,flags) NULL
96 #define LL_SO_CLOSE(module) 
97 #define LL_SO_SYMBOL(module, name) NULL
98 #define LL_SO_TRFLAGS(flags) (flags)
99 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
100
101 #endif
102
103 struct _MonoDl {
104         SO_HANDLE_TYPE handle;
105         int main_module;
106 };
107
108 #ifdef PLATFORM_WIN32
109
110 static char*
111 w32_dlerror (void)
112 {
113         char* ret = NULL;
114         wchar_t* buf = NULL;
115         DWORD code = GetLastError ();
116
117         if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
118                 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
119         {
120                 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
121                 LocalFree (buf);
122         } else {
123                 g_assert_not_reached ();
124         }
125         return ret;
126 }
127
128 static gpointer
129 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
130 {
131         HMODULE *modules;
132         DWORD buffer_size = sizeof (HMODULE) * 1024;
133         DWORD needed, i;
134         gpointer proc = NULL;
135
136         /* get the symbol directly from the specified module */
137         if (!module->main_module)
138                 return GetProcAddress (module->handle, symbol_name);
139
140         /* get the symbol from the main module */
141         proc = GetProcAddress (module->handle, symbol_name);
142         if (proc != NULL)
143                 return proc;
144
145         /* get the symbol from the loaded DLLs */
146         modules = (HMODULE *) g_malloc (buffer_size);
147         if (modules == NULL)
148                 return NULL;
149
150         if (!EnumProcessModules (GetCurrentProcess (), modules,
151                                  buffer_size, &needed)) {
152                 g_free (modules);
153                 return NULL;
154         }
155
156         /* check whether the supplied buffer was too small, realloc, retry */
157         if (needed > buffer_size) {
158                 g_free (modules);
159
160                 buffer_size = needed;
161                 modules = (HMODULE *) g_malloc (buffer_size);
162
163                 if (modules == NULL)
164                         return NULL;
165
166                 if (!EnumProcessModules (GetCurrentProcess (), modules,
167                                          buffer_size, &needed)) {
168                         g_free (modules);
169                         return NULL;
170                 }
171         }
172
173         for (i = 0; i < needed / sizeof (HANDLE); i++) {
174                 proc = GetProcAddress (modules [i], symbol_name);
175                 if (proc != NULL) {
176                         g_free (modules);
177                         return proc;
178                 }
179         }
180
181         g_free (modules);
182         return NULL;
183 }
184
185
186 static gpointer
187 w32_load_module (const char* file, int flags)
188 {
189         gpointer hModule = NULL;
190         if (file) {
191                 gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
192                 guint last_sem = SetErrorMode (SEM_FAILCRITICALERRORS);
193                 guint32 last_error = 0;
194
195                 hModule = LoadLibrary (file_utf16);
196                 if (!hModule)
197                         last_error = GetLastError ();
198
199                 SetErrorMode (last_sem);
200                 g_free (file_utf16);
201
202                 if (!hModule)
203                         SetLastError (last_error);
204         } else {
205                 hModule = GetModuleHandle (NULL);
206         }
207         return hModule;
208 }
209 #endif
210
211 /*
212  * read a value string from line with any of the following formats:
213  * \s*=\s*'string'
214  * \s*=\s*"string"
215  * \s*=\s*non_white_space_string
216  */
217 static char*
218 read_string (char *p, FILE *file)
219 {
220         char *endp;
221         char *startp;
222         while (*p && isspace (*p))
223                 ++p;
224         if (*p == 0)
225                 return NULL;
226         if (*p == '=')
227                 p++;
228         while (*p && isspace (*p))
229                 ++p;
230         if (*p == '\'' || *p == '"') {
231                 char t = *p;
232                 p++;
233                 startp = p;
234                 endp = strchr (p, t);
235                 /* FIXME: may need to read more from file... */
236                 if (!endp)
237                         return NULL;
238                 *endp = 0;
239                 return g_memdup (startp, (endp - startp) + 1);
240         }
241         if (*p == 0)
242                 return NULL;
243         startp = p;
244         while (*p && !isspace (*p))
245                 ++p;
246         *p = 0;
247         return g_memdup (startp, (p - startp) + 1);
248 }
249
250 /*
251  * parse a libtool .la file and return the path of the file to dlopen ()
252  * handling both the installed and uninstalled cases
253  */
254 static char*
255 get_dl_name_from_libtool (const char *libtool_file)
256 {
257         FILE* file;
258         char buf [512];
259         char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
260         if (!(file = fopen (libtool_file, "r")))
261                 return NULL;
262         while ((line = fgets (buf, 512, file))) {
263                 while (*line && isspace (*line))
264                         ++line;
265                 if (*line == '#' || *line == 0)
266                         continue;
267                 if (strncmp ("dlname", line, 6) == 0) {
268                         g_free (dlname);
269                         dlname = read_string (line + 6, file);
270                 } else if (strncmp ("libdir", line, 6) == 0) {
271                         g_free (libdir);
272                         libdir = read_string (line + 6, file);
273                 } else if (strncmp ("installed", line, 9) == 0) {
274                         g_free (installed);
275                         installed = read_string (line + 9, file);
276                 }
277         }
278         fclose (file);
279         line = NULL;
280         if (installed && strcmp (installed, "no") == 0) {
281                 char *dir = g_path_get_dirname (libtool_file);
282                 if (dlname)
283                         line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
284                 g_free (dir);
285         } else {
286                 if (libdir && dlname)
287                         line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
288         }
289         g_free (dlname);
290         g_free (libdir);
291         g_free (installed);
292         return line;
293 }
294
295 /**
296  * mono_dl_open:
297  * @name: name of file containing shared module
298  * @flags: flags
299  * @error_msg: pointer for error message on failure
300  *
301  * Load the given file @name as a shared library or dynamically loadable
302  * module. @name can be NULL to indicate loading the currently executing
303  * binary image.
304  * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
305  * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
306  * to lazily load the symbols instead of resolving everithing at load time.
307  * @error_msg points to a string where an error message will be stored in
308  * case of failure.
309  *
310  * Returns: a MonoDl pointer on success, NULL on failure.
311  */
312 MonoDl*
313 mono_dl_open (const char *name, int flags, char **error_msg)
314 {
315         MonoDl *module;
316         void *lib;
317         int lflags = LL_SO_TRFLAGS (flags);
318
319         if (error_msg)
320                 *error_msg = NULL;
321
322         module = malloc (sizeof (MonoDl));
323         if (!module) {
324                 if (error_msg)
325                         *error_msg = g_strdup ("Out of memory");
326                 return NULL;
327         }
328         module->main_module = name == NULL? TRUE: FALSE;
329         lib = LL_SO_OPEN (name, lflags);
330         if (!lib) {
331                 char *lname;
332                 char *llname;
333                 const char *suff;
334                 const char *ext;
335                 /* This platform does not support dlopen */
336                 if (name == NULL)
337                         return NULL;
338                 
339                 suff = ".la";
340                 ext = strrchr (name, '.');
341                 if (ext && strcmp (ext, ".la") == 0)
342                         suff = "";
343                 lname = g_strconcat (name, suff, NULL);
344                 llname = get_dl_name_from_libtool (lname);
345                 g_free (lname);
346                 if (llname) {
347                         lib = LL_SO_OPEN (llname, lflags);
348                         g_free (llname);
349                 }
350                 if (!lib) {
351                         if (error_msg) {
352                                 *error_msg = LL_SO_ERROR ();
353                         }
354                         free (module);
355                         return NULL;
356                 }
357         }
358         module->handle = lib;
359         return module;
360 }
361
362 /**
363  * mono_dl_symbol:
364  * @module: a MonoDl pointer
365  * @name: symbol name
366  * @symbol: pointer for the result value
367  *
368  * Load the address of symbol @name from the given @module.
369  * The address is stored in the pointer pointed to by @symbol.
370  *
371  * Returns: NULL on success, an error message on failure
372  */
373 char*
374 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
375 {
376         void *sym;
377
378 #if MONO_DL_NEED_USCORE
379         {
380                 char *usname = malloc (strlen (name) + 2);
381                 *usname = '_';
382                 strcpy (usname + 1, name);
383                 sym = LL_SO_SYMBOL (module, usname);
384                 free (usname);
385         }
386 #else
387         sym = LL_SO_SYMBOL (module, name);
388 #endif
389         if (sym) {
390                 if (symbol)
391                         *symbol = sym;
392                 return NULL;
393         }
394         if (symbol)
395                 *symbol = NULL;
396         return LL_SO_ERROR ();
397 }
398
399 /**
400  * mono_dl_close:
401  * @module: a MonoDl pointer
402  *
403  * Unload the given module and free the module memory.
404  *
405  * Returns: 0 on success.
406  */
407 void
408 mono_dl_close (MonoDl *module)
409 {
410         LL_SO_CLOSE (module);
411         free (module);
412 }
413
414 /**
415  * mono_dl_build_path:
416  * @directory: optional directory
417  * @name: base name of the library
418  * @iter: iterator token
419  *
420  * Given a directory name and the base name of a library, iterate
421  * over the possible file names of the library, taking into account
422  * the possible different suffixes and prefixes on the host platform.
423  *
424  * The returned file name must be freed by the caller.
425  * @iter must point to a NULL pointer the first time the function is called
426  * and then passed unchanged to the following calls.
427  * Returns: the filename or NULL at the end of the iteration
428  */
429 char*
430 mono_dl_build_path (const char *directory, const char *name, void **iter)
431 {
432         int idx;
433         const char *prefix;
434         const char *suffix;
435         int prlen;
436         char *res;
437         if (!iter)
438                 return NULL;
439         idx = GPOINTER_TO_UINT (*iter);
440         if (idx >= G_N_ELEMENTS (suffixes))
441                 return NULL;
442
443         prlen = strlen (SOPREFIX);
444         if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
445                 prefix = SOPREFIX;
446         else
447                 prefix = "";
448         /* if the platform prefix is already provided, we suppose the caller knows the full name already */
449         if (prlen && strncmp (name, SOPREFIX, prlen) == 0)
450                 suffix = "";
451         else
452                 suffix = suffixes [idx];
453         if (directory && *directory)
454                 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffixes [idx], NULL);
455         else
456                 res = g_strconcat (prefix, name, suffixes [idx], NULL);
457         ++idx;
458         *iter = GUINT_TO_POINTER (idx);
459         return res;
460 }
461
462 #if EMBEDDED_PINVOKE
463 static GHashTable *mono_dls;
464 static char *ll_last_error = "";
465
466 /**
467  * mono_dl_register_library:
468  * @name: Library name, this is the name used by the DllImport as the external library name
469  * @mappings: the mappings to register for P/Invoke.
470  *
471  * This function is only available on builds that define
472  * EMBEDDED_PINVOKE, this is available for systems that do not provide
473  * a dynamic linker but still want to use DllImport to easily invoke
474  * code from the managed side into the unmanaged world.
475  *
476  * Mappings is a pointer to the first element of an array of
477  * MonoDlMapping values.  The list must be terminated with both 
478  * the name and addr fields set to NULL.
479  *
480  * This is typically used like this:
481  * MonoDlMapping sample_library_mappings [] = {
482  *   { "CallMe", CallMe },
483  *   { NULL, NULL }
484  * };
485  *
486  * ...
487  * main ()
488  * {
489  *    ...
490  *    mono_dl_register_library ("sample", sample_library_mappings);
491  *    ...
492  * }
493  *
494  * Then the C# code can use this P/Invoke signature:
495  *
496  *      [DllImport ("sample")]
497  *      extern static int CallMe (int f);
498  */
499 void
500 mono_dl_register_library (const char *name, MonoDlMapping *mappings)
501 {
502         if (mono_dls == NULL)
503                 mono_dls = g_hash_table_new (g_str_hash, g_str_equal);
504         
505         printf ("Inserting: 0x%p\n", mappings);
506         g_hash_table_insert (mono_dls, g_strdup (name), mappings);
507 }
508
509 void *
510 LL_SO_OPEN (const char *file, int flag)
511 {
512         void *mappings;
513         
514         if (mono_dls == NULL){
515                 ll_last_error = "Library not registered";
516                 return NULL;
517         }
518                 
519         mappings = g_hash_table_lookup (mono_dls, file);
520         ll_last_error = mappings == NULL ? "File not registered" : "";
521         printf ("Returning mappings=0x%p\n", mappings);
522         return mappings;
523 }
524
525 int LL_SO_CLOSE (void *handle)
526 {
527         // No-op
528         return 0;
529 }
530
531 void *
532 _LL_SO_SYMBOL (void *handle, const char *symbol)
533 {
534         MonoDlMapping *mappings = (MonoDlMapping *) handle;
535         
536         printf ("During lookup: 0x%p\n", handle);
537         for (;mappings->name; mappings++){
538                 if (strcmp (symbol, mappings->name) == 0){
539                         ll_last_error = "";
540                         return mappings->addr;
541                 }
542         }
543         ll_last_error = "Symbol not found";
544         return NULL;
545 }
546
547 char *
548 LL_SO_ERROR (void)
549 {
550         return ll_last_error;
551 }
552 #endif